Filtern einer Liste basierend auf einer Liste von Booleschen Werten

127

Ich habe eine Liste von Werten, die ich filtern muss, wenn die Werte in einer Liste von Booleschen Werten enthalten sind:

list_a = [1, 2, 4, 6]
filter = [True, False, True, False]

Ich erstelle eine neue gefilterte Liste mit der folgenden Zeile:

filtered_list = [i for indx,i in enumerate(list_a) if filter[indx] == True]

was in ... endet:

print filtered_list
[1,4]

Die Linie funktioniert, sieht aber (für mich) etwas übertrieben aus und ich habe mich gefragt, ob es einen einfacheren Weg gibt, dasselbe zu erreichen.


Ratschläge

Zusammenfassung von zwei guten Ratschlägen in den folgenden Antworten:

1- Benennen Sie eine Liste nicht filterwie ich, da es sich um eine integrierte Funktion handelt.

2- Vergleichen Sie Dinge nicht so, Truewie ich es getan habe, if filter[idx]==True..da dies unnötig ist. Nur zu benutzen if filter[idx]ist genug.

Gabriel
quelle
3
Nur zu Ihrer Information, dies ist ein allgemeines Parallel-Computing-Grundelement, das als Stream-Komprimierung bezeichnet wird . (Es wird als "primitiv" bezeichnet, nicht weil es einfach ist, sondern weil es als Baustein für viele andere parallele Algorithmen verwendet wird.)
BlueRaja - Danny Pflughoeft
2
Einige Stil Notizen: if filter[indx] == TrueSie nicht verwenden , ==wenn Sie für Identität überprüfen möchten mit True, Verwendung is. Wie auch immer, in diesem Fall ist der gesamte Vergleich nutzlos, Sie könnten ihn einfach verwenden if filter[indx]. Zuletzt: Verwenden Sie niemals den Namen eines integrierten Systems als Variablen- / Modulnamen (ich beziehe mich auf den Namen filter). Mit so etwas wie included, damit das ifschön liest ( if included[indx]).
Bakuriu

Antworten:

184

Sie suchen itertools.compress:

>>> from itertools import compress
>>> list_a = [1, 2, 4, 6]
>>> fil = [True, False, True, False]
>>> list(compress(list_a, fil))
[1, 4]

Zeitvergleiche (py3.x):

>>> list_a = [1, 2, 4, 6]
>>> fil = [True, False, True, False]
>>> %timeit list(compress(list_a, fil))
100000 loops, best of 3: 2.58 us per loop
>>> %timeit [i for (i, v) in zip(list_a, fil) if v]  #winner
100000 loops, best of 3: 1.98 us per loop

>>> list_a = [1, 2, 4, 6]*100
>>> fil = [True, False, True, False]*100
>>> %timeit list(compress(list_a, fil))              #winner
10000 loops, best of 3: 24.3 us per loop
>>> %timeit [i for (i, v) in zip(list_a, fil) if v]
10000 loops, best of 3: 82 us per loop

>>> list_a = [1, 2, 4, 6]*10000
>>> fil = [True, False, True, False]*10000
>>> %timeit list(compress(list_a, fil))              #winner
1000 loops, best of 3: 1.66 ms per loop
>>> %timeit [i for (i, v) in zip(list_a, fil) if v] 
100 loops, best of 3: 7.65 ms per loop

Nicht filterals Variablenname verwenden, sondern eine integrierte Funktion.

Ashwini Chaudhary
quelle
@ Mehdi Ich finde den Matlab-Weg sehr unintuitiv, aber ich nehme an, es hängt davon ab, was Sie gewohnt sind.
Ian Goldby
Wie kann ich auswählen [2, 6]?
Florent
Ich verstehe, list(compress(list_a, [not i for i in fill]))sollte zurückkehren[2, 6]
Florent
42

Wie so:

filtered_list = [i for (i, v) in zip(list_a, filter) if v]

Die Verwendung zipist die pythonische Methode, um mehrere Sequenzen parallel zu durchlaufen , ohne dass eine Indizierung erforderlich ist. Dies setzt voraus, dass beide Sequenzen die gleiche Länge haben (Zip stoppt, nachdem die kürzeste abgelaufen ist). Die Verwendung itertoolsfür einen so einfachen Fall ist ein bisschen übertrieben ...

Eine Sache, die Sie in Ihrem Beispiel tun sollten, ist, Dinge mit True zu vergleichen. Dies ist normalerweise nicht erforderlich. Stattdessen if filter[idx]==True: ...können Sie einfach schreiben if filter[idx]: ....

Bas Swinckels
quelle
40

Mit numpy:

In [128]: list_a = np.array([1, 2, 4, 6])
In [129]: filter = np.array([True, False, True, False])
In [130]: list_a[filter]

Out[130]: array([1, 4])

oder sehen Sie sich die Antwort von Alex Szatmary an, wenn list_a ein numpy-Array sein kann, aber kein Filter

Numpy gibt dir normalerweise auch einen großen Geschwindigkeitsschub

In [133]: list_a = [1, 2, 4, 6]*10000
In [134]: fil = [True, False, True, False]*10000
In [135]: list_a_np = np.array(list_a)
In [136]: fil_np = np.array(fil)

In [139]: %timeit list(itertools.compress(list_a, fil))
1000 loops, best of 3: 625 us per loop

In [140]: %timeit list_a_np[fil_np]
10000 loops, best of 3: 173 us per loop
Hammer
quelle
Guter Punkt, ich ziehe mit NumPyüber , listsoweit möglich. Wenn Sie es listtrotzdem verwenden müssen, müssen Sie (mithilfe der NumPyLösung) np.arrayaus beiden Listen erstellen , die boolesche Indizierung verwenden und schließlich das Array mit der tolist()Methode wieder in eine Liste konvertieren . Um genau zu sein, sollten Sie diese Objekterstellung in den Zeitvergleich einbeziehen. Dann ist die Verwendung itertools.compressimmer noch die schnellste Lösung.
Nerxis
17

Verwenden Sie dazu numpy, dh wenn Sie ein Array haben a, anstelle von list_a:

a = np.array([1, 2, 4, 6])
my_filter = np.array([True, False, True, False], dtype=bool)
a[my_filter]
> array([1, 4])
Alex Szatmary
quelle
3
Wenn Sie my_filter in ein boolesches Array verwandeln, können Sie die direkte boolesche Indizierung verwenden, ohne dass dies erforderlich ist where.
Bas Swinckels
1
filtered_list = [list_a[i] for i in range(len(list_a)) if filter[i]]
Daniel Braun
quelle
-1

Mit Python 3 können list_a[filter]Sie TrueWerte abrufen. Um FalseWerte zu erhalten , verwenden Sielist_a[~filter]

Franklin'j Gil'z
quelle