Я знаю, что есть метод для списка Python, который возвращает первый индекс чего-либо:
>>> l = [1, 2, 3]
>>> l.index(2)
1
Есть ли что-то подобное для массивов NumPy?
Я знаю, что есть метод для списка Python, который возвращает первый индекс чего-либо:
>>> l = [1, 2, 3]
>>> l.index(2)
1
Есть ли что-то подобное для массивов NumPy?
Да, вот ответ, данный массив NumPy, array
и значение item
для поиска:
itemindex = numpy.where(array==item)
В результате получается кортеж с сначала всеми индексами строк, а затем всеми индексами столбцов.
Например, если массив имеет два измерения и содержит ваш элемент в двух местах, то
array[itemindex[0][0]][itemindex[1][0]]
будет равна вашему предмету, и поэтому будет
array[itemindex[0][1]][itemindex[1][1]]
Если вам нужен индекс первого вхождения только одно значение, вы можете использовать nonzero
(или where
, что в этом случае одинаково):
>>> t = array([1, 1, 1, 2, 2, 3, 8, 3, 8, 8])
>>> nonzero(t == 8)
(array([6, 8, 9]),)
>>> nonzero(t == 8)[0][0]
6
Если вам нужен первый индекс каждого из многих значений, вы, очевидно, могли бы сделать то же самое, что и выше, но есть трюк, который может быть быстрее. Нижеследующие находят индексы первого элемента каждой подпоследовательности:
>>> nonzero(r_[1, diff(t)[:-1]])
(array([0, 3, 5, 6, 7, 8]),)
Обратите внимание, что он находит начало как подпоследовательности 3s, так и обеих подпоследовательностей 8s:
[ 1, 1, 1, 2, 2, 3, 8, 3, 8, 8]
Итак, это немного отличается от поиска первого появления каждого значения. В вашей программе вы можете работать со отсортированной версией t
, чтобы получить то, что вы хотите:
>>> st = sorted(t)
>>> nonzero(r_[1, diff(st)[:-1]])
(array([0, 3, 5, 7]),)
Вы также можете преобразовать массив NumPy в список в эфир и получить его индекс. Например,
l = [1,2,3,4,5] # Python list
a = numpy.array(l) # NumPy array
i = a.tolist().index(2) # i will return index of 2
print i
Это напечатает 1.
Если вы собираетесь использовать это как индекс во что-то еще, вы можете использовать логические индексы, если массивы являются широковещательными; вам не нужны явные индексы. Абсолютным простейшим способом сделать это является простое индексирование на основе значения истины.
other_array[first_array == item]
Любая логическая операция работает:
a = numpy.arange(100)
other_array[first_array > 50]
Ненулевой метод также принимает значения booleans:
index = numpy.nonzero(first_array == item)[0][0]
Два нули для набора индексов (при условии, что first_array равен 1D), а затем первый элемент в массиве индексов.
Просто добавьте очень производительную и удобную альтернативу numba, основанную на np.ndenumerate
чтобы найти первый индекс:
from numba import njit
import numpy as np
@njit
def index(array, item):
for idx, val in np.ndenumerate(array):
if val == item:
return idx
# If no item was found return None, other return types might be a problem due to
# numbas type inference.
Это довольно быстро и, естественно, имеет дело с многомерными массивами:
>>> arr1 = np.ones((100, 100, 100))
>>> arr1[2, 2, 2] = 2
>>> index(arr1, 2)
(2, 2, 2)
>>> arr2 = np.ones(20)
>>> arr2[5] = 2
>>> index(arr2, 2)
(5,)
Это может быть намного быстрее (потому что это закорачивает операцию), чем любой подход, использующий np.where
или np.nonzero
.
Однако np.argwhere
также может иметь дело изящно с многомерными массивами (вам нужно будет вручную привести его в кортеж и не закорочены), но он потерпит неудачу, если совпадение не найдено:
>>> tuple(np.argwhere(arr1 == 2)[0])
(2, 2, 2)
>>> tuple(np.argwhere(arr2 == 2)[0])
(5,)
l.index(x)
возвращает наименьшее значение i, так что я является индексом первого появления x в списке.
Можно смело предположить, что функция index()
в Python реализована так, что она останавливается после нахождения первого совпадения, и это приводит к оптимальной средней производительности.
Чтобы найти остановку элемента после первого совпадения в массиве NumPy, используйте итератор (ndenumerate).
In [67]: l=range(100)
In [68]: l.index(2)
Out[68]: 2
Массив NumPy:
In [69]: a = np.arange(100)
In [70]: next((idx for idx, val in np.ndenumerate(a) if val==2))
Out[70]: (2L,)
Обратите внимание, что оба метода index()
и next
возвращают ошибку, если элемент не найден. С помощью next
можно использовать второй аргумент для возврата специального значения в случае, если элемент не найден, например,
In [77]: next((idx for idx, val in np.ndenumerate(a) if val==400),None)
В NumPy есть и другие функции (argmax
, where
и nonzero
), которые можно использовать для поиска элемента в массиве, но у всех них есть недостаток, argmax
, что он просматривает весь массив и ищет все вхождения, поэтому он не оптимизирован для поиска первый элемент. Также обратите внимание, что where
и nonzero
возвращают массивы, поэтому вам нужно выбрать первый элемент, чтобы получить индекс.
In [71]: np.argmax(a==2)
Out[71]: 2
In [72]: np.where(a==2)
Out[72]: (array([2], dtype=int64),)
In [73]: np.nonzero(a==2)
Out[73]: (array([2], dtype=int64),)
Просто проверяя, что для больших массивов решение, использующее итератор, быстрее, когда искомый элемент находится в начале массива (используя %timeit
в оболочке IPython):
In [285]: a = np.arange(100000)
In [286]: %timeit next((idx for idx, val in np.ndenumerate(a) if val==0))
100000 loops, best of 3: 17.6 µs per loop
In [287]: %timeit np.argmax(a==0)
1000 loops, best of 3: 254 µs per loop
In [288]: %timeit np.where(a==0)[0][0]
1000 loops, best of 3: 314 µs per loop
Это открытая проблема NumPy GitHub.
Смотрите также: Numpy: быстро найти первый индекс значения
Для индексации по любым критериям вы можете сделать что-то вроде следующего:
In [1]: from numpy import *
In [2]: x = arange(125).reshape((5,5,5))
In [3]: y = indices(x.shape)
In [4]: locs = y[:,x >= 120] # put whatever you want in place of x >= 120
In [5]: pts = hsplit(locs, len(locs[0]))
In [6]: for pt in pts:
.....: print(', '.join(str(p[0]) for p in pt))
4, 4, 0
4, 4, 1
4, 4, 2
4, 4, 3
4, 4, 4
И здесь быстрая функция, чтобы сделать то, что делает list.index(), за исключением того, что не вызывает исключение, если оно не найдено. Осторожно - это, вероятно, очень медленно на больших массивах. Вы, вероятно, можете использовать это для массивов, если вы предпочитаете использовать его как метод.
def ndindex(ndarray, item):
if len(ndarray.shape) == 1:
try:
return [ndarray.tolist().index(item)]
except:
pass
else:
for i, subarray in enumerate(ndarray):
try:
return [i] + ndindex(subarray, item)
except:
pass
In [1]: ndindex(x, 103)
Out[1]: [4, 0, 3]
Для 1D-массивов я бы рекомендовал np.flatnonzero(array == value)[0]
, что эквивалентно как np.nonzero(array == value)[0][0]
, так и np.where(array == value)[0][0]
, но избегает уродства распаковки 1-элементного кортежа.
В NumPy существует множество операций, которые можно объединить для достижения этой цели. Это вернет индексы элементов, равные item:
numpy.nonzero(array - item)
Затем вы можете взять первые элементы списков, чтобы получить один элемент.
Для одномерных отсортированных массивов было бы намного проще и эффективнее O (log (n)) использовать numpy.searchsorted, который возвращает целое число NumPy (позиция). Например,
arr = np.array([1, 1, 1, 2, 3, 3, 4])
i = np.searchsorted(arr, 3)
Просто убедитесь, что массив уже отсортирован
Также проверьте, что возвращаемый индекс я действительно содержит искомый элемент, так как главная цель searchsorted - найти индексы, в которые должны быть вставлены элементы для поддержания порядка.
if arr[i] == 3:
print("present")
else:
print("not present")
Альтернативой выбору первого элемента из np.where() является использование выражения генератора вместе с перечислением, например:
>>> import numpy as np
>>> x = np.arange(100) # x = array([0, 1, 2, 3, ... 99])
>>> next(i for i, x_i in enumerate(x) if x_i == 2)
2
Для двумерного массива можно было бы сделать:
>>> x = np.arange(100).reshape(10,10) # x = array([[0, 1, 2,... 9], [10,..19],])
>>> next((i,j) for i, x_i in enumerate(x)
... for j, x_ij in enumerate(x_i) if x_ij == 2)
(0, 2)
Преимущество этого подхода заключается в том, что он перестает проверять элементы массива после первого совпадения, тогда как np.where проверяет все элементы для соответствия. Выражение генератора было бы быстрее, если бы оно соответствовало раннему массиву.
numpy_indexed package (отказ от ответственности, я являюсь его автором) содержит векторный эквивалент list.index для numpy.ndarray; то есть:
sequence_of_arrays = [[0, 1], [1, 2], [-5, 0]]
arrays_to_query = [[-5, 0], [1, 0]]
import numpy_indexed as npi
idx = npi.indices(sequence_of_arrays, arrays_to_query, missing=-1)
print(idx) # [2, -1]
Это решение имеет векторизованную производительность, обобщает на ndarrays и имеет различные способы устранения недостающих значений.
Примечание: это для Python 2.7 версии
Вы можете использовать лямбда-функцию для решения этой проблемы, и она работает как с массивом NumPy, так и со списком.
your_list = [11, 22, 23, 44, 55]
result = filter(lambda x:your_list[x]>30, range(len(your_list)))
#result: [3, 4]
import numpy as np
your_numpy_array = np.array([11, 22, 23, 44, 55])
result = filter(lambda x:your_numpy_array [x]>30, range(len(your_list)))
#result: [3, 4]
И вы можете использовать
result[0]
получить первый индекс отфильтрованных элементов.
Для Python 3.6 используйте
list(result)
вместо
result