Wie wähle ich Elemente eines Arrays unter bestimmten Bedingungen aus?

156

Angenommen , ich eine numpy Array haben x = [5, 2, 3, 1, 4, 5], y = ['f', 'o', 'o', 'b', 'a', 'r']. Ich möchte die Elemente auswählen, die Elementen yentsprechen x, die größer als 1 und kleiner als 5 sind.

Ich habe es versucht

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

aber das funktioniert nicht. Wie würde ich das machen?

Bob
quelle

Antworten:

219

Ihr Ausdruck funktioniert, wenn Sie Klammern hinzufügen:

>>> y[(1 < x) & (x < 5)]
array(['o', 'o', 'a'], 
      dtype='|S1')
jfs
quelle
1
Das ist schön. VecMask = 1 <x generiert eine Vektormaske wie vecMask = (False, True, ...), die einfach mit anderen Vektormasken kombiniert werden kann. Jedes Element ist die Bedingung, um die Elemente eines Quellvektors zu übernehmen (True) oder nicht (False). Dies kann auch mit der Vollversion numpy.extract (vecMask, vecSrc) oder numpy.where (vecMask, vecSrc, vecSrc2) verwendet werden.
MasterControlProgram
6
@ JennyYueJin: Es passiert wegen der Priorität. (Bitweise) &hat eine höhere Priorität als <und >, die wiederum eine höhere Priorität als (logisch) haben and. x > 1 and x < 5evouliert zuerst die Ungleichungen und dann die logische Konjunktion; x > 1 & x < 5wertet die bitweise Konjunktion von 1und (die Werte in) xund dann die Ungleichungen aus. (x > 1) & (x < 5)zwingt die Ungleichungen dazu, zuerst zu bewerten, sodass alle Operationen in der beabsichtigten Reihenfolge ausgeführt werden und die Ergebnisse genau definiert sind. Siehe Dokumente hier.
Calavicci
@ ru111 Es funktioniert auch unter Python 3.6 (es gibt keinen Grund dafür, nicht mehr zu arbeiten).
JFS
Ich erhalte "ValueError: Der Wahrheitswert eines Arrays mit mehr als einem Element ist nicht eindeutig. Verwenden Sie a.any () oder a.all ()"
ru111
@ ru111 sollten Sie schreiben (0 < x) & (x < 10)(wie in der Antwort gezeigt), stattdessen 0 < x < 10funktioniert dies nicht für numpy Arrays in einer Python-Version.
JFS
34

IMO OP will eigentlich nicht np.bitwise_and()(aka &), sondern eigentlich, np.logical_and()weil sie logische Werte wie Trueund vergleichen False- siehe diesen SO-Beitrag auf logisch vs. bitweise , um den Unterschied zu sehen.

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

Ein gleichwertiger Weg, dies zu tun, besteht darin np.all(), das axisArgument entsprechend festzulegen.

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

durch die Zahlen:

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

so mit np.all()langsamer, aber &und logical_andsind in etwa gleich.

Mark Mikofski
quelle
7
Sie müssen ein wenig vorsichtig sein, wie Sie darüber sprechen, was bewertet wird. Zum Beispiel in output = y[np.logical_and(x > 1, x < 5)], x < 5 wird ausgewertet (möglicherweise eine enorme Array erstellen), auch wenn es das zweite Argument ist, weil die Auswertung außerhalb der Funktion geschieht. IOW, logical_anderhält zwei bereits ausgewertete Argumente. Dies unterscheidet sich von dem üblichen Fall von a and b, in dem bnicht bewertet wird, ob aes wahr ist.
DSM
15
Es gibt keinen Unterschied zwischen bitweise_und () und logisch_und () für boolesche Arrays
jfs
21

Fügen Sie den Antworten von @JF Sebastian und @Mark Mikofski ein Detail hinzu:
Wenn Sie die entsprechenden Indizes (anstelle der tatsächlichen Werte des Arrays) erhalten möchten , reicht der folgende Code aus:

Zur Erfüllung mehrerer (aller) Bedingungen:

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

Zur Erfüllung mehrerer (oder) Bedingungen:

select_indices = np.where( np.logical_or( x < 1, x > 5 ) )[0] # x <1 or x >5
Guter Wille
quelle
2
Beachten Sie, dass numpy.where nicht nur ein Array der Indizes zurückgibt, sondern stattdessen ein Tupel (die Ausgabe von condition.nonzero ()), das Arrays enthält - in diesem Fall (the array of indices you want,)müssen select_indices = np.where(...)[0]Sie also das gewünschte Ergebnis erhalten und erwarten.
Calavicci
5

Ich benutze es gerne np.vectorizefür solche Aufgaben. Folgendes berücksichtigen:

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

Der Vorteil ist, dass Sie der vektorisierten Funktion viele weitere Arten von Einschränkungen hinzufügen können.

Ich hoffe es hilft.


quelle
1
Dies ist keine gute Möglichkeit, die Indizierung in NumPy durchzuführen (dies ist sehr langsam).
Alex Riley
1

Eigentlich würde ich es so machen:

L1 ist die Indexliste der Elemente, die die Bedingung 1 erfüllen (möglicherweise können Sie L1 verwenden somelist.index(condition1)oder np.where(condition1)erhalten.)

In ähnlicher Weise erhalten Sie L2, eine Liste von Elementen, die Bedingung 2 erfüllen;

Dann finden Sie die Kreuzung mit intersect(L1,L2).

Sie können auch Schnittpunkte mehrerer Listen finden, wenn Sie mehrere Bedingungen erfüllen müssen.

Dann können Sie den Index in jedem anderen Array anwenden, z. B. x.

Shuo Yang
quelle
0

Bei 2D-Arrays können Sie dies tun. Erstellen Sie unter Verwendung der Bedingung eine 2D-Maske. Geben Sie die Bedingungsmaske je nach Array in int oder float ein und multiplizieren Sie sie mit dem ursprünglichen Array.

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
quelle