Jak działa python numpy.where ()?

94

Bawię się, numpyprzekopuję dokumentację i natrafiłem na magię. Mianowicie mówię o numpy.where():

>>> x = np.arange(9.).reshape(3, 3)
>>> np.where( x > 5 )
(array([2, 2, 2]), array([0, 1, 2]))

W jaki sposób osiągają wewnętrznie, że jesteś w stanie przekazać coś podobnego x > 5do metody? Myślę, że ma to coś wspólnego, __gt__ale szukam szczegółowego wyjaśnienia.

pajton
źródło

Odpowiedzi:

75

W jaki sposób wewnętrznie osiągają, że jesteś w stanie przekazać metodę podobną do x> 5?

Krótka odpowiedź brzmi: tak nie jest.

Każda operacja logiczna na tablicy numpy zwraca tablicę logiczną. (to znaczy __gt__, __lt__itp wszystkie powrotne tablic logicznych, gdzie dany warunek jest spełniony).

Na przykład

x = np.arange(9).reshape(3,3)
print x > 5

plony:

array([[False, False, False],
       [False, False, False],
       [ True,  True,  True]], dtype=bool)

To jest ten sam powód, dla którego coś takiego jak if x > 5:wywołuje ValueError, jeśli xjest tablicą numpy. Jest to tablica wartości prawda / fałsz, a nie pojedyncza wartość.

Ponadto tablice numpy mogą być indeksowane przez tablice logiczne. Np. W tym przypadku x[x>5]daje [6 7 8].

Szczerze mówiąc, jest to dość rzadkie, że faktycznie potrzebujesz, numpy.whereale zwraca tylko wskazania, w których znajduje się tablica logiczna True. Zwykle możesz zrobić to, czego potrzebujesz, dzięki prostemu indeksowaniu logicznemu.

Joe Kington
źródło
10
Aby zaznaczyć, że numpy.wheremają 2 „tryby operacyjne”, pierwszy zwraca wartość indices, gdzie condition is Truei jeśli występują parametry opcjonalne xi y(taki sam kształt conditionlub nadający się do nadawania do takiego kształtu!), Zwróci wartości z, xgdy jest condition is Trueinaczej y. Dzięki temu jest wherebardziej wszechstronny i umożliwia częstsze używanie. Dzięki
zjedz
1
W niektórych przypadkach może wystąpić również obciążenie przy użyciu __getitem__składni []over albo numpy.wherelub numpy.take. Ponieważ __getitem__musi również obsługiwać krojenie, jest trochę narzutu. Widziałem zauważalne różnice w szybkości podczas pracy ze strukturami danych Python Pandas i logicznym indeksowaniem bardzo dużych kolumn. W takich przypadkach, jeśli nie potrzebujesz krojenia, to takei wherefaktycznie są lepsze.
ely
24

Stara odpowiedź jest trochę zagmatwana. Daje ci LOKALIZACJE (wszystkie z nich), gdzie twoje stwierdzenie jest prawdziwe.

więc:

>>> a = np.arange(100)
>>> np.where(a > 30)
(array([31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
       48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
       65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
       82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
       99]),)
>>> np.where(a == 90)
(array([90]),)

a = a*40
>>> np.where(a > 1000)
(array([26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
       43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
       60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
       77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
       94, 95, 96, 97, 98, 99]),)
>>> a[25]
1000
>>> a[26]
1040

Używam go jako alternatywy dla list.index (), ale ma też wiele innych zastosowań. Nigdy nie używałem go z tablicami 2D.

http://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html

Nowa odpowiedź Wydaje się, że osoba pytała o coś bardziej fundamentalnego.

Pytanie brzmiało, jak TY mógłbyś zaimplementować coś, co pozwala funkcji (na przykład gdzie) wiedzieć, czego zażądano.

Najpierw zwróć uwagę, że wywołanie dowolnego z operatorów porównania robi interesującą rzecz.

a > 1000
array([False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True`,  True,  True,  True,  True,  True,  True,  True,  True,  True], dtype=bool)`

Odbywa się to poprzez przeciążenie metody „__gt__”. Na przykład:

>>> class demo(object):
    def __gt__(self, item):
        print item


>>> a = demo()
>>> a > 4
4

Jak widać, „a> 4” był prawidłowym kodem.

Możesz uzyskać pełną listę i dokumentację wszystkich przeciążonych funkcji tutaj: http://docs.python.org/reference/datamodel.html

Niesamowite jest to, jak łatwo to zrobić. WSZYSTKIE operacje w Pythonie są wykonywane w ten sposób. Powiedzenie a> b jest równoważne a. gt (b)!

Garrett Berg
źródło
3
To przeciążenie operatora porównania nie wydaje się jednak działać dobrze z bardziej złożonymi wyrażeniami logicznymi - na przykład nie mogę tego zrobić np.where(a > 30 and a < 50)lub np.where(30 < a < 50)ponieważ kończy się próbą obliczenia logicznego AND dwóch tablic logicznych, co jest dość bez znaczenia. Czy jest sposób, aby napisać taki stan np.where?
davidA
@meowsqueaknp.where((a > 30) & (a < 50))
tibalt
Dlaczego np.where () zwraca listę w Twoim przykładzie?
Andreas Yankopolus
0

np.wherezwraca krotkę o długości równej wymiarowi numpy ndarray, na której jest wywoływana (innymi słowy ndim), a każdy element krotki jest liczbą indeksów wszystkich tych wartości w początkowej tablicy ndarray, dla której warunek jest True. (Proszę nie mylić wymiaru z kształtem)

Na przykład:

x=np.arange(9).reshape(3,3)
print(x)
array([[0, 1, 2],
      [3, 4, 5],
      [6, 7, 8]])
y = np.where(x>4)
print(y)
array([1, 2, 2, 2], dtype=int64), array([2, 0, 1, 2], dtype=int64))


y jest krotką o długości 2, ponieważ x.ndimwynosi 2. Pierwsza pozycja w krotce zawiera numery wierszy wszystkich elementów większych niż 4, a druga pozycja zawiera numery kolumn wszystkich elementów większych niż 4. Jak widać, [1,2,2 , 2] odpowiada numerom wierszy 5,6,7,8, a [2,0,1,2] odpowiada numerom kolumn 5,6,7,8 Zauważ, że ndarray przechodzi wzdłuż pierwszego wymiaru (w odniesieniu do wierszy ).

Podobnie,

x=np.arange(27).reshape(3,3,3)
np.where(x>4)


zwróci krotkę o długości 3, ponieważ x ma 3 wymiary.

Ale czekaj, jest więcej do np. Gdzie!

kiedy dodawane są dwa dodatkowe argumenty np.where; wykona operację zamiany dla wszystkich tych kombinacji wierszy i kolumn w parach, które są uzyskiwane przez powyższą krotkę.

x=np.arange(9).reshape(3,3)
y = np.where(x>4, 1, 0)
print(y)
array([[0, 0, 0],
   [0, 0, 1],
   [1, 1, 1]])
Piyush Singh
źródło