Zliczanie liczby prawdziwych wartości logicznych na liście w języku Python

152

Mam listę wartości logicznych:

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

i szukam sposobu na policzenie liczby wystąpień Truena liście (więc w powyższym przykładzie chcę, aby był to zwrot 3). Znalazłem przykłady szukania liczby wystąpień określonych elementów, ale czy jest więcej skuteczny sposób na zrobienie tego, ponieważ pracuję z Booleansami? Myślę o czymś analogicznym do alllub any.

acs
źródło
Na przykład, jeśli pamiętasz, jak liczenie bitów zostało wykonane na sprzęcie przy użyciu tylko asemblera.
Vladislavs Dovgalecs

Odpowiedzi:

207

Truejest równa 1.

>>> sum([True, True, False, False, False, True])
3
Ignacio Vazquez-Abrams
źródło
23
To nie jest idiomatyczne i powoduje „nadużycie” przymusu typu bool.
Jan Segre
24
@Jan Segre, nie ma przymusu, bool to typ całkowity.
panda-34
25
@ panda-34, sprawdziłem i issubclass(bool, int)faktycznie trzyma, więc nie ma przymusu.
Jan Segre
153

listma countmetodę:

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

W rzeczywistości jest to bardziej wydajne niż sum, a także bardziej wyraźne co do zamiaru, więc nie ma powodu, aby używać 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
źródło
2
Nie mogę policzyć wartości fałszywych, jeśli jest również wartość 0
Kostanos
10
Nie możesz użyć sumdrugiej odpowiedzi, jeśli masz inne wartości „prawda” oprócz 1 lub Prawda. Poza tym w pytaniu nie było nic, ale Truelub False.
Mark Tolonen,
43

Jeśli interesuje Cię tylko stała True, proste sumjest w porządku. Należy jednak pamiętać, że w Pythonie obliczane Truesą również inne wartości . Bardziej niezawodnym rozwiązaniem byłoby użycie boolwbudowanego:

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

AKTUALIZACJA: Oto inne równie solidne rozwiązanie, które ma tę zaletę, że jest bardziej przejrzyste:

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

PS Python ciekawostki: True może być prawdą bez bycia 1. Uwaga: nie próbuj tego w pracy!

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

O wiele bardziej zła:

True = False
Ned Deily
źródło
Ok, widzę twój przykład i widzę, co on robi. Oprócz tego, że jest to wesołe, czy jest naprawdę dobry powód, aby zrobić to, co tutaj pokazałeś?
acs
1
Tak, w górnej części. Jak już wspomniałem, test w Pythonie dla „prawdziwości” (jak w ifinstrukcji) jest bardziej skomplikowany niż tylko testowanie True. Zobacz docs.python.org/py3k/library/stdtypes.html#truth . Miało True = 2to tylko podkreślić, że pojęcie „prawdy” jest bardziej złożone; z odrobiną dodatkowego kodu (np. używając bool()) możesz uczynić rozwiązanie bardziej niezawodnym i bardziej ogólnym.
Ned Deily,
9
W Pythonie 3 Truei Falsesą słowami kluczowymi i nie można ich zmienić.
ThePiercingPrince
8

Możesz użyć sum():

>>> sum([True, True, False, False, False, True])
3
Mikser
źródło
5

Ze względu na kompletność ( sumjest to zwykle preferowane), chciałem wspomnieć, że możemy również użyć filterdo uzyskania prawdziwych wartości. W zwykłym przypadku filterprzyjmuje funkcję jako pierwszy argument, ale jeśli ją przekażeszNone , będzie filtrować pod kątem wszystkich „prawdziwych” wartości. Ta funkcja jest nieco zaskakująca, ale jest dobrze udokumentowana i działa zarówno w Pythonie 2, jak i 3.

Różnica między wersjami polega na tym, że w Pythonie 2 filterzwraca listę, więc możemy użyć len:

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

Ale w Pythonie 3 filterzwraca iterator, więc nie możemy go używać len, a jeśli chcemy uniknąć używania sum(z jakiegokolwiek powodu), musimy uciec się do konwersji iteratora na listę (co czyni to znacznie mniej ładnym):

>>> 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
źródło
4

Po przeczytaniu wszystkich odpowiedzi i komentarzy na to pytanie, pomyślałem, że zrobię mały eksperyment.

I generowane 50.000 losowe wartości logicznych i nazywa sumi countna nich.

Oto moje wyniki:

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

Dla pewności powtórzyłem to jeszcze kilka razy:

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

I jak widać, countjest 3 razy szybszy niż sum. Więc proponuję użyć counttak, jak zrobiłem w count_it.

Wersja Pythona: 3.6.7
Rdzenie procesora: 4
Pamięć RAM: 16 GB
System operacyjny: Ubuntu 18.04.1 LTS

GMishx
źródło
3

Bezpieczniej jest boolnajpierw przejść . Można to łatwo zrobić:

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

Następnie złapiesz wszystko, co Python uzna za Prawda lub Fałsz, do odpowiedniego zasobnika:

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

Jeśli wolisz, możesz użyć zrozumienia:

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

źródło
1

Wolę len([b for b in boollist if b is True])(lub odpowiednik wyrażenia generatora), ponieważ jest to dość oczywiste. Mniej „magiczna” niż odpowiedź zaproponowana przez Ignacio Vazquez-Abrams.

Alternatywnie możesz to zrobić, co nadal zakłada, że ​​bool można zamienić na int, ale nie przyjmuje żadnych założeń dotyczących wartości True: ntrue = sum(boollist) / int(True)

kampu
źródło
Twoje rozwiązanie ma co najmniej dwa problemy. Po pierwsze, cierpi na ten sam problem z odpornością; które można naprawić, zmieniając test na if b. Ale, co ważniejsze, tworzysz listę jednorazową wymagającą, aby wszystkie wartości były jednocześnie w pamięci i nie możesz jej używać lenz wyrażeniem generatora. Lepiej unikać takich praktyk, aby można było skalować rozwiązanie.
Ned Deily,
@Ned Deily: if bjest dokładnie w błędzie. Byłoby poprawne tylko wtedy, gdyby pytanie dotyczyło elementów ocenianych jako Prawda, a nie rzeczywistych wartości logicznych True. Ale rozumiem twoją drugą kwestię. W takim razie jest wariant sum(1 if b is True else 0 for b in boollist).
kampu
Jak zauważyłem w innym miejscu, nie jest dla mnie jasne z pytania, czy OP naprawdę oznacza liczenie tylko obiektów typu bool o wartości 1, czy też oznacza większy i ogólnie bardziej użyteczny zestaw wartości, które oceniają jako prawdziwe. W pierwszym przypadku test tożsamości jest właściwym podejściem, ale też ogranicza. Obiekty typu bool i tak są raczej dziwnymi kaczkami w Pythonie, stosunkowo nowym dodatkiem do języka. W każdym razie sum(1 for b in boollist if b is True)
wybrałbym