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

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

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

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

13 ответов


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

itemindex = numpy.where(array==item)

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

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

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

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

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

и NumPy.где


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

ненулевой метод также принимает логические значения:

index = numpy.nonzero(first_array == item)[0][0]

два нуля для кортежа индексов (предполагая, что first_array равен 1D), а затем первый элемент в массив индексов.


просто добавить очень performant и удобный 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,)

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

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

и вот быстрая функция, чтобы сделать какой список.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]

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

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


для массивов 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 (позиция). Например,

i = np.searchsorted(np.array([1, 1, 1, 2, 3, 3, 4]), 3)

просто убедитесь, что вы отсортировали массив раньше, если он еще не отсортирован.


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


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