2010-05-24 6 views
34

Ho una lista di numeri interi e devo contare quanti di essi sono> 0.
Attualmente lo sto facendo con una lista di comprensione che assomiglia a questo:Conteggio di elementi interi positivi in ​​un elenco con le liste di Python

sum([1 for x in frequencies if x > 0]) 

Sembra una comprensione accettabile ma non mi piace molto il "1"; sembra un po 'un numero magico. C'è un modo più pitone per farlo?

+2

conteggio elementi non nulli non è la stessa di elementi di conteggio> 0. Il titolo dovrebbe essere modificato di conseguenza – joaquin

+0

Ho aggiornato il titolo della domanda in modo che rifletta il suo contenuto. Spero che stia bene con te. – EOL

risposta

63

Se si vuole ridurre la quantità di memoria, è possibile evitare di generare una lista temporanea utilizzando un generatore:

sum(x > 0 for x in frequencies) 

Questo funziona perché bool è una sottoclasse di int:

>>> isinstance(True,int) 
True 

e il valore True s' è 1:

>>> True==1 
True 

Tuttavia, come sottolinea Joe Golton nei commenti, questa soluzione non è molto veloce. Se hai abbastanza memoria per usare un elenco temporaneo intermedio, allora sth's solution potrebbe essere più veloce. Ecco alcuni tempi a confronto diverse soluzioni:

>>> frequencies = [random.randint(0,2) for i in range(10**5)] 

>>> %timeit len([x for x in frequencies if x > 0]) # sth 
100 loops, best of 3: 3.93 ms per loop 

>>> %timeit sum([1 for x in frequencies if x > 0]) 
100 loops, best of 3: 4.45 ms per loop 

>>> %timeit sum(1 for x in frequencies if x > 0) 
100 loops, best of 3: 6.17 ms per loop 

>>> %timeit sum(x > 0 for x in frequencies) 
100 loops, best of 3: 8.57 ms per loop 

Attenzione che i risultati timeit possono variare a seconda versione di Python, OS, o hardware.

Naturalmente, se si sta facendo matematica su una lunga lista di numeri, probabilmente si dovrebbe utilizzare NumPy:

>>> frequencies = np.random.randint(3, size=10**5) 
>>> %timeit (frequencies > 0).sum() 
1000 loops, best of 3: 669 us per loop 

L'array NumPy richiede meno memoria rispetto alla lista Python equivalente, e il calcolo può essere eseguito molto più velocemente di qualsiasi soluzione Python pura.

+2

Una variazione: [x> 0 per x in frequenze] .count (Vero) –

+3

@Peter: nota che il tuo suggerimento scorre due volte sopra i dati; una volta per creare l'elenco di output e due volte per contare i valori True. – tzot

+0

@ ΤΖΩΤΖΙΟΥ: Sì, certo, grazie per le informazioni! –

6

Potreste usare len() sul elenco filtrato:

len([x for x in frequencies if x > 0]) 
+2

ancora meglio, per utilizzare un generatore (strip [e]) –

+1

È possibile utilizzare il filtro con questo per renderlo più chiaro. len (filtro (lambda x: x> 0, frequenze)) –

+2

@valya: Questo non funzionerà con un generatore – sth

18

Un modo un po 'più Pythonic sarebbe quella di utilizzare un generatore invece:

sum(1 for x in frequencies if x > 0) 

questo modo si evita la generazione l'intera lista prima di chiamare sum().

+0

+1 perché questo è un modo comunemente trascurato di fare una comprensione. Se stai valutando una comprensione di una lista da una chiamata di funzione, puoi omettere '[]'. – jathanism

+0

Interrompe se nessuno degli elementi corrisponde ai criteri. – FogleBird

+0

@FogleBird: il 'sum()' di un generatore vuoto restituisce 0. –

0

Che ne dici di questo?

reduce(lambda x, y: x+1 if y > 0 else x, frequencies)

EDIT: Con ispirazione dalla risposta accettata da @ ~ unutbu:

reduce(lambda x, y: x + (y > 0), frequencies)

+0

Vorrei avere un commento per andare con quel voto negativo per imparare dai miei errori. Per favore? –

+0

Sembra esserci una tendenza lontana dalle funzioni lambda verso la comprensione delle liste. – fairfieldt

+1

Non ero uno per minimizzare; tuttavia, direi che le persone tendono a disapprovare "riduci", che vengono eliminate gradualmente (secondo la proclamazione di Guido). Mi piace 'reduce', ma anch'io disapprovo il suo uso in questo caso, poiché la variante' sum (x> 0 ...) 'mi sembra più semplice. – tzot

4

Questo funziona, ma l'aggiunta di bool s come int s può essere pericoloso. Prego richiedono questo codice con un grano di sale (manutenibilità va prima):

sum(k>0 for k in x) 
+2

L'aggiunta di booleani come interi è garantita per funzionare in Python 2 e 3: http://stackoverflow.com/questions/2764017/is-false-0-and-true-1-in-python-an-implementation-detail-or -garantito-da-t – EOL

+0

+1 per l'avviso, però. :) – EOL

0

Se l'array contiene solo elementi> = 0 (cioètutti gli elementi sono 0 o un numero intero positivo) allora si può solo contare gli zeri e sottrarre questo numero formare la lunghezza della matrice:

len(arr) - arr.count(0) 
Problemi correlati