python: obtiene el número de elementos de la lista (secuencia) con cierta condición

84

Suponiendo que tengo una lista con una gran cantidad de elementos.

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

Quiero obtener la cantidad de elementos de esa lista, donde un elemento debe cumplir cierta condición. Mi primer pensamiento fue:

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

Pero si la lista filtrada my_condition () también tiene una gran cantidad de elementos, creo que crear una nueva lista para el resultado filtrado es solo una pérdida de memoria. Por eficiencia, en mi humilde opinión, la llamada anterior no puede ser mejor que:

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

¿Existe alguna forma de estilo funcional para lograr obtener el número de elementos que satisfacen cierta condición sin generar una lista temporal?

Gracias por adelantado.

cinsk
fuente
3
La elección entre generadores y listas es una elección entre el tiempo de ejecución y el consumo de memoria. Se sorprendería de la frecuencia con la que los resultados son contrarios a la intuición si perfila el código. La optimización prematura es la fuente de todos los males.
Paulo Scardine

Respuestas:

102

Puedes usar una expresión generadora :

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

o incluso

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

que utiliza el hecho de que int(True) == 1.

Alternativamente, puede usar itertools.imap(python 2) o simplemente map(python 3):

>>> def my_condition(x):
...     return x % 4 == 3
... 
>>> sum(map(my_condition, l))
2
DSM
fuente
1
@mgilson: No creo que nunca haga ese cálculo; el valor startpredeterminado es 0, por lo que la primera adición es True + 0, ¿no?
DSM
4
Si. Quizás debería ser más claro ... No importa lo que int(True)sea. int("1") == 1también, pero eso no significa que puedas hacerlo "1" + 0. Lo que importa es cómo Python evalúa integer + Trueo integer + False.
mgilson
2
@mgilson: hmm, está bien, me has convencido.
DSM
4
El punto es que booles una subclase de inty usted, por lo que puede agregar bools e ints fácilmente (con Trueun valor de 1 y Falseun valor de 0).
mgilson
Bueno, eso es a lo que me refería al mencionar int(True) == 1, pero su punto que int("1") == 1prueba que abreviarlo de esa manera puede implicar cosas que no son ciertas.
DSM
21

Quiere una comprensión del generador en lugar de una lista aquí.

Por ejemplo,

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

O usar itertools.imap (aunque creo que la lista explícita y las expresiones generadoras se ven algo más Pythonic).

Tenga en cuenta que, aunque no es obvio en el sumejemplo, puede componer muy bien las comprensiones del generador. Por ejemplo,

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

Lo bueno de esta técnica es que puede especificar pasos conceptualmente separados en el código sin forzar la evaluación y el almacenamiento en la memoria hasta que se evalúe el resultado final.

JohnJ
fuente
10

Esto también se puede hacer usando reducesi prefiere la programación funcional

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

De esta manera, solo hace 1 paso y no se genera una lista intermedia.

El pequeño alumno de Fermat
fuente
7

podrías hacer algo como:

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

que solo agrega 1 por cada elemento que cumple la condición.

Jsdodgers
fuente
2
from itertools import imap
sum(imap(my_condition, l))
kkonrad
fuente
2
imapno está disponible con Python actual.
Torsten Bronger