Contando el número de booleanos verdaderos en una lista de Python

152

Tengo una lista de booleanos:

[True, True, False, False, False, True]

y estoy buscando una manera de contar el número de Trueen la lista (así que en el ejemplo anterior, quiero que sea el retorno 3). He encontrado ejemplos de cómo buscar el número de ocurrencias de elementos específicos, pero ¿hay más? forma eficiente de hacerlo ya que estoy trabajando con booleanos? Estoy pensando en algo análogo a allo any.

acs
fuente
Como si recuerdas cómo se realizó el conteo de bits en el hardware utilizando solo ensamblador.
Vladislavs Dovgalecs

Respuestas:

207

Truees igual a 1.

>>> sum([True, True, False, False, False, True])
3
Ignacio Vazquez-Abrams
fuente
23
Eso no es idiomático y hace "abuso" del tipo de coerción de bool.
Jan Segre
24
@ Jan Segre, no hay coerción, bool es un tipo entero.
panda-34
25
@ panda-34, lo comprobé y, issubclass(bool, int)de hecho, se mantiene, así que no hay coerción.
Jan Segre
152

listtiene un countmétodo:

>>> [True,True,False].count(True)
2

En realidad, esto es más eficiente que sum, además de ser más explícito sobre la intención, por lo que no hay razón para usar sum:

In [1]: import random

In [2]: x = [random.choice([True, False]) for i in range(100)]

In [3]: %timeit x.count(True)
970 ns ± 41.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [4]: %timeit sum(x)
1.72 µs ± 161 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Mark Tolonen
fuente
2
No puedo contar valores falsos si también hay un valor 0
Kostanos
10
No puede usar sumla otra respuesta si tiene otros valores "verdaderos" además de 1 o Verdadero. Además, la pregunta no menciona nada más que Trueo False.
Mark Tolonen
43

Si solo le preocupa la constante True, un simple sumestá bien. Sin embargo, tenga en cuenta que en Python también se evalúan otros valores True. Una solución más robusta sería usar el boolincorporado:

>>> l = [1, 2, True, False]
>>> sum(bool(x) for x in l)
3

ACTUALIZACIÓN: Aquí hay otra solución igualmente robusta que tiene la ventaja de ser más transparente:

>>> sum(1 for x in l if x)
3

Curiosidades de PS Python: True podría ser cierto sin ser 1. Advertencia: ¡no intentes esto en el trabajo!

>>> True = 2
>>> if True: print('true')
... 
true
>>> l = [True, True, False, True]
>>> sum(l)
6
>>> sum(bool(x) for x in l)
3
>>> sum(1 for x in l if x)
3

Mucho más malvado:

True = False
Ned Deily
fuente
Ok, veo tu ejemplo, y veo lo que está haciendo. Además de la LOL-ness, ¿hay realmente una buena razón para hacer lo que has mostrado aquí?
acs
1
Sí, para la parte superior. Como indiqué, la prueba de Python para un "verdadero" (como en una ifdeclaración) es más complicada que simplemente probar True. Ver docs.python.org/py3k/library/stdtypes.html#truth . El True = 2era sólo para reforzar el concepto de "verdad" es más complejo; con un poco de código adicional (es decir, usando bool()) puede hacer que la solución sea más robusta y más general.
Ned Deily
9
En Python 3, Truey Falseson palabras clave y no puede cambiarlas.
ThePiercingPrince
8

Puedes usar sum():

>>> sum([True, True, False, False, False, True])
3
Licuadora
fuente
5

Solo por el bien de la integridad ( sumgeneralmente es preferible), quería mencionar que también podemos usar filterpara obtener los valores de verdad. En el caso habitual, filteracepta una función como primer argumento, pero si la pasa None, filtrará todos los valores "verdaderos". Esta característica es algo sorprendente, pero está bien documentada y funciona tanto en Python 2 como en 3.

La diferencia entre las versiones es que en Python 2 filterdevuelve una lista, por lo que podemos usar len:

>>> bool_list = [True, True, False, False, False, True]
>>> filter(None, bool_list)
[True, True, True]
>>> len(filter(None, bool_list))
3

Pero en Python 3, filterdevuelve un iterador, por lo que no podemos usarlo len, y si queremos evitar el uso sum(por cualquier motivo) debemos recurrir a convertir el iterador en una lista (lo que hace que esto sea mucho menos bonito):

>>> bool_list = [True, True, False, False, False, True]
>>> filter(None, bool_list)
<builtins.filter at 0x7f64feba5710>
>>> list(filter(None, bool_list))
[True, True, True]
>>> len(list(filter(None, bool_list)))
3
yoniLavi
fuente
4

Después de leer todas las respuestas y comentarios sobre esta pregunta, pensé en hacer un pequeño experimento.

Generé 50,000 booleanos aleatorios y llamé sumy countsobre ellos.

Aquí están mis resultados:

>>> a = [bool(random.getrandbits(1)) for x in range(50000)]
>>> len(a)
50000
>>> a.count(False)
24884
>>> a.count(True)
25116
>>> def count_it(a):
...   curr = time.time()
...   counting = a.count(True)
...   print("Count it = " + str(time.time() - curr))
...   return counting
... 
>>> def sum_it(a):
...   curr = time.time()
...   counting = sum(a)
...   print("Sum it = " + str(time.time() - curr))
...   return counting
... 
>>> count_it(a)
Count it = 0.00121307373046875
25015
>>> sum_it(a)
Sum it = 0.004102230072021484
25015

Solo para estar seguro, lo repetí varias veces más:

>>> count_it(a)
Count it = 0.0013530254364013672
25015
>>> count_it(a)
Count it = 0.0014507770538330078
25015
>>> count_it(a)
Count it = 0.0013344287872314453
25015
>>> sum_it(a)
Sum it = 0.003480195999145508
25015
>>> sum_it(a)
Sum it = 0.0035257339477539062
25015
>>> sum_it(a)
Sum it = 0.003350496292114258
25015
>>> sum_it(a)
Sum it = 0.003744363784790039
25015

Y como puedes ver, countes 3 veces más rápido que sum. Por lo tanto, sugeriría usar countcomo lo hice en count_it.

Versión de Python: 3.6.7
Núcleos de CPU: 4
Tamaño de RAM: 16 GB
SO: Ubuntu 18.04.1 LTS

GMishx
fuente
3

Es más seguro correr boolprimero. Esto se hace fácilmente:

>>> sum(map(bool,[True, True, False, False, False, True]))
3

Luego, capturará todo lo que Python considera Verdadero o Falso en el cubo apropiado:

>>> allTrue=[True, not False, True+1,'0', ' ', 1, [0], {0:0}, set([0])]
>>> list(map(bool,allTrue))
[True, True, True, True, True, True, True, True, True]

Si lo prefiere, puede usar una comprensión:

>>> allFalse=['',[],{},False,0,set(),(), not True, True-1]
>>> [bool(i) for i in allFalse]
[False, False, False, False, False, False, False, False, False]

fuente
1

Prefiero len([b for b in boollist if b is True])(o el equivalente de la expresión del generador), ya que se explica por sí mismo. Menos 'mágico' que la respuesta propuesta por Ignacio Vázquez-Abrams.

Alternativamente, puede hacer esto, que aún asume que bool es convertible a int, pero no hace suposiciones sobre el valor de True: ntrue = sum(boollist) / int(True)

kampu
fuente
Su solución tiene al menos dos problemas. Uno, sufre el mismo problema de robustez; que podría solucionar simplemente cambiando la prueba a if b. Pero, lo que es más importante, está construyendo una lista de descarte que requiere que todos los valores estén en la memoria a la vez y no se puede usar lencon una expresión generadora. Es mejor evitar tales prácticas para que la solución pueda escalar.
Ned Deily
@Ned Deily: if bestá exactamente equivocado. Solo sería correcto si la pregunta fuera sobre elementos que evalúan como Verdaderos, en lugar de booleanos verdaderos reales. Sin embargo, tomo tu segundo punto. En ese caso está la variante sum(1 if b is True else 0 for b in boollist).
Kampu
Como señalé en otra parte, no me queda claro a partir de la pregunta si el OP realmente significa contar solo objetos de tipo bool con el valor 1 o significa el conjunto de valores más grande y generalmente más útil que evalúa verdadero. Si es lo primero, entonces una prueba de identidad es el enfoque correcto pero también es limitante. De todos modos, los objetos de tipo bool son patos bastante extraños en Python, una adición relativamente reciente al lenguaje. En cualquier caso, iría por lo más simple:sum(1 for b in boollist if b is True)
Ned Deily