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

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

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

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

Ответ 1

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

itemindex = numpy.where(array==item)

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

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

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

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

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

numpy.where

Ответ 2

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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: быстро найти первый индекс значения

Ответ 7

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

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]

Ответ 8

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

Ответ 9

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

numpy.nonzero(array - item)

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

Ответ 10

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

Ответ 11

Альтернативой выбору первого элемента из 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 проверяет все элементы для соответствия. Выражение генератора было бы быстрее, если бы оно соответствовало раннему массиву.

Ответ 12

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 и имеет различные способы устранения недостающих значений.

Ответ 13

Примечание: это для 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