Numpy wo Funktion mehrere Bedingungen

132

Ich habe eine Reihe von Entfernungen, die Dists genannt werden. Ich möchte Dists auswählen, die zwischen zwei Werten liegen. Ich habe dazu die folgende Codezeile geschrieben:

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

Dies wählt jedoch nur für die Bedingung

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

Wenn ich die Befehle nacheinander mit einer temporären Variablen ausführe, funktioniert das einwandfrei. Warum funktioniert der obige Code nicht und wie kann ich ihn zum Laufen bringen?

Prost

user1654183
quelle

Antworten:

203

Der beste Weg in Ihrem speziellen Fall wäre, Ihre beiden Kriterien in ein Kriterium zu ändern:

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

Es wird nur ein boolesches Array erstellt, und meiner Meinung nach ist es leichter zu lesen, da es besagt, dass es distinnerhalb eines droder r? (Obwohl ich neu definieren würde r, das Zentrum Ihrer Interessenregion zu sein, anstatt am Anfang, also r = r + dr/2.) Aber das beantwortet Ihre Frage nicht.


Die Antwort auf Ihre Frage:
Sie brauchen nicht wirklich, wherewenn Sie nur versuchen, die Elemente herauszufiltern dists, die nicht Ihren Kriterien entsprechen:

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

Weil das &Ihnen ein Element gibt and(die Klammern sind notwendig).

Oder wenn Sie whereaus irgendeinem Grund verwenden möchten , können Sie Folgendes tun:

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

Warum:
Der Grund, warum es nicht funktioniert, ist, dass np.whereeine Liste von Indizes zurückgegeben wird, kein boolesches Array. Sie versuchen, andzwischen zwei Zahlenlisten zu gelangen , die natürlich nicht die erwarteten True/ False-Werte haben . Wenn aund bbeide TrueWerte sind, wird a and bzurückgegeben b. Wenn Sie also so etwas sagen [0,1,2] and [2,3,4], erhalten Sie nur etwas [2,3,4]. Hier ist es in Aktion:

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

Was Sie erwartet hatten, um zu vergleichen, war zum Beispiel einfach das boolesche Array

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)

Jetzt können Sie np.wheredas kombinierte boolesche Array aufrufen :

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

Oder indizieren Sie einfach das ursprüngliche Array mit dem booleschen Array mithilfe einer ausgefallenen Indizierung

In [241]: dists[(dists >= r) & (dists <= r + dr)]
Out[241]: array([ 5. ,  5.5,  6. ])
askewchan
quelle
61

Die akzeptierte Antwort erklärte das Problem gut genug. Der mehr Numpythonische Ansatz zum Anwenden mehrerer Bedingungen besteht jedoch darin, numpy logische Funktionen zu verwenden . In dieser ase können Sie verwenden np.logical_and:

np.where(np.logical_and(np.greater_equal(dists,r),np.greater_equal(dists,r + dr)))
Kasramvd
quelle
11

Eine interessante Sache, auf die hier hingewiesen werden muss; In diesem Fall funktioniert auch die übliche Verwendung von OR und AND , jedoch mit einer kleinen Änderung. Verwenden Sie anstelle von "und" und anstelle von "oder" kaufmännisches Und (&) und Pipe Operator (|), und es funktioniert.

Wenn wir 'und' verwenden :

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

Wenn wir kaufmännisches Und (&) verwenden :

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

Dies gilt auch für den Fall, dass bei Pandas Dataframe mehrere Filter angewendet werden sollen. Die Gründe dafür haben nun etwas mit logischen Operatoren und bitweisen Operatoren zu tun. Um dies besser zu verstehen, würde ich vorschlagen, diese Antwort oder eine ähnliche Frage / Antwort im Stapelüberlauf durchzugehen.

AKTUALISIEREN

Ein Benutzer fragte, warum in der Klammer (ar> 3) und (ar <6) angegeben werden müssen. Nun, hier ist die Sache. Bevor ich darüber spreche, was hier passiert, muss man über die Priorität von Operatoren in Python Bescheid wissen.

Ähnlich wie bei BODMAS hat Python auch Vorrang vor dem, was zuerst ausgeführt werden sollte. Elemente in der Klammer werden zuerst ausgeführt, und dann kommt der bitweise Operator zur Arbeit. Ich werde unten zeigen, was in beiden Fällen passiert, wenn Sie "(", ")" verwenden und nicht verwenden.

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

Da es hier keine Klammern gibt, wird der bitweise Operator ( &) hier verwirrt, was Sie überhaupt verlangen, um ein logisches UND zu erhalten, da in der Operator-Vorrangtabelle, wenn Sie sehen, &Vorrang vor <oder >Operatoren hat. Hier ist die Tabelle von der niedrigsten zur höchsten Priorität.

Geben Sie hier die Bildbeschreibung ein

Es ist nicht einmal die Durchführung <und den >Betrieb und aufgefordert, eine logische UND - Operation auszuführen. Deshalb gibt es diesen Fehler.

Unter folgendem Link erfahren Sie mehr über: Vorrang des Operators

Nun zu Fall 2:

Wenn Sie die Halterung verwenden, sehen Sie deutlich, was passiert.

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)

Zwei Arrays von True und False. Und Sie können einfach logische UND-Operationen an ihnen ausführen. Welches gibt Ihnen:

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

Und ruhen Sie sich aus, np. Wo, für bestimmte Fälle, wo immer True, den ersten Wert (dh hier 'yo') und, wenn False, den anderen (dh hier, das Original behalten) zuweist.

Das ist alles. Ich hoffe ich habe die Abfrage gut erklärt.

Amit Amola
quelle
1
Warum müssen Sie setzen ()um (ar>3)und (ar>6)?
RTrain3k
Das ist eine wirklich gute Frage. Es ist eine so gute Frage, dass ich mir denken musste, was um alles in der Welt das Bedürfnis danach ist. Also habe ich auch einen Kollegen gefragt und wir haben darüber gesprochen und jetzt habe ich eine Lösung für Sie. Als UPDATE in die Antwort einfügen. Es ist wirklich einfach, aber schwer zu verstehen.
Amit Amola
Kasse das Update RTrain3k, ich habe Ihre Frage beantwortet.
Amit Amola
5

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

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

Sie können auch np.argwhereanstelle von np.wherefür eine klare Ausgabe verwenden. Aber das ist dein Anruf :)

Ich hoffe es hilft.


quelle
2

Versuchen:

np.intersect1d(np.where(dists >= r)[0],np.where(dists <= r + dr)[0])
Xin Wang
quelle
2

Das sollte funktionieren:

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

Der eleganteste Weg ~~

Qhan
quelle
2

Versuchen:

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

Ausgabe: (Array ([2, 3]),)

Weitere Details finden Sie unter Logikfunktionen .

Xiong-Hui Chen
quelle
0

Ich habe dieses einfache Beispiel ausgearbeitet

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
quelle
6
In diesem Fall muss nicht wiederholt werden. NumPy verfügt über eine boolesche Indizierung.
M456