В NumPy есть множество операций, которые, возможно, можно было бы объединить для достижения этой цели. Это вернет индексы элементов, равные item:
numpy.nonzero(array - item)
Затем вы можете взять первые элементы списков, чтобы получить один элемент.
Разве это не дало бы индексы всех элементов, которые нет равны item?
Чтобы индексировать по любому критерию, вы можете сделать что-то вроде следующего:
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]
Да, учитывая массив array
и значение item
для поиска, вы можете использовать np.where
как:
itemindex = numpy.where(array==item)
Результатом является кортеж, содержащий сначала все индексы строк, а затем все индексы столбцов.
Например, если массив имеет два измерения и содержит ваш элемент в двух местах, тогда
array[itemindex[0][0]][itemindex[1][0]]
будет равно вашему элементу, и поэтому будет:
array[itemindex[0][1]][itemindex[1][1]]
Если вы ищете первую строку, в которой элемент существует в первом столбце, это работает (хотя выдает ошибку индекса, если ее нет) rows, columns = np.where(array==item); first_idx = sorted([r for r, c in zip(rows, columns) if c == 0])[0]
Также взгляните на этот вопрос: stackoverflow.com/questions/7632963/…
Что, если вы хотите, чтобы поиск прекратился после нахождения первого значения? Я не думаю, что where () сравнимо с find ()
Ах! Если вас интересует производительность, ознакомьтесь с ответом на этот вопрос: stackoverflow.com/questions/7632963/…
np.argwhere
был бы здесь немного полезнее: itemindex = np.argwhere(array==item)[0]; array[tuple(itemindex)]
Стоит отметить, что этот ответ предполагает, что массив является 2D. where
работает с любым массивом и вернет кортеж длиной 3 при использовании в 3D-массиве и т. д.
Если вы собираетесь использовать это как индекс для чего-то еще, вы можете использовать логические индексы, если массивы транслируются; вам не нужны явные индексы. Самый простой способ сделать это - просто индексировать на основе значения истинности.
other_array[first_array == item]
Работает любая логическая операция:
a = numpy.arange(100)
other_array[first_array > 50]
Ненулевой метод также принимает логические значения:
index = numpy.nonzero(first_array == item)[0][0]
Два нуля предназначены для кортежа индексов (при условии, что first_array равен 1D), а затем для первого элемента в массиве индексов.
Если вам нужен индекс первого появления только одно значение, вы можете использовать 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]),)
Обратите внимание, что он находит начало обеих подпоследовательностей из 3 сек и обеих подпоследовательностей из 8:
[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]),)
Не могли бы вы объяснить, что такое r_
?
@Geoff, r_
объединяется; или, точнее, он переводит объекты срезов в конкатенацию по каждой оси. Вместо этого я мог бы использовать hstack
; это могло быть менее запутанным. См. документация для получения дополнительной информации о r_
. Также есть c_
.
+1, симпатичный! (vs NP.where) ваше решение намного проще (и, вероятно, быстрее) в случае, когда это только первое вхождение данного значения в одномерном массиве, которое нам нужно
Последний случай (поиск первого индекса всех значений) задается vals, locs = np.unique(t, return_index=True)
.
@askewchan ваша версия функционально эквивалентна, но намного медленнее
@Jivan, мне кажется, что я ошибся: unique
находит первый индекс всех значений, но ответ дает индексы везде, где значение изменяется (т.е. имеет ненулевое различие). Они эквивалентны только в том случае, если массив t
уже отсортирован (или все равные значения находятся в смежных группах).
Вы также можете преобразовать массив 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.
Возможно, библиотека изменилась с момента ее написания. Но это было первое решение, которое у меня сработало.
Я хорошо использовал это, чтобы найти несколько значений в списке, используя понимание списка: [find_list.index(index_list[i]) for i in range(len(index_list))]
@MattWenham Если он достаточно большой, вы можете преобразовать свой find_list
в массив NumPy из object
(или что-то более конкретное, что подходит) и просто сделать find_arr[index_list]
.
Совершенно не по теме, но я впервые вижу фразу «в воздухе» - то, что я видел больше всего, на ее месте, вероятно, «на лету».
Альтернативой выбора первого элемента из np.where () является использование выражения генератора вместе с enumerate, например:
>>> 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 проверяет все элементы на совпадение. Выражение генератора было бы быстрее, если бы в массиве было совпадение.
В случае, если в массиве может вообще не быть совпадений, этот метод также позволяет вам удобно указать резервное значение. Если в первом примере в качестве запасного варианта будет возвращен None
, он станет next((i for i, x_i in enumerate(x) if x_i == 2), None)
.
Просто чтобы добавить очень производительную и удобную альтернативу 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,)
@njit
- это сокращение от jit(nopython=True)
, то есть функция будет полностью скомпилирована на лету во время первого запуска, так что вызовы интерпретатора Python будут полностью удалены.
Начиная с версии не ниже 0.20.0, вы также можете написать его как генератор, чтобы все вхождения определенного значения можно было найти по запросу.
Для одномерных массивов я бы рекомендовал np.flatnonzero(array == value)[0]
, который эквивалентен как np.nonzero(array == value)[0][0]
, так и np.where(array == value)[0][0]
, но позволяет избежать уродства распаковки одноэлементного кортежа.
l.index(x)
возвращает наименьшее значение я, так что я является индексом первого появления x в списке.
Можно смело предположить, что функция index()
в Python реализована так, что она останавливается после нахождения первого совпадения, и это приводит к оптимальной средней производительности.
Чтобы найти элемент, останавливающийся после первого совпадения в массиве NumPy, используйте итератор (нумеровать).
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
), которые можно использовать для поиска элемента в массиве, но все они имеют недостаток, заключающийся в просмотре всего массива в поисках вхождений все, поэтому они не оптимизированы для поиска первый элемент. Также обратите внимание, что 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: быстро найти первый индекс значения
Я думаю, вам также следует указать время для наихудшего случая (последний элемент), чтобы читатели знали, что с ними происходит в худшем случае, когда они используют ваш подход.
@MSeifert Я не могу найти разумное время для наихудшего решения итератора - я собираюсь удалить этот ответ, пока не выясню, что с ним не так
не работает %timeit next((idx for idx, val in np.ndenumerate(a) if val==99999))
? Если вам интересно, почему он в 1000 раз медленнее - это потому, что циклы python по numpy-массивам заведомо медленны.
@MSeifert нет, я этого не знал, но меня также озадачивает тот факт, что argmax
и where
в этом случае намного быстрее (искомый элемент в конце массива)
Они должны быть такими же быстрыми, как если бы элемент был в начале. Они всегда обрабатывают весь массив, поэтому всегда занимают одно и то же время (по крайней мере, должны).
@MSeifert Я имел ввиду, что итератор медленнее, argmax
и where
занимают одинаковое время, как и ожидалось
Это потому, что итерация по numpy-массивам с использованием python-loops не является хорошей идеей (потому что это очень медленно!).
Пакет numpy_indexed (отказ от ответственности, я его автор) содержит векторизованный эквивалент 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
В результате получается <filter object at 0x0000027535294D30>
на Python 3 (проверено на Python 3.6.3). Возможно обновление для Python 3?
Для одномерных массивов отсортированный было бы намного проще и эффективнее O (log (n)) использовать numpy.searchsorted, который возвращает целое число (позицию) NumPy. Например,
arr = np.array([1, 1, 1, 2, 3, 3, 4])
i = np.searchsorted(arr, 3)
Просто убедитесь, что массив уже отсортирован
Также проверьте, действительно ли возвращаемый индекс i содержит искомый элемент, поскольку основная цель searchsorted - найти индексы, в которые следует вставить элементы для поддержания порядка.
if arr[i] == 3:
print("present")
else:
print("not present")
searchsorted не является nlog (n), поскольку он не сортирует массив перед поиском, он предполагает, что массив аргументов уже отсортирован. ознакомьтесь с документацией numpy.searchsorted (ссылка выше)
Используйте ndindex
Образец массива
arr = np.array([[1,4],
[2,3]])
print(arr)
...[[1,4],
[2,3]]
создать пустой список для хранения индекса и кортежей элементов
index_elements = []
for i in np.ndindex(arr.shape):
index_elements.append((arr[i],i))
преобразовать список кортежей в словарь
index_elements = dict(index_elements)
Ключи - это элементы, а значения - их индексы - используйте ключи для доступа к индексу
index_elements[4]
output
... (0,1)
К вашему сведению: Получение индексов сразу нескольких элементов в массиве NumPy