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

<p>я знаю, что существует метод для списка python, который возвращает первый индекс чего-либо:</p><pre><code>>>> l = [1, 2, 3] >>> l.index(2) 1 </code></pre><p>есть ли что-то подобное для массивов numpy?</p>

<p>да, вот ответ с массивом numpy <code>array</code> и значением <code>item</code> для поиска:</p><pre><code>itemindex = numpy.where(array==item) </code></pre><p>в результате получается кортеж с сначала всеми индексами строк, а затем всеми индексами столбцов.</p><p>например, если массив имеет два измерения и содержит ваш элемент в двух местах, то</p><pre><code>array[itemindex[0][0]][itemindex[1][0]] </code></pre><p>будет равна вашему предмету, и поэтому будет</p><pre><code>array[itemindex[0][1]][itemindex[1][1]] </code></pre><p> <a href="http://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html">numpy.where</a> </p>

<p>если вам нужен индекс первого вхождения <strong>только одно значение</strong>, вы можете использовать <code>nonzero</code> (или <code>where</code>, что в данном случае равнозначно):</p><pre><code>>>> 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 </code></pre><p>если вам нужен первый индекс каждого из <strong>много значений</strong>, вы, очевидно, могли бы делать то же самое, что и выше, но есть хитрость, которая может быть быстрее. следующее находит индексы первого элемента каждой <em> подпоследовательности </em>:</p><pre><code>>>> nonzero(r\_[1, diff(t)[:-1]]) (array([0, 3, 5, 6, 7, 8]),) </code></pre><p>обратите внимание, что он находит начало как подпоследовательности 3s, так и обеих подпоследовательностей 8s:</p><p>[<strong>1</strong>, 1, 1, <strong>2</strong>, 2, , <strong>8</strong>, =, <strong>8</strong>, 8]</p><p>так что это немного отличается от нахождения первого <em> вхождения </em> каждого значения. в вашей программе вы можете работать с отсортированной версией <code>t</code>, чтобы получить то, что вы хотите:</p><pre><code>>>> st = sorted(t) >>> nonzero(r\_[1, diff(st)[:-1]]) (array([0, 3, 5, 7]),) </code></pre>

<p>вы также можете преобразовать массив numpy в список в эфир и получить его индекс. например,</p><pre><code>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 </code></pre><p>это напечатает 1.</p>

<p>если вы собираетесь использовать это как индекс для чего-то другого, вы можете использовать логические индексы, если массивы являются трансляционными; вам не нужны явные индексы. абсолютно простой способ сделать это - просто индексировать на основе истинного значения.</p><pre><code>other\_array[first\_array == item] </code></pre><p>любая логическая операция работает:</p><pre><code>a = numpy.arange(100) other\_array[first\_array > 50] </code></pre><p>ненулевой метод также принимает логические значения:</p><pre><code>index = numpy.nonzero(first\_array == item)[0][0] </code></pre><p>два нуля относятся к кортежу индексов (при условии, что first\_array равен 1d), а затем к первому элементу в массиве индексов.</p>

<p>просто чтобы добавить очень производительную и удобную альтернативу <a href="/questions/tagged/numba">numba</a> на основе <a href="https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndenumerate.html"><code>np.ndenumerate</code></a> , чтобы найти первый индекс:</p><pre><code>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. </code></pre><p>это довольно быстро и <em> имеет дело с многомерными массивами </em>:</p><pre><code>>>> 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,) </code></pre><p>это может быть <strong> намного быстрее </strong> (потому что это короткое замыкание операции), чем любой подход, использующий <code>np.where</code> или <code>np.nonzero</code>.</p><hr><p>однако <a href="https://docs.scipy.org/doc/numpy/reference/generated/numpy.argwhere.html"><code>np.argwhere</code></a> также может изящно <em> иметь дело </em> с многомерными массивами (вам необходимо вручную привести его к кортежу <strong> и </strong> это не закорочено) но не получится, если совпадение не найдено:</p><pre><code>>>> tuple(np.argwhere(arr1 == 2)[0]) (2, 2, 2) >>> tuple(np.argwhere(arr2 == 2)[0]) (5,) </code></pre>

<p><code>l.index(x)</code> возвращает наименьшее <em> i </em> такое, что <em> i </em> является индексом первого вхождение х в списке.</p><p>можно смело предположить, что функция <code>index()</code> в python реализована так, что останавливается после нахождения первого соответствия, и это приводит к оптимальной средней производительности.</p><p>чтобы найти остановку элемента после первого совпадения в массиве numpy, используйте итератор ( <a href="https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndenumerate.html">ndenumerate</a> ).</p><pre><code>in [67]: l=range(100) in [68]: l.index(2) out[68]: 2 </code></pre><p>массив numpy:</p><pre><code>in [69]: a = np.arange(100) in [70]: next((idx for idx, val in np.ndenumerate(a) if val==2)) out[70]: (2l,) </code></pre><p>обратите внимание, что оба метода <code>index()</code> и <code>next</code> возвращают ошибку, если элемент не найден. с <code>next</code> можно использовать второй аргумент для возврата специального значения в случае, если элемент не найден, например,.</p><pre><code>in [77]: next((idx for idx, val in np.ndenumerate(a) if val==400),none) </code></pre><p>в numpy есть и другие функции (<code>argmax</code>, <code>where</code> и <code>nonzero</code>), которые можно использовать для поиска элемента в массиве, но у всех них есть недостаток - проходить весь массив, ища <em> все </em> вхождений, поэтому не оптимизированы для поиска первого элемента. также обратите внимание, что <code>where</code> и <code>nonzero</code> возвращают массивы, поэтому вам нужно выбрать первый элемент, чтобы получить индекс.</p><pre><code>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),) </code></pre><h3>сравнение времени</h3><p>просто проверяя, что для больших массивов решение, использующее итератор, быстрее <em>, когда искомый элемент находится в начале массива </em> (используя <code>%timeit</code> в оболочке ipython) :</p><pre><code>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 </code></pre><hr><p>это открытый <a href="https://github.com/numpy/numpy/issues/2269">проблема numpy github</a> .</p><p>смотрите также: <a href="https://stackoverflow.com/q/7632963/2314737">numpy: быстро найти первый индекс значения</a> </p>

<p>для индексации по любым критериям вы можете сделать что-то вроде следующего:</p><pre><code>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 </code></pre><p>и вот быстрая функция, чтобы сделать то, что делает list.index (), за исключением того, что не вызывает исключение, если оно не найдено. осторожно - это, вероятно, очень медленно на больших массивах. вы, вероятно, можете использовать это для массивов, если вы предпочитаете использовать его как метод.</p><pre><code>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] </code></pre>

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

<p>для одномерных <strong> отсортированных </strong> массивов было бы намного проще и эффективнее o(log(n)) использовать - <a href="https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.searchsorted.html#numpy.searchsorted">numpy.searchsorted</a> , который возвращает целое число numpy (позиция). например,</p><pre><code>arr = np.array([1, 1, 1, 2, 3, 3, 4]) i = np.searchsorted(arr, 3) </code></pre><p>просто убедитесь, что массив уже отсортирован</p><p>также проверьте, что возвращаемый индекс i действительно содержит искомый элемент, так как главная цель searchsorted - найти индексы, в которые должны быть вставлены элементы для поддержания порядка.</p><pre><code>if arr[i] == 3: print("present") else: print("not present") </code></pre>

<p>в numpy существует множество операций, которые можно объединить для достижения этой цели. это вернет индексы элементов, равные item:</p><pre><code>numpy.nonzero(array - item) </code></pre><p>затем вы можете взять первые элементы списков, чтобы получить один элемент.</p>

<p>альтернативой выбору первого элемента из np.where () является использование выражения генератора вместе с перечислением, например:</p><pre><code>>>> 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 </code></pre><p>для двумерного массива можно сделать:</p><pre><code>>>> 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) </code></pre><p>преимущество этого подхода состоит в том, что он прекращает проверку элементов массива после того, как найдено первое совпадение, тогда как np.where проверяет все элементы на совпадение. выражение генератора будет быстрее, если в массиве есть совпадение.</p>

<p>пакет <a href="https://github.com/eelcohoogendoorn/numpy\_arraysetops\_ep">numpy\_indexed</a> (заявление об отказе от ответственности, я его автор) содержит векторизованный эквивалент list.index для numpy.ndarray; то есть:</p><pre><code>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] </code></pre><p>это решение векторизовало производительность, обобщает до ndarrays и имеет различные способы обработки пропущенных значений.</p>