Czy istnieje funkcja NumPy, która zwraca pierwszy indeks czegoś w tablicy?

Odpowiedzi:

522

Tak, oto odpowiedź na podaną tablicę NumPy arrayi wartość item, aby wyszukać:

itemindex = numpy.where(array==item)

Wynikiem jest krotka z najpierw wszystkimi indeksami wierszy, a następnie wszystkimi indeksami kolumn.

Na przykład, jeśli tablica ma dwa wymiary i zawierała twój przedmiot w dwóch lokalizacjach

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

będzie równa twojemu przedmiotowi i tak też będzie

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

numpy.where

Alex
źródło
1
Jeśli szukasz pierwszego wiersza, w którym element istnieje w pierwszej kolumnie, to działa (chociaż spowoduje błąd indeksu, jeśli nie istnieje)rows, columns = np.where(array==item); first_idx = sorted([r for r, c in zip(rows, columns) if c == 0])[0]
BrT
27
Co jeśli chcesz, aby przestał szukać po znalezieniu pierwszej wartości? Nie sądzę, że where () jest porównywalne do find ()
Michael Clerx,
2
Ach! Jeśli interesuje Cię wydajność, sprawdź odpowiedź na to pytanie: stackoverflow.com/questions/7632963/...
Michael Clerx
11
np.argwherebyłby nieco bardziej przydatny tutaj:itemindex = np.argwhere(array==item)[0]; array[tuple(itemindex)]
Eric
3
Warto zauważyć, że ta odpowiedź zakłada, że ​​tablica jest 2D. wheredziała na dowolnej macierzy i zwróci krotkę o długości 3, gdy zostanie użyta na macierzy 3D itp.
P. Camilleri
69

Jeśli potrzebujesz indeksu pierwszego wystąpienia tylko jednej wartości , możesz użyć nonzero(lub where, co w tym przypadku odpowiada tej samej rzeczy):

>>> 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

Jeśli potrzebujesz pierwszego indeksu każdej z wielu wartości , możesz oczywiście zrobić to samo, co powyżej wielokrotnie, ale istnieje pewna sztuczka, która może być szybsza. Poniżej znajduje się indeks pierwszego elementu każdego podsekwencji :

>>> nonzero(r_[1, diff(t)[:-1]])
(array([0, 3, 5, 6, 7, 8]),)

Zauważ, że znajduje początek zarówno podsekwencji 3, jak i obu podsekwencji 8:

[ 1 , 1, 1, 2 , 2, 3 , 8 , 3 , 8 , 8]

Jest to więc nieco inne niż znalezienie pierwszego wystąpienia każdej wartości. W swoim programie możesz pracować z posortowaną wersją, taby uzyskać to, czego chcesz:

>>> st = sorted(t)
>>> nonzero(r_[1, diff(st)[:-1]])
(array([0, 3, 5, 7]),)
Vebjorn Ljosa
źródło
4
Czy możesz wyjaśnić, co r_to jest?
Geoff
1
@Geoff, r_konkatenuje; lub, dokładniej, przekłada obiekty wycięte na konkatenację wzdłuż każdej osi. Mógłbym hstackzamiast tego użyć ; to mogło być mniej mylące. Zobacz dokumentację uzyskać więcej informacji na temat r_. Istnieje również c_.
Vebjorn Ljosa
+1, fajny! (w porównaniu z NP. gdzie indziej) Twoje rozwiązanie jest o wiele prostsze (i prawdopodobnie szybsze) w przypadku, gdy potrzebujemy tylko pierwszego wystąpienia danej wartości w tablicy 1D
doug
3
Ten drugi przypadek (znalezienie pierwszego indeksu wszystkich wartości) podajevals, locs = np.unique(t, return_index=True)
askewchan,
50

Możesz także przekonwertować tablicę NumPy na listę w powietrzu i uzyskać jej indeks. Na przykład,

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

Wydrukuje 1.

Hima
źródło
Być może biblioteka uległa zmianie od czasu jej napisania. Ale to było pierwsze rozwiązanie, które zadziałało dla mnie.
amracel
1
Dobrze to wykorzystałem, aby znaleźć wiele wartości na liście, używając zrozumienia listy:[find_list.index(index_list[i]) for i in range(len(index_list))]
Matt Wenham
1
@MattWenham Jeśli jest wystarczająco duży, możesz przekonwertować find_listgo na tablicę NumPy object(lub cokolwiek bardziej szczegółowego, który jest odpowiedni) i po prostu zrobić find_arr[index_list].
Narfanar,
Zupełnie nie na temat, ale po raz pierwszy widzę wyrażenie „w powietrzu” - to, co widziałem najbardziej, na swoim miejscu, to prawdopodobnie „w locie”.
flow2k
18

Wystarczy dodać bardzo wydajny i poręczny alternatywa oparta na np.ndenumerateznalezieniu pierwszego indeksu:

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.

Jest to dość szybkie i naturalnie zajmuje się tablicami wielowymiarowymi :

>>> 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,)

Może to być znacznie szybsze (ponieważ powoduje to zwarcie operacji) niż jakakolwiek metoda wykorzystująca np.wherelub np.nonzero.


Jednak np.argwheremoże również z wdziękiem poradzić sobie z tablicami wielowymiarowymi (musisz ręcznie rzucić go na krotkę i nie jest zwarty), ale nie powiedzie się, jeśli nie zostanie znalezione dopasowanie:

>>> tuple(np.argwhere(arr1 == 2)[0])
(2, 2, 2)
>>> tuple(np.argwhere(arr2 == 2)[0])
(5,)
MSeifert
źródło
2
@njitjest skrótem, jit(nopython=True)tj. funkcja zostanie w pełni skompilowana w locie podczas pierwszego uruchomienia, tak aby wywołania interpretera Pythona zostały całkowicie usunięte.
bartolo-otrit
14

Jeśli zamierzasz użyć tego jako indeksu do czegoś innego, możesz użyć indeksów boolowskich, jeśli tablice są nadające; nie potrzebujesz wyraźnych wskaźników. Absolutnie najprostszym sposobem na to jest po prostu indeksowanie na podstawie wartości prawdy.

other_array[first_array == item]

Każda operacja logiczna działa:

a = numpy.arange(100)
other_array[first_array > 50]

Metoda niezerowa przyjmuje również logiczne:

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

Dwa zera oznaczają krotkę indeksów (zakładając, że first_array to 1D), a następnie pierwszy element w tablicy indeksów.

Matt
źródło
10

l.index(x)zwraca najmniejsze i, tak że i jest indeksem pierwszego wystąpienia x na liście.

Można bezpiecznie założyć, że index()funkcja w Pythonie jest zaimplementowana tak, że zatrzymuje się po znalezieniu pierwszego dopasowania, a to skutkuje optymalną średnią wydajnością.

Aby znaleźć element zatrzymujący się po pierwszym dopasowaniu w tablicy NumPy, użyj iteratora ( ndenumerate ).

In [67]: l=range(100)

In [68]: l.index(2)
Out[68]: 2

Tablica NumPy:

In [69]: a = np.arange(100)

In [70]: next((idx for idx, val in np.ndenumerate(a) if val==2))
Out[70]: (2L,)

Zauważ, że obie metody index()i nextzwracają błąd, jeśli element nie zostanie znaleziony. Za nextpomocą drugiego argumentu można zwrócić specjalną wartość na wypadek, gdyby element nie został znaleziony, np

In [77]: next((idx for idx, val in np.ndenumerate(a) if val==400),None)

Istnieją inne funkcje w NumPy ( argmax, wherei nonzero), których można użyć do znalezienia elementu w tablicy, ale wszystkie mają tę wadę, że przechodzą przez całą tablicę w poszukiwaniu wszystkich wystąpień, a zatem nie są zoptymalizowane pod kątem znalezienia pierwszego elementu. Zwróć także uwagę na to wherei nonzerozwracaj tablice, więc musisz wybrać pierwszy element, aby uzyskać indeks.

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),)

Porównanie czasu

Wystarczy sprawdzić, czy w przypadku dużych tablic rozwiązanie korzystające z iteratora jest szybsze, gdy szukany element znajduje się na początku tablicy (używając %timeitw powłoce 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

To jest otwarty problem NumPy GitHub .

Zobacz także: Numpy: szybko znajdź pierwszy indeks wartości

użytkownik2314737
źródło
1
Myślę, że powinieneś również podać czas na najgorszy przypadek (ostatni element), aby czytelnicy wiedzieli, co się z nimi dzieje w najgorszym przypadku, gdy zastosują twoje podejście.
MSeifert
@MSeifert Nie mogę znaleźć rozsądnego terminu na najgorsze rozwiązanie iteratora - zamierzam usunąć tę odpowiedź, dopóki nie dowiem się, co jest z nią nie tak
user2314737
1
nie %timeit next((idx for idx, val in np.ndenumerate(a) if val==99999))działa? Jeśli zastanawiasz się, dlaczego jest to 1000 razy wolniej - dzieje się tak dlatego, że pętle pythonowe nad tablicami liczb są bardzo powolne.
MSeifert
@MSeifert no ja nie wiem, ale jestem zaskoczony również przez fakt, że argmaxi wheresą znacznie szybsze w tym przypadku (poszukiwanej elementu na końcu tablicy)
user2314737
Powinny być tak szybkie, jakby element znajdował się na początku. Zawsze przetwarzają całą tablicę, więc zawsze zajmują tyle samo czasu (przynajmniej powinny).
MSeifert
9

W przypadku tablic sortowanych jednowymiarowo byłoby znacznie prostsze i wydajniejsze O (log (n)), aby użyć numpy.searchsorted, która zwraca liczbę całkowitą NumPy (pozycja). Na przykład,

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

Upewnij się tylko, że tablica jest już posortowana

Sprawdź także, czy zwrócony indeks i rzeczywiście zawiera szukany element, ponieważ głównym celem searchsorted jest znalezienie indeksów, w których należy wstawić elementy, aby zachować porządek.

if arr[i] == 3:
    print("present")
else:
    print("not present")
Alok Nayak
źródło
2
searchsorted nie jest nlog (n), ponieważ nie sortuje tablicy przed wyszukiwaniem, zakłada, że ​​tablica argumentów jest już posortowana. sprawdź dokumentację numpy.searchsorted (link powyżej)
Alok Nayak
6

Aby indeksować według dowolnych kryteriów, możesz wykonać następujące czynności:

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

A oto krótka funkcja pozwalająca zrobić to, co robi list.index (), z wyjątkiem tego, że nie zgłasza wyjątku, jeśli go nie znaleziono. Uwaga - na dużych tablicach jest to prawdopodobnie bardzo wolne. Prawdopodobnie możesz załatać to na tablice, jeśli wolisz użyć jej jako metody.

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]
Autoplektyka
źródło
5

Dla tablic 1D, polecam np.flatnonzero(array == value)[0], co jest równoważne zarówno np.nonzero(array == value)[0][0]a np.where(array == value)[0][0]jednak unika brzydotę unboxing 1-elementu krotki.

1 ''
źródło
4

Alternatywą dla wyboru pierwszego elementu z np.where () jest użycie wyrażenia generatora wraz z wyliczeniem, takiego jak:

>>> 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

W przypadku tablicy dwuwymiarowej można zrobić:

>>> 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)

Zaletą tego podejścia jest to, że przestaje on sprawdzać elementy tablicy po znalezieniu pierwszego dopasowania, podczas gdy np. Gdzie sprawdza wszystkie elementy pod kątem dopasowania. Wyrażenie generatora byłoby szybsze, gdyby dopasowanie było wczesne w tablicy.

Noyer282
źródło
W przypadku, gdy w tablicy może nie być żadnego dopasowania, ta metoda pozwala również wygodnie określić wartość rezerwową. Gdyby pierwszy przykład powrócił Nonejako rezerwowy, stałby się next((i for i, x_i in enumerate(x) if x_i == 2), None).
Erlend Magnus Viggen
4

Istnieje wiele operacji w NumPy, które mogą być połączone, aby to osiągnąć. Zwróci to indeksy elementów równe itemowi:

numpy.nonzero(array - item)

Następnie możesz wziąć pierwsze elementy list, aby uzyskać pojedynczy element.

Ned Batchelder
źródło
5
czy nie dałoby to wskaźników wszystkich elementów, które nie są równe pozycji?
Autoplectic
3

Numpy_indexed pakiet (Zastrzeżenie, jestem jego autorem) zawiera wektorowy równowartość list.index dla numpy.ndarray; to jest:

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]

To rozwiązanie ma wektoryzację wydajności, uogólnia do ndarrays i ma różne sposoby radzenia sobie z brakującymi wartościami.

Eelco Hoogendoorn
źródło
-1

Uwaga: dotyczy wersji Python 2.7

Do rozwiązania problemu możesz użyć funkcji lambda, która działa zarówno na tablicy NumPy, jak i na liście.

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]

I możesz użyć

result[0]

aby uzyskać pierwszy indeks filtrowanych elementów.

W przypadku python 3.6 użyj

list(result)

zamiast

result
Statham
źródło
Wynikiem tego jest <filter object at 0x0000027535294D30>Python 3 (testowany na Python 3.6.3). Być może aktualizacja dla Python 3?
Peter Mortensen