Odrętwiały, gdzie działają wiele warunków

142

Mam tablicę odległości zwanych odległościami. Chcę wybrać odległości, które znajdują się między dwiema wartościami. Napisałem następujący wiersz kodu, aby to zrobić:

 dists[(np.where(dists >= r)) and (np.where(dists <= r + dr))]

Jednak wybiera to tylko dla warunku

 (np.where(dists <= r + dr))

Jeśli wykonuję polecenia sekwencyjnie, używając zmiennej tymczasowej, działa dobrze. Dlaczego powyższy kod nie działa i jak mam go uruchomić?

Twoje zdrowie

user1654183
źródło

Odpowiedzi:

209

Najlepszym sposobem w twoim konkretnym przypadku byłaby po prostu zmiana dwóch kryteriów na jedno kryterium:

dists[abs(dists - r - dr/2.) <= dr/2.]

Tworzy tylko jedną tablicę logiczną i moim zdaniem jest łatwiejszy do odczytania, ponieważ mówi, że znajduje się distw obrębie drlub r? (Chociaż przedefiniowałbym to, raby być centrum twojego regionu zainteresowania zamiast na początku, więc r = r + dr/2.) Ale to nie odpowiada na twoje pytanie.


Odpowiedź na twoje pytanie:
tak naprawdę nie potrzebujesz, wherejeśli próbujesz tylko odfiltrować elementy dists, które nie pasują do twoich kryteriów:

dists[(dists >= r) & (dists <= r+dr)]

Ponieważ &da ci elementarny and(nawiasy są konieczne).

Lub, jeśli wherez jakiegoś powodu chcesz użyć , możesz zrobić:

 dists[(np.where((dists >= r) & (dists <= r + dr)))]

Dlaczego:
Przyczyną tego nie jest to, że np.wherezwraca listę indeksów, a nie tablicę logiczną. Próbujesz dostać się andmiędzy dwiema listami liczb, które oczywiście nie mają oczekiwanych wartości True/ False. Jeśli obie wartości są ai , to zwraca . Więc powiedzenie czegoś takiego po prostu ci da . Oto akcja:bTruea and bb[0,1,2] and [2,3,4][2,3,4]

In [230]: dists = np.arange(0,10,.5)
In [231]: r = 5
In [232]: dr = 1

In [233]: np.where(dists >= r)
Out[233]: (array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),)

In [234]: np.where(dists <= r+dr)
Out[234]: (array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]),)

In [235]: np.where(dists >= r) and np.where(dists <= r+dr)
Out[235]: (array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]),)

Na przykład to, co spodziewałeś się porównać, to po prostu tablica boolowska

In [236]: dists >= r
Out[236]: 
array([False, False, False, False, False, False, False, False, False,
       False,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True], dtype=bool)

In [237]: dists <= r + dr
Out[237]: 
array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True, False, False, False, False, False,
       False, False], dtype=bool)

In [238]: (dists >= r) & (dists <= r + dr)
Out[238]: 
array([False, False, False, False, False, False, False, False, False,
       False,  True,  True,  True, False, False, False, False, False,
       False, False], dtype=bool)

Teraz możesz wywołać np.wherepołączoną tablicę boolowską:

In [239]: np.where((dists >= r) & (dists <= r + dr))
Out[239]: (array([10, 11, 12]),)

In [240]: dists[np.where((dists >= r) & (dists <= r + dr))]
Out[240]: array([ 5. ,  5.5,  6. ])

Lub po prostu zindeksuj oryginalną tablicę tablicą logiczną, używając fantazyjnego indeksowania

In [241]: dists[(dists >= r) & (dists <= r + dr)]
Out[241]: array([ 5. ,  5.5,  6. ])
askewchan
źródło
65

Przyjęta odpowiedź wystarczająco dobrze wyjaśniła problem. Jednak bardziej numpytonicznym podejściem do stosowania wielu warunków jest użycie funkcji logicznych numpy . W tej asie możesz użyć np.logical_and:

np.where(np.logical_and(np.greater_equal(dists,r),np.greater_equal(dists,r + dr)))
Kasravnd
źródło
Głosowano za użyciem np.logical_and. Dzięki!
S3DEV
13

Jedna interesująca rzecz, na którą należy zwrócić uwagę; zwykły sposób używania OR i AND również będzie działał w tym przypadku, ale z niewielką zmianą. Zamiast "and" i zamiast "or", użyj raczej Ampersand (&) i operatora potoku (|) i zadziała.

Kiedy używamy „i” :

ar = np.array([3,4,5,14,2,4,3,7])
np.where((ar>3) and (ar<6), 'yo', ar)

Output:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Kiedy używamy Ampersand (&) :

ar = np.array([3,4,5,14,2,4,3,7])
np.where((ar>3) & (ar<6), 'yo', ar)

Output:
array(['3', 'yo', 'yo', '14', '2', 'yo', '3', '7'], dtype='<U11')

Tak samo jest w przypadku, gdy próbujemy zastosować wiele filtrów w przypadku pand Dataframe. Teraz uzasadnienie tego ma coś wspólnego z operatorami logicznymi i operatorami bitowymi, a dla lepszego zrozumienia tego samego, proponuję przejść przez tę odpowiedź lub podobne pytania i odpowiedzi w przepływie stosu.

AKTUALIZACJA

Użytkownik zapytał, dlaczego istnieje potrzeba podania (ar> 3) i (ar <6) w nawiasach. Cóż, o to chodzi. Zanim zacznę mówić o tym, co się tutaj dzieje, trzeba wiedzieć o pierwszeństwie operatorów w Pythonie.

Podobnie jak w przypadku BODMAS, Python również daje pierwszeństwo temu, co powinno być wykonane jako pierwsze. Elementy w nawiasach są wykonywane jako pierwsze, a następnie do pracy dochodzi operator bitowy. Poniżej pokażę, co się dzieje w obu przypadkach, gdy używasz i nie używasz "(", ")".

Przypadek 1:

np.where( ar>3 & ar<6, 'yo', ar)
np.where( np.array([3,4,5,14,2,4,3,7])>3 & np.array([3,4,5,14,2,4,3,7])<6, 'yo', ar)

Ponieważ nie ma tu nawiasów, operator bitowy ( &) jest tu zdezorientowany, że o co prosisz go, aby uzyskać logiczne AND of, ponieważ w tabeli pierwszeństwa operatorów, jeśli widzisz, &ma pierwszeństwo przed operatorami <lub >. Oto tabela od najniższego do najwyższego priorytetu.

wprowadź opis obrazu tutaj

To nie jest nawet wykonanie <i >działanie i prosi, aby wykonać operację logiczną AND. Dlatego właśnie podaje ten błąd.

Możesz sprawdzić poniższy link, aby dowiedzieć się więcej o: pierwszeństwo operatorów

Teraz do przypadku 2:

Jeśli użyjesz wspornika, wyraźnie zobaczysz, co się stanie.

np.where( (ar>3) & (ar<6), 'yo', ar)
np.where( (array([False,  True,  True,  True, False,  True, False,  True])) & (array([ True,  True,  True, False,  True,  True,  True, False])), 'yo', ar)

Dwie tablice True i False. I możesz łatwo wykonywać na nich operacje logiczne AND. Co daje ci:

np.where( array([False,  True,  True, False, False,  True, False, False]),  'yo', ar)

A reszta wiecie, np. Gdzie, dla podanych przypadków, gdziekolwiek True, przypisuje pierwszą wartość (tj. Tutaj 'yo'), a jeśli False, drugą (czyli tutaj zachowując oryginał).

To wszystko. Mam nadzieję, że dobrze wyjaśniłem pytanie.

Amit Amola
źródło
1
Dlaczego trzeba umieścić ()wokół (ar>3)i (ar>6)?
RTrain3k
To naprawdę dobre pytanie. To tak dobre pytanie, że sam musiałem pomyśleć, że to, co u licha, jest tego potrzebne. Zrobiłem to, zapytałem też kolegę i omówiliśmy, czy mam dla ciebie rozwiązanie. Umieszczenie go w odpowiedzi jako AKTUALIZACJA. To naprawdę proste, ale naprawdę trudne do zrozumienia.
Amit Amola
Sprawdź aktualizację RTrain3k, odpowiedziałem na twoje zapytanie.
Amit Amola
5

Lubię używać np.vectorizedo takich zadań. Rozważ następujące:

>>> # function which returns True when constraints are satisfied.
>>> func = lambda d: d >= r and d<= (r+dr) 
>>>
>>> # Apply constraints element-wise to the dists array.
>>> result = np.vectorize(func)(dists) 
>>>
>>> result = np.where(result) # Get output.

Możesz także użyć np.argwherezamiast np.wheredla wyraźnego wydruku. Ale to twoja decyzja :)

Mam nadzieję, że to pomoże.


źródło
1
To jest BARDZO powolne. Jak już wspomniano w dokumencie: „Funkcja wektoryzacji jest zapewniana głównie dla wygody, a nie wydajności. Implementacja jest zasadniczo pętlą for”.
caoanan
2

Próbować:

np.intersect1d(np.where(dists >= r)[0],np.where(dists <= r + dr)[0])
Xin Wang
źródło
2

To powinno działać:

dists[((dists >= r) & (dists <= r+dr))]

Najbardziej elegancki sposób ~~

Qhan
źródło
2

Próbować:

import numpy as np
dist = np.array([1,2,3,4,5])
r = 2
dr = 3
np.where(np.logical_and(dist> r, dist<=r+dr))

Dane wyjściowe: (tablica ([2, 3]),)

Aby uzyskać więcej informacji, zobacz Funkcje logiczne .

Xiong-Hui Chen
źródło
0

Opracowałem ten prosty przykład

import numpy as np

ar = np.array([3,4,5,14,2,4,3,7])

print [X for X in list(ar) if (X >= 3 and X <= 6)]

>>> 
[3, 4, 5, 4, 3]
kiriloff
źródło
6
W tym przypadku nie ma potrzeby wykonywania iteracji. NumPy ma indeksowanie boolowskie.
M456