Подтвердить что ты не робот

Нумерованный эквивалент list.index

В низкоуровневой функции, которая называется много раз, мне нужно сделать эквивалент python list.index, но с массивом numpy. Функция должна возвратиться, когда она найдет первое значение, и в противном случае повысит значение ValueError. Что-то вроде:

>>> a = np.array([1, 2, 3])
>>> np_index(a, 1)
0
>>> np_index(a, 10)
Traceback (most recent call last):    
  File "<stdin>", line 1, in <module>
ValueError: 10 not in array

Я хочу, если возможно, избегать цикла Python. np.where не является вариантом, поскольку он всегда выполняет итерацию по всему массиву; Мне нужно что-то, что останавливается после обнаружения первого индекса.


EDIT. Еще одна конкретная информация, связанная с этой проблемой.

  • Примерно в 90% случаев индекс, который я ищу, находится в первых 1/4 до 1/2 массива. Так что здесь потенциально может произойти 2-4 ускорения. Остальные 10% времени не имеют значения в массиве.

  • Я уже профилировал вещи, а вызов np.where - это узкое место, занимающее не менее 50% общей продолжительности выполнения.

  • Не важно, чтобы он поднимал ValueError; он просто должен вернуть что-то, что, очевидно, указывает на то, что значение не находится в массиве.

Я, вероятно, буду кодировать решение в Cython, как было предложено.

4b9b3361

Ответ 1

См. мой комментарий к вопросу OP для предостережений, но в целом я бы сделал следующее:

import numpy as np
a = np.array([1, 2, 3])
np.min(np.nonzero(a == 2)[0])

Если значение, которое вы ищете, не находится в массиве, вы получите ValueError из-за:

ValueError: zero-size array to ufunc.reduce without identity

потому что вы пытаетесь принять значение min для пустого массива.

Я бы просмотрел этот код и посмотрел, действительно ли это узкое место, потому что в целом, когда numpy ищет весь массив, используя встроенную функцию, а не явный цикл python, он относительно быстр. Настаивание на прекращении поиска, когда оно находит первое значение, может быть функционально неуместным.

Ответ 2

Если ваш массив numpy равен 1d массиву, возможно, попробуйте вот так:

a = np.array([1, 2, 3])
print a.tolist().index(2)
>>> 1

Если это не 1d, вы можете найти такой массив, как:

a = np.array([[1, 2, 3],[2,5,6],[0,0,2]])
print a[0,:].tolist().index(2)
>>> 1

print a[1,:].tolist().index(2)
>>> 0

print a[2,:].tolist().index(2)
>>> 2

Ответ 3

Ближайшая вещь, которую я могу найти в том, что вы просите, отлична от нуля. Это может показаться странным, но документация делает его похожим на то, что он может иметь желаемый результат.

http://www.scipy.org/Numpy_Example_List_With_Doc#nonzero

В частности, эта часть:

a.nonzero()

Возвращает индексы элементов, отличных от нуля.

Для полной документации обратитесь к numpy.nonzero.

См. также

numpy.nonzero: эквивалентная функция

>>> from numpy import *
>>> y = array([1,3,5,7])
>>> indices = (y >= 5).nonzero()
>>> y[indices]
array([5, 7])
>>> nonzero(y)                                # function also exists
(array([0, 1, 2, 3]),)

Где (http://www.scipy.org/Numpy_Example_List_With_Doc#where) также может вас заинтересовать.

Ответ 4

Вы можете закодировать его в Cython и просто импортировать из Python script. Не нужно переносить весь проект в Cython.

# paste into: indexing.pyx
def index(long[:] lst, long value):
    cdef int i
    for i in range(len(lst)):
        if lst[i] == value:
            return i
    raise ValueError

# import in your .py code
import pyximport
pyximport.install()
from indexing import index

# example
from numpy import zeros
a = zeros(10**6, int)
a[-1] = 1

index(a, 1)
Wall time: 6.07 ms
999999

index(a, 0)
Wall time: 38.1 µs
0

Ответ 5

Единственный раз, когда у меня была эта проблема, достаточно было указать массив numpy в виде списка:

a = numpy.arange(3)
print(list(a).index(2))

>>> 2

Ответ 6

NumPy searchsorted очень похож на индекс списков, за исключением того, что он требует отсортированного массива и ведет себя более численно. Большие различия заключаются в том, что вам не нужно иметь точное соответствие, и вы можете искать, начиная с левой или с правой стороны. Для получения информации о том, как это работает, см. Следующие примеры:

import numpy as np
a = np.array([10, 20, 30])

a.searchsorted(-99) == a.searchsorted(0) == a.searchsorted(10)
# returns index 0 for value 10

a.searchsorted(20.1) == a.searchsorted(29.9) == a.searchsorted(30)
# returns index 2 for value 30

a.searchsorted(30.1) == a.searchsorted(99) == a.searchsorted(np.nan)
# returns index 3 for undefined value

В последнем случае, когда возвращается индекс из 3, вы можете обрабатывать это как хотите. Я собираюсь от имени и намерения функции, которую он останавливает после нахождения первого подходящего индекса.