2016-02-24 26 views
5

Angesichts einer Liste von ganzen Zahlen, was ist die Pythisch/beste Art zu zählen, wie viele Elemente innerhalb eines bestimmten Bereichs sind?Python: Anzahl der Elemente in der Liste für if Bedingung

Ich recherchieren und fand zwei Möglichkeiten, es zu tun:

>>> x = [10, 60, 20, 66, 79, 5] 
>>> len([i for i in x if 60 < i < 70]) 
1 

oder:

>>> x = [10, 60, 20, 66, 79, 5] 
>>> sum(1 for i in x if 60 < i < 70) 
1 

Welche Methode weniger Zeit/Speicher verwendet (für größere Listen) und warum? Oder vielleicht ist eine andere Art und Weise besser ...

+0

Benötigen Sie eigentlich * die * Liste? Wenn nicht, vermeidet die zweite Version es jemals zu erstellen. – jonrsharpe

+0

Die meisten Pythonic bedeutet nicht weniger Zeit/Gedächtnis, aber Sie fragen beide in Ihrer Frage. Willst du wissen, welches Python oder am effizientesten ist? –

+0

Sie können auch 'if i in range (61, 70)' verwenden. –

Antwort

2

Der Generator Ausdruck ist Speicher effizient, weil Sie keine zusätzliche Liste erstellen müssen.

Erstellen einer Liste und ihrer Gesamtlänge bekommen (letzteres ein sehr schnelles O (1) ist Betrieb) scheint schneller zu sein als einen Generator zu schaffen und für relativ kleine Listen n Ergänzungen zu tun.

In [13]: x = [1] 
In [14]: timeit len([i for i in x if 60 < i < 70]) 
10000000 loops, best of 3: 141 ns per loop 
In [15]: timeit sum(1 for i in x if 60 < i < 70) 
1000000 loops, best of 3: 355 ns per loop 
In [16]: x = range(10) 
In [17]: timeit len([i for i in x if 60 < i < 70]) 
1000000 loops, best of 3: 564 ns per loop 
In [18]: timeit sum(1 for i in x if 60 < i < 70) 
1000000 loops, best of 3: 781 ns per loop 
In [19]: x = range(50) 
In [20]: timeit len([i for i in x if 60 < i < 70]) 
100000 loops, best of 3: 2.4 µs per loop 
In [21]: timeit sum(1 for i in x if 60 < i < 70) 
100000 loops, best of 3: 2.62 µs per loop 
In [22]: x = range(1000) 
In [23]: timeit len([i for i in x if 60 < i < 70]) 
10000 loops, best of 3: 50.9 µs per loop 
In [24]: timeit sum(1 for i in x if 60 < i < 70) 
10000 loops, best of 3: 51.7 µs per loop 

Ich habe versucht, mit verschiedenen Listen, zum Beispiel [65]*n und der Trend ändert sich nicht. Zum Beispiel:

In [1]: x = [65]*1000 
In [2]: timeit len([i for i in x if 60 < i < 70]) 
10000 loops, best of 3: 67.3 µs per loop 
In [3]: timeit sum(1 for i in x if 60 < i < 70) 
10000 loops, best of 3: 82.3 µs per loop 
4

In den besonderen Fällen Sie

präsentiert
[i for i in x if 60 < i < 70] 

tatsächlich erzeugt eine brandneue Liste, dann nimmt seinen len. Im Gegensatz dazu ist

(1 for i in x if 60 < i < 70) 

ein generator expression über die Sie einen sum nehmen.

Für ausreichend große relevante Elemente wird die zweite Version effizienter sein (insbesondere in Bezug auf Speicher).


Timings

x = [65] * 9999999 

%%time 

len([i for i in x if 60 < i < 70]) 

CPU times: user 724 ms, sys: 44 ms, total: 768 ms 
Wall time: 768 ms 
Out[7]: 
9999999 

%%time 

sum(1 for i in x if 60 < i < 70) 
CPU times: user 592 ms, sys: 0 ns, total: 592 ms 
Wall time: 593 ms 
+0

Ich frage den Geschwindigkeitsvorteil, den Sie dem Generator geben. Meine Timings können den Generator nicht schneller reproduzieren. In der Listenversion müssen Sie keine Zusätze hinzufügen, sondern nur die Länge. – timgeb

+0

@timgeb In der Listenversion spielt möglicherweise die Garbage Collection eine Rolle. –

+0

@FrerichRaabe das ist ein guter Punkt – timgeb

2

Sie können dies leicht testen Sie die timeit Modul. Für Ihr spezielles Beispiel die erste len -basierte Lösung scheint schneller zu sein:

$ python --version 
Python 2.7.10 
$ python -m timeit -s "x = [10,60,20,66,79,5]" "len([i for i in x if 60 < i < 70])" 
1000000 loops, best of 3: 0.514 usec per loop 
$ python -m timeit -s "x = [10,60,20,66,79,5]" "sum(i for i in x if 60 < i < 70)" 
1000000 loops, best of 3: 0.693 usec per loop 

Auch für größere Listen - aber mit den meisten Elementen Ihr Prädikat nicht passend - die len Version nicht langsamer zu sein scheint:

$ python -m timeit -s "x = [66] + [8] * 10000" "len([i for i in x if 60 < i < 70])" 
1000 loops, best of 3: 504 usec per loop 
$ python -m timeit -s "x = [66] + [8] * 10000" "sum(1 for i in x if 60 < i < 70)" 
1000 loops, best of 3: 501 usec per loop 

In der Tat, auch wenn die meisten Elemente der Liste Spiel gegeben (so eine große Ergebnisliste passieren zu len aufgebaut ist), die len Version gewinnt:

$ python -m timeit -s "x = [66] + [65] * 10000" "len([i for i in x if 60 < i < 70])" 
1000 loops, best of 3: 762 usec per loop 
$ python -m timeit -s "x = [66] + [65] * 10000" "sum(1 for i in x if 60 < i < 70)" 
1000 loops, best of 3: 935 usec per loop 

Was jedoch viel schneller scheint, ist, wenn möglich, keine Liste an erster Stelle zu haben, sondern z. a collections.Counter. Z.B.Für 100000 Elemente erhalte ich:

$ python -m timeit -s "import collections; x = [66] + [65] * 100000" "len([i for i in x if 60 < i < 70])" 
100 loops, best of 3: 8.11 msec per loop 
$ python -m timeit -s "import collections; x = [66] + [65] * 100000; d = collections.Counter(x)" "sum(v for k,v in d.items() if 60 < k < 70)" 
1000000 loops, best of 3: 0.761 usec per loop 
Verwandte Themen