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.


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.


@ 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]: ....


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

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

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

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.