Есть ли функция NumPy для возврата первого индекса чего-либо в массиве? - PullRequest
395 голосов
/ 11 января 2009

Я знаю, что есть метод для списка Python, который возвращает первый индекс чего-либо:

>>> l = [1, 2, 3]
>>> l.index(2)
1

Есть ли что-то подобное для массивов NumPy?

Ответы [ 13 ]

463 голосов
/ 11 января 2009

Да, вот ответ с массивом NumPy array и значением item для поиска:

itemindex = numpy.where(array==item)

Результатом является кортеж с сначала всеми индексами строк, а затем всеми индексами столбцов.

Например, если массив имеет два измерения и содержит ваш элемент в двух местах, то

array[itemindex[0][0]][itemindex[1][0]]

будет равно вашему предмету и

array[itemindex[0][1]][itemindex[1][1]]

numpy.where

62 голосов
/ 25 июня 2009

Если вам нужен индекс первого вхождения только для одного значения , вы можете использовать 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]),)
40 голосов
/ 02 июня 2014

Вы также можете преобразовать массив 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.

14 голосов
/ 11 января 2009

Если вы собираетесь использовать это как индекс для чего-то другого, вы можете использовать логические индексы, если массивы являются трансляционными; вам не нужны явные индексы. Абсолютно простой способ сделать это - просто индексировать на основе значения истинности.

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), а затем первый элемент в массиве индексов.

13 голосов
/ 11 января 2017

Просто добавьте очень производительную и удобную альтернативу на основе 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,)
8 голосов
/ 06 мая 2017

l.index(x) возвращает наименьшее значение i , такое что 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), которые можно использовать для поиска элемента в массиве, но у всех них есть недостаток - проходить весь массив в поисках все вхождений, поэтому не оптимизированы для поиска первого элемента. Также обратите внимание, что 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: быстрый поиск первого индекса значения

6 голосов
/ 11 января 2009

Для индексации по любым критериям вы можете сделать что-то вроде следующего:

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]
5 голосов
/ 05 февраля 2017

Для одномерных массивов я бы порекомендовал np.flatnonzero(array == value)[0], что эквивалентно np.nonzero(array == value)[0][0] и np.where(array == value)[0][0], но позволяет избежать уродства распаковки кортежа из 1 элемента.

4 голосов
/ 17 апреля 2018

Для одномерных отсортированных массивов было бы намного проще и эффективнее 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")
4 голосов
/ 11 января 2009

В NumPy существует множество операций, которые можно объединить для достижения этой цели. Это вернет индексы элементов, равные item:

numpy.nonzero(array - item)

Затем вы можете взять первые элементы списков, чтобы получить один элемент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...