Contando o número de True Booleans em uma lista Python

152

Eu tenho uma lista de booleanos:

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

e estou procurando uma maneira de contar o número da Truelista (por isso, no exemplo acima, quero que o retorno seja 3). Encontrei exemplos de como procurar o número de ocorrências de elementos específicos, mas há mais maneira eficiente de fazer isso, pois estou trabalhando com booleanos? Estou pensando em algo análogo a allou any.

acs
fonte
Como se você se lembrasse de como a contagem de bits foi feita no hardware usando apenas o assembler.
Vladislavs Dovgalecs

Respostas:

207

Trueé igual a 1.

>>> sum([True, True, False, False, False, True])
3
Ignacio Vazquez-Abrams
fonte
23
Isso não é idiomático e faz "abuso" do tipo de coerção do bool.
Jan Segre
24
@ Jan Segre, não há coerção, bool é um tipo inteiro.
Panda-34
25
@ panda-34, eu verifiquei e issubclass(bool, int), de fato, detém, então não há coerção.
Jan Segre
152

listtem um countmétodo:

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

Isso é realmente mais eficiente do que sum, além de ser mais explícito sobre a intenção, portanto não há razão 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
fonte
2
Eu não posso contar valores falsos se também houver 0
#
10
Você não pode usar suma outra resposta se tiver outros valores "verdadeiros" além de 1 ou Verdadeiro. Além disso, então a pergunta não mencionou nada além de Trueou False.
Mark Tolonen
43

Se você está preocupado apenas com a constante True, um simples sumé bom. No entanto, lembre-se de que no Python outros valores também são avaliados True. Uma solução mais robusta seria usar o boolbuiltin:

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

ATUALIZAÇÃO: Aqui está outra solução igualmente robusta que tem a vantagem de ser mais transparente:

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

Curiosidades PS Python: True poderia ser verdade sem ser 1. Aviso: não tente fazer isso no trabalho!

>>> 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

Muito mais maldade:

True = False
Ned Deily
fonte
Ok, vejo seu exemplo e vejo o que está fazendo. Tirando o LOL-ness, existe realmente uma boa razão para fazer o que você mostrou aqui?
ACS
1
Sim, para a parte superior. Como indiquei, o teste do Python para um "verdadeiro" (como em uma ifdeclaração) é mais complicado do que apenas testar True. Consulte docs.python.org/py3k/library/stdtypes.html#truth . O True = 2era apenas para reforçar que o conceito de "verdade" é mais complexa; com um pouco de código extra (ou seja, usando bool()), você pode tornar a solução mais robusta e mais geral.
Ned Deily
9
Em Python 3, Truee Falsesão palavras-chave e você não pode alterá-los.
ThePiercingPrince
8

Você pode usar sum():

>>> sum([True, True, False, False, False, True])
3
Liquidificador
fonte
5

Por uma questão de completude ( sumgeralmente é preferível), eu queria mencionar que também podemos usar filterpara obter os valores verdadeiros. No caso usual, filteraceita uma função como o primeiro argumento, mas se você a passar None, ela filtrará todos os valores "verdade". Esse recurso é um tanto surpreendente, mas está bem documentado e funciona em Python 2 e 3.

A diferença entre as versões é que no Python 2 filterretorna uma lista, para que possamos usar len:

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

Mas no Python 3, filterretorna um iterador, portanto não podemos usá-lo lene, se queremos evitar o uso sum(por qualquer motivo), precisamos recorrer à conversão do iterador em uma lista (o que torna muito 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
fonte
4

Depois de ler todas as respostas e comentários sobre essa pergunta, pensei em fazer um pequeno experimento.

I gerados 50.000 booleans aleatórios e chamado sume countsobre eles.

Aqui estão meus 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

Só para ter certeza, repeti várias vezes:

>>> 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

E como você pode ver, counté 3 vezes mais rápido que sum. Então, eu sugeriria usar countcomo fiz count_it.

Versão Python: 3.6.7
Núcleos da CPU: 4
Tamanho da RAM: 16 GB
SO: Ubuntu 18.04.1 LTS

GMishx
fonte
3

É mais seguro percorrer boolprimeiro. Isso é feito facilmente:

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

Então, você capturará tudo o que o Python considerar Verdadeiro ou Falso no intervalo apropriado:

>>> 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]

Se preferir, você pode usar uma compreensão:

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

fonte
1

Eu prefiro len([b for b in boollist if b is True])(ou o equivalente da expressão geradora), pois é bastante auto-explicativo. Menos 'mágico' do que a resposta proposta por Ignacio Vazquez-Abrams.

Como alternativa, você pode fazer isso, que ainda assume que o bool é convertível em int, mas não faz suposições sobre o valor de True: ntrue = sum(boollist) / int(True)

kampu
fonte
Sua solução tem pelo menos dois problemas. Um, sofre do mesmo problema de robustez; que você pode corrigir alterando apenas o teste para if b. Mas, mais importante, você está construindo uma lista descartável que exige que todos os valores estejam na memória de uma só vez e não pode ser usada lencom uma expressão geradora. Melhor evitar essas práticas para que a solução possa ser dimensionada.
Ned Deily #
@ Ned Deily: if bestá exatamente errado. Seria correto se a pergunta fosse sobre itens avaliados como True, em vez de verdadeiros booleanos. No entanto, entendo o seu segundo ponto. Nesse caso, há a variante sum(1 if b is True else 0 for b in boollist).
Kampu
Como observei em outro lugar, não está claro para mim a questão de saber se o OP realmente significa contar apenas objetos do tipo bool com o valor 1 ou significa o conjunto maior e geralmente mais útil de valores que avaliam true. Se for o primeiro, um teste de identidade é a abordagem correta, mas também é limitativa. Objetos do tipo bool são patos bastante estranhos no Python, de qualquer forma, uma adição relativamente recente à linguagem. Em qualquer caso, eu iria para o mais simples:sum(1 for b in boollist if b is True)
Ned Deily