Python: Abrufen der Anzahl der Elemente aus der Liste (Sequenz) unter bestimmten Bedingungen

80

Angenommen, ich habe eine Liste mit einer großen Anzahl von Elementen.

l = [ 1, 4, 6, 30, 2, ... ]

Ich möchte die Anzahl der Elemente aus dieser Liste abrufen, wobei ein Element bestimmte Bedingungen erfüllen sollte. Mein erster Gedanke war:

count = len([i for i in l if my_condition(l)])

Wenn die gefilterte Liste my_condition () auch eine große Anzahl von Elementen enthält, ist das Erstellen einer neuen Liste für gefilterte Ergebnisse meiner Meinung nach nur Speicherverschwendung. Aus Effizienzgründen kann der obige Aufruf meiner Meinung nach nicht besser sein als:

count = 0
for i in l:
    if my_condition(l):
        count += 1

Gibt es eine funktionale Möglichkeit, um die Anzahl der Elemente zu ermitteln, die bestimmte Bedingungen erfüllen, ohne eine temporäre Liste zu erstellen?

Danke im Voraus.

Cinsk
quelle
3
Die Wahl zwischen Generatoren und Listen ist eine Wahl zwischen Ausführungszeit und Speicherverbrauch. Sie wären überrascht, wie oft die Ergebnisse nicht intuitiv sind, wenn Sie den Code profilieren. Vorzeitige Optimierung ist die Wurzel allen Übels.
Paulo Scardine

Antworten:

98

Sie können einen Generatorausdruck verwenden :

>>> l = [1, 3, 7, 2, 6, 8, 10]
>>> sum(1 for i in l if i % 4 == 3)
2

oder auch

>>> sum(i % 4 == 3 for i in l)
2

das nutzt die Tatsache, dass int(True) == 1.

Alternativ können Sie itertools.imap(Python 2) oder einfach map(Python 3) verwenden:

>>> def my_condition(x):
...     return x % 4 == 3
... 
>>> sum(map(my_condition, l))
2
DSM
quelle
1
@mgilson: Ich glaube nicht, dass diese Berechnung jemals durchgeführt wird - startstandardmäßig 0, also ist die erste Addition True + 0, nein?
DSM
4
Ja. Vielleicht sollte ich klarer sein ... Es ist egal was int(True)ist. int("1") == 1auch, aber das heißt nicht, dass du es kannst "1" + 0. Was zählt ist, wie Python bewertet integer + Trueoder integer + False.
mgilson
2
@mgilson: hmm, okay, du hast mich überzeugt.
DSM
4
Der Punkt ist, dass booles sich um eine Unterklasse von handelt intund Sie einfach Bools und Ints hinzufügen können (mit Trueeinem Wert von 1 und Falseeinem Wert von 0).
mgilson
Nun, das habe ich mit Erwähnung erreicht int(True) == 1, aber Ihr Punkt, der int("1") == 1beweist, dass eine Abkürzung auf diese Weise Dinge implizieren kann, die nicht wahr sind.
DSM
19

Sie möchten hier eher ein Generatorverständnis als eine Liste.

Zum Beispiel,

l = [1, 4, 6, 7, 30, 2]

def my_condition(x):
    return x > 5 and x < 20

print sum(1 for x in l if my_condition(x))
# -> 2
print sum(1 for x in range(1000000) if my_condition(x))
# -> 14

Oder verwenden Sie itertools.imap(obwohl ich denke, dass die expliziten Listen- und Generatorausdrücke etwas pythonischer aussehen).

Beachten Sie, dass Sie, obwohl dies aus dem sumBeispiel nicht ersichtlich ist, das Generatorverständnis gut zusammenstellen können. Zum Beispiel,

inputs = xrange(1000000)      # In Python 3 and above, use range instead of xrange
odds = (x for x in inputs if x % 2)  # Pick odd numbers
sq_inc = (x**2 + 1 for x in odds)    # Square and add one
print sum(x/2 for x in sq_inc)       # Actually evaluate each one
# -> 83333333333500000

Das Coole an dieser Technik ist, dass Sie konzeptionell separate Schritte im Code angeben können, ohne die Auswertung und Speicherung im Speicher zu erzwingen, bis das Endergebnis ausgewertet wird.

JohnJ
quelle
10

Dies kann auch mit erfolgen, reducewenn Sie eine funktionale Programmierung bevorzugen

reduce(lambda count, i: count + my_condition(i), l, 0)

Auf diese Weise machen Sie nur 1 Durchgang und es wird keine Zwischenliste generiert.

Fermats kleiner Student
quelle
7

Sie könnten so etwas tun wie:

l = [1,2,3,4,5,..]
count = sum(1 for i in l if my_condition(i))

Dies fügt nur 1 für jedes Element hinzu, das die Bedingung erfüllt.

Jsdodgers
quelle
2
from itertools import imap
sum(imap(my_condition, l))
kkonrad
quelle
2
imapist mit aktuellem Python nicht verfügbar.
Torsten Bronger