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

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

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

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

К вашему сведению: Получение индексов сразу нескольких элементов в массиве NumPy

Franck Dernoncourt 24.08.2015 23:56
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
512
1
743 499
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

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

numpy.nonzero(array - item)

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

Разве это не дало бы индексы всех элементов, которые нет равны item?

Autoplectic 11.01.2009 05:06

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

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]

BrT 15.01.2013 17:44

Также взгляните на этот вопрос: stackoverflow.com/questions/7632963/…

Brian Larsen 19.06.2014 22:58

Что, если вы хотите, чтобы поиск прекратился после нахождения первого значения? Я не думаю, что where () сравнимо с find ()

Michael Clerx 20.11.2014 22:12

Ах! Если вас интересует производительность, ознакомьтесь с ответом на этот вопрос: stackoverflow.com/questions/7632963/…

Michael Clerx 20.11.2014 22:17
np.argwhere был бы здесь немного полезнее: itemindex = np.argwhere(array==item)[0]; array[tuple(itemindex)]
Eric 17.10.2016 23:46

Стоит отметить, что этот ответ предполагает, что массив является 2D. where работает с любым массивом и вернет кортеж длиной 3 при использовании в 3D-массиве и т. д.

P. Camilleri 05.07.2017 10:52

Из документации для numpy.where: Если указано только условие, эта функция является сокращением для np.asarray (condition) .nonzero (). Предпочтительно использовать прямое ненулевое значение, так как оно работает правильно для подклассов.

AMC 07.02.2020 02:17

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

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 23.03.2011 21:55

@Geoff, r_ объединяется; или, точнее, он переводит объекты срезов в конкатенацию по каждой оси. Вместо этого я мог бы использовать hstack; это могло быть менее запутанным. См. документация для получения дополнительной информации о r_. Также есть c_.

Vebjorn Ljosa 24.03.2011 22:58

+1, симпатичный! (vs NP.where) ваше решение намного проще (и, вероятно, быстрее) в случае, когда это только первое вхождение данного значения в одномерном массиве, которое нам нужно

doug 14.02.2014 05:33

Последний случай (поиск первого индекса всех значений) задается vals, locs = np.unique(t, return_index=True).

askewchan 02.11.2015 18:39

@askewchan ваша версия функционально эквивалентна, но намного медленнее

Jivan 11.06.2020 22:25

@Jivan, мне кажется, что я ошибся: unique находит первый индекс всех значений, но ответ дает индексы везде, где значение изменяется (т.е. имеет ненулевое различие). Они эквивалентны только в том случае, если массив t уже отсортирован (или все равные значения находятся в смежных группах).

askewchan 13.06.2020 01:41

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

Возможно, библиотека изменилась с момента ее написания. Но это было первое решение, которое у меня сработало.

amracel 03.04.2019 22:20

Я хорошо использовал это, чтобы найти несколько значений в списке, используя понимание списка: [find_list.index(index_list[i]) for i in range(len(index_list))]

Matt Wenham 30.04.2019 00:24

@MattWenham Если он достаточно большой, вы можете преобразовать свой find_list в массив NumPy из object (или что-то более конкретное, что подходит) и просто сделать find_arr[index_list].

Narfanar 30.04.2019 12:33

Совершенно не по теме, но я впервые вижу фразу «в воздухе» - то, что я видел больше всего, на ее месте, вероятно, «на лету».

flow2k 28.11.2019 03:25

Альтернативой выбора первого элемента из 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).

Erlend Magnus Viggen 28.06.2019 11:46

Просто чтобы добавить очень производительную и удобную альтернативу на основе 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 будут полностью удалены.
bartolo-otrit 02.10.2018 10:22

Начиная с версии не ниже 0.20.0, вы также можете написать его как генератор, чтобы все вхождения определенного значения можно было найти по запросу.

norok2 28.10.2020 19:32

Для одномерных массивов я бы рекомендовал 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 12.05.2017 17:08

@MSeifert Я не могу найти разумное время для наихудшего решения итератора - я собираюсь удалить этот ответ, пока не выясню, что с ним не так

user2314737 12.05.2017 17:51

не работает %timeit next((idx for idx, val in np.ndenumerate(a) if val==99999))? Если вам интересно, почему он в 1000 раз медленнее - это потому, что циклы python по numpy-массивам заведомо медленны.

MSeifert 12.05.2017 17:54

@MSeifert нет, я этого не знал, но меня также озадачивает тот факт, что argmax и where в этом случае намного быстрее (искомый элемент в конце массива)

user2314737 12.05.2017 18:00

Они должны быть такими же быстрыми, как если бы элемент был в начале. Они всегда обрабатывают весь массив, поэтому всегда занимают одно и то же время (по крайней мере, должны).

MSeifert 12.05.2017 18:02

@MSeifert Я имел ввиду, что итератор медленнее, argmax и where занимают одинаковое время, как и ожидалось

user2314737 12.05.2017 18:11

Это потому, что итерация по numpy-массивам с использованием python-loops не является хорошей идеей (потому что это очень медленно!).

MSeifert 12.05.2017 18:13

Пакет 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?

Peter Mortensen 26.06.2018 23:33

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

Alok Nayak 07.08.2018 19:31

Используйте 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)
  

Другие вопросы по теме