Python подсчет элементов в итерируемых с фильтром - PullRequest
0 голосов
/ 21 февраля 2019

Для подсчета элементов в списке вы можете использовать collections.Counter, но что, если нужно подсчитать только некоторые элементы?

Я настроил этот пример(обратите внимание: numpy просто для удобства. В общем, список будет содержать произвольные объекты Python):

num_samples = 10000000
num_unique = 1000
numbers = np.random.randint(0, num_unique, num_samples)

Я хотел бы посчитать, как часто число встречается в этом списке, но меня интересует тольков цифрах <= 10. </p>

Это базовый уровень для победы.Счетчик просто считает все, что должно привести к дополнительным затратам.

%%time
counter = Counter(numbers)

CPU times: user 1.38 s, sys: 7.49 ms, total: 1.39 s
Wall time: 1.39 s

Фильтрация повторяемого кода при подсчете кажется невозможной.Но следующий код очень плохой стиль, он проходит по списку дважды, вместо использования одного цикла:

%%time
numbers = [number for number in numbers if number<=10]
counter = Counter(numbers)

CPU times: user 1.3 s, sys: 22.1 ms, total: 1.32 s
Wall time: 1.33 s

Это ускорение в основном незначительно.Давайте попробуем один цикл:

%%time

counter = defaultdict(int)
for number in numbers:
    if number > 10:
        continue
    counter[number]+=1

CPU times: user 1.99 s, sys: 11.5 ms, total: 2 s
Wall time: 2.01 s

Ну, мой единственный цикл намного хуже.Я предполагаю, что Counter получает прибыль от реализации на основе C?

Следующее, что я попробовал, было переключение моего выражения списка на выражение генератора.В принципе это должно означать, что генератор проходит только один раз, пока он потребляется счетчиком.Числа разочаровывают, хотя, в основном, это так же быстро, как ванильный счетчик:

%%time
iterator = (number for number in numbers if number <= 10)
counter = Counter(iterator)

CPU times: user 1.38 s, sys: 8.51 ms, total: 1.39 s
Wall time: 1.39 s

В этот момент я сделал шаг назад и перезапустил цифры несколько раз.Три версии счетчика (нефильтрованные, составление списка, выражение генератора) почти одинаковы по скорости.Версия defaultdict значительно медленнее.

Как эффективно подсчитывать элементы в списке Python, одновременно фильтруя элементы?

1 Ответ

0 голосов
/ 21 февраля 2019

Если речь идет о больших числовых массивах, вам лучше воспользоваться векторизованными числовыми операциями.

%%time
np.unique(numbers[numbers <= 10], return_counts=True)

Вывод:

Wall time: 31.2 ms

(array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10]),
 array([10055, 10090,  9941, 10002,  9994,  9989, 10070,  9859, 10038,
        10028,  9965], dtype=int64))

Для сравнения, мой собственный расчет времени вашегокод дал чуть выше, чем ваш.

...