Підрахунок кількості справжніх булів у списку Python


152

У мене є список булевих:

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

і я шукаю спосіб підрахувати кількість Trueу списку (тому у наведеному вище прикладі я хочу, щоб це було повернення 3.) Я знайшов приклади пошуку кількості зустрічань конкретних елементів, але чи є більше ефективний спосіб зробити це, оскільки я працюю з Booleans? Я думаю про щось аналогічне allабо any.


Начебто, якщо ви пам'ятаєте, як підрахунок бітів проводився лише в апараті за допомогою асемблера
Владислав Довгалеч

Відповіді:


207

Trueдорівнює 1.

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

23
Це не ідіоматично і робить «зловживання» типом примусу bool.
січ Сегре

24
@Jan Segre, примусу немає, bool є цілим типом.
панда-34

25
@ panda-34, я перевірив і issubclass(bool, int)насправді тримається, тому примусу немає.
Ян Сегре

153

listмає countметод:

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

Це насправді ефективніше, ніж sumчіткіше щодо намірів, тому немає ніяких причин використовувати 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)

2
Я не можу порахувати помилкових значень, якщо також є значення 0
Костанос

10
Ви не можете скористатися sumіншою відповіддю, якщо крім 1 або Істини є інші "істинні" значення. Крім того, тоді питання не згадувало нічого, окрім Trueабо False.
Марк Толонен

43

Якщо ви переймаєтесь лише постійним True, просто sum- це добре. Однак майте на увазі, що в Python також оцінюються й інші значення True. Більш надійним рішенням буде використання boolвбудованого:

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

ОНОВЛЕННЯ: Ось ще одне подібне надійне рішення, яке має перевагу бути прозорішим:

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

PS Python дрібниці: True може бути правдою, не будучи 1. Попередження: не намагайтеся цього робити на роботі!

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

Набагато більше зла:

True = False

Гаразд, я бачу ваш приклад, і я бачу, що це робить. Окрім LOL-ності цього, чи є насправді вагомий привід робити те, що ви тут показали?
акс

1
Так, для верхньої частини. Як я вже зазначив, тест Python для "істинного" (як у ifзаяві) є складнішим, ніж просто тестування True. Див. Docs.python.org/py3k/library/stdtypes.html#truth . Треба True = 2було лише підкріпити, що поняття "справжнє" є більш складним; маючи додатковий код (тобто використовуючи bool()), ви можете зробити рішення більш надійним та загальним.
Нед Дейлі

9
В Python 3, Trueі Falseключові слова , і ви не можете змінити їх.
ThePiercingPrince


5

Тільки для повноти ( sumяк правило, бажано), я хотів би зазначити, що ми також можемо використовувати filterдля отримання правдивих значень. У звичайному випадку filterприймає функцію в якості першого аргументу, але якщо ви передасте її None, вона буде фільтрувати всі "truthy" значення. Ця особливість є дещо дивною, але добре задокументована і працює як в Python 2, так і в 3.

Різниця між версіями полягає в тому, що в Python 2 filterповертає список, тому ми можемо використовувати len:

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

Але в Python 3 filterповертає ітератор, тому ми не можемо використовувати len, і якщо ми хочемо уникати використання sum(з будь-якої причини), нам потрібно вдатися до перетворення ітератора в список (що робить це набагато менш красивим):

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

4

Прочитавши всі відповіді та коментарі до цього питання, я подумав зробити невеликий експеримент.

Я генерував 50 000 випадкових булів і дзвонив sumі countна них.

Ось мої результати:

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

Напевне, я повторив це ще кілька разів:

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

І як бачите, countв 3 рази швидше, ніж sum. Тому я б запропонував використовувати так, countяк робив count_it.

Версія Python: 3.6.7
процесорних ядер: 4
розмір оперативної пам’яті: 16 Гб
ОС: Ubuntu 18.04.1 LTS


3

Безпечніше пробігти boolпершим. Це легко зробити:

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

Тоді ви перейдете все, що Python вважає True чи False, у відповідне відро:

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

Якщо ви віддаєте перевагу, ви можете використовувати розуміння:

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

1

Я віддаю перевагу len([b for b in boollist if b is True])(або еквівалент генераторного вираження), так як це цілком зрозуміло. Менш "чарівний", ніж відповідь, запропонована Ігнасіо Васкес-Абрамсом.

Крім того, ви можете зробити це, що все ще передбачає, що bool перетворюється на int, але не робить припущень щодо значення True: ntrue = sum(boollist) / int(True)


Ваше рішення має щонайменше дві проблеми. По-перше, він страждає від тієї ж проблеми надійності; що ви могли б виправити, просто змінивши тест на if b. Але, що ще важливіше, ви створюєте перекидний список, який вимагає, щоб усі значення були в пам'яті відразу, і ви не можете використовувати lenз виразом генератора. Краще уникати подібних практик, щоб рішення могло мати масштаб.
Нед Дейлі

@Ned Deily: if bабсолютно неправильно. Це було б правильно, якщо б питання стосувалося предметів, які оцінюються як True, а не справжні буліни True. Але я беру на вашу думку другий пункт. У цьому випадку є варіант sum(1 if b is True else 0 for b in boollist).
kampu

Як я зазначав в іншому місці, мені не зрозуміло з питання, чи дійсно ОП означає рахувати лише об'єкти типу bool зі значенням 1 або означає більший і загалом корисніший набір значень, які оцінюють істинні. Якщо перший, то тест на ідентичність - це правильний підхід, але він також обмежує. Об'єкти типу bool - все-таки досить незвичайні качки в Python, порівняно недавнє доповнення до мови. У будь-якому випадку я б пішов на простіше:sum(1 for b in boollist if b is True)
Ned Deily
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.