Jak wybrać elementy tablicy z podanym warunkiem?

156

Załóżmy, że mam numpy tablicy x = [5, 2, 3, 1, 4, 5], y = ['f', 'o', 'o', 'b', 'a', 'r']. Chcę wybrać elementy yodpowiadające elementom w xktórych są większe niż 1 i mniejsze niż 5.

próbowałem

x = array([5, 2, 3, 1, 4, 5])
y = array(['f','o','o','b','a','r'])
output = y[x > 1 & x < 5] # desired output is ['o','o','a']

ale to nie działa. Jak bym to zrobił?

Pion
źródło

Odpowiedzi:

220

Twoje wyrażenie działa, jeśli dodasz nawiasy:

>>> y[(1 < x) & (x < 5)]
array(['o', 'o', 'a'], 
      dtype='|S1')
jfs
źródło
1
To miłe ... vecMask = 1 <x generuje maskę wektorową, taką jak vecMask = (False, True, ...), którą można po prostu połączyć z innymi maskami wektorowymi. Każdy element jest warunkiem przyjęcia elementów wektora źródłowego (True) lub nie (False). Można tego użyć również z pełną wersją numpy.extract (vecMask, vecSrc) lub numpy.where (vecMask, vecSrc, vecSrc2).
MasterControlProgram
6
@JennyYueJin: Dzieje się tak z powodu pierwszeństwa. (Bitowo) &ma wyższy priorytet niż <i >, które z kolei mają wyższy priorytet niż (logiczny) and. x > 1 and x < 5najpierw ewaluuje nierówności, a potem logiczną koniunkcję; x > 1 & x < 5oblicza bitową koniunkcję 1i (wartości w) x, a następnie nierówności. (x > 1) & (x < 5)zmusza nierówności do oceny w pierwszej kolejności, więc wszystkie operacje odbywają się w zamierzonej kolejności, a wyniki są dobrze zdefiniowane. Zobacz dokumentację tutaj.
calavicci
@ ru111 Działa również na Pythonie 3.6 (nie ma powodu, aby przestał działać).
jfs
Otrzymuję „ValueError: Prawda wartość tablicy z więcej niż jednym elementem jest niejednoznaczna. Użyj a.any () lub a.all ()”
ru111
@ ru111 powinieneś napisać (0 < x) & (x < 10)(jak pokazano w odpowiedzi), zamiast tego 0 < x < 10nie działa dla tablic numpy w żadnej wersji Pythona.
jfs
34

IMO OP tak naprawdę nie chce np.bitwise_and()(aka &), ale faktycznie chce, np.logical_and()ponieważ porównuje wartości logiczne, takie jak Truei False- zobacz ten post SO dotyczący logiki i bitów, aby zobaczyć różnicę.

>>> x = array([5, 2, 3, 1, 4, 5])
>>> y = array(['f','o','o','b','a','r'])
>>> output = y[np.logical_and(x > 1, x < 5)] # desired output is ['o','o','a']
>>> output
array(['o', 'o', 'a'],
      dtype='|S1')

Równoważnym sposobem jest np.all()odpowiednie ustawienie axisargumentu.

>>> output = y[np.all([x > 1, x < 5], axis=0)] # desired output is ['o','o','a']
>>> output
array(['o', 'o', 'a'],
      dtype='|S1')

według liczb:

>>> %timeit (a < b) & (b < c)
The slowest run took 32.97 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 1.15 µs per loop

>>> %timeit np.logical_and(a < b, b < c)
The slowest run took 32.59 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 1.17 µs per loop

>>> %timeit np.all([a < b, b < c], 0)
The slowest run took 67.47 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 5.06 µs per loop

więc używanie np.all()jest wolniejsze, ale &i logical_andsą mniej więcej takie same.

Mark Mikofski
źródło
7
Musisz być trochę ostrożny w tym, jak mówisz o tym, co jest oceniane. Na przykład in output = y[np.logical_and(x > 1, x < 5)], x < 5 jest oceniane (prawdopodobnie tworząc ogromną tablicę), mimo że jest to drugi argument, ponieważ ta ocena ma miejsce poza funkcją. IOW, logical_andotrzymuje dwa już ocenione argumenty. Różni się to od zwykłego przypadku a and b, w którym bnie jest oceniane, czy ajest prawdziwe.
DSM,
15
nie ma różnicy między bitwise_and () i logical_and () dla tablic boolowskich
jfs
21

Dodaj jeden szczegół do odpowiedzi @JF Sebastiana i @Marka Mikofskiego:
Jeśli chcesz uzyskać odpowiednie indeksy (zamiast rzeczywistych wartości tablicy), zrobi to następujący kod:

Aby spełnić wiele (wszystkich) warunków:

select_indices = np.where( np.logical_and( x > 1, x < 5) )[0] #   1 < x <5

Aby spełnić wiele (lub) warunków:

select_indices = np.where( np.logical_or( x < 1, x > 5 ) )[0] # x <1 or x >5
Życzliwość
źródło
2
Zwróć uwagę, że numpy.where nie zwróci tylko tablicy indeksów, ale zamiast tego zwróci krotkę (wynik warunku.nonzero ()) zawierającą tablice - w tym przypadku (the array of indices you want,), więc musisz select_indices = np.where(...)[0]uzyskać żądany wynik i oczekuj.
calavicci
5

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

>>> # Arrays
>>> x = np.array([5, 2, 3, 1, 4, 5])
>>> y = np.array(['f','o','o','b','a','r'])

>>> # Function containing the constraints
>>> func = np.vectorize(lambda t: t>1 and t<5)

>>> # Call function on x
>>> y[func(x)]
>>> array(['o', 'o', 'a'], dtype='<U1')

Zaletą jest to, że w funkcji wektoryzowanej można dodać znacznie więcej typów ograniczeń.

Mam nadzieję, że to pomoże.


źródło
1
To nie jest dobry sposób na indeksowanie w NumPy (będzie to bardzo powolne).
Alex Riley
1

Właściwie zrobiłbym to w ten sposób:

L1 to lista indeksowa elementów spełniających warunek 1; (może możesz użyć somelist.index(condition1)lub, np.where(condition1)aby uzyskać L1).

Podobnie, otrzymujesz L2, listę elementów spełniających warunek 2;

Następnie znajdziesz przecięcie za pomocą intersect(L1,L2).

Możesz również znaleźć przecięcie wielu list, jeśli masz wiele warunków do spełnienia.

Następnie możesz zastosować indeks w dowolnej innej tablicy, na przykład x.

Shuo Yang
źródło
0

W przypadku tablic 2D możesz to zrobić. Utwórz maskę 2D, używając warunku. Prześlij maskę warunku na typ int lub float, w zależności od tablicy, i pomnóż ją przez oryginalną tablicę.

In [8]: arr
Out[8]: 
array([[ 1.,  2.,  3.,  4.,  5.],
       [ 6.,  7.,  8.,  9., 10.]])

In [9]: arr*(arr % 2 == 0).astype(np.int) 
Out[9]: 
array([[ 0.,  2.,  0.,  4.,  0.],
       [ 6.,  0.,  8.,  0., 10.]])
Gautam Sreekumar
źródło