Счетчик разрешающих повторений

У меня есть светофор Enum, определяющий возможные состояния:

class TrafficLightPhase(Enum):
    RED = "RED"
    YELLOW = "YELLOW"
    GREEN = "GREEN"

Я опрашиваю светофор, чтобы узнать текущее состояние каждую секунду, и помещаю значения в deque с этой функцией:

def read_phases():
    while running:
        current_phase = get_current_phase_phases()
        last_phases.append(current_phase)
        time.sleep(1)

Я хочу сгруппировать последовательности одного и того же состояния, чтобы узнать время фаз светофора.

Я пытался использовать Counter класс collections, как это:

counter = collections.Counter(last_phases)

Он очень хорошо группирует разные состояния, но я не знаю, когда начнется следующий цикл. Есть ли подобная структура данных, как Counter что позволяет повторений? так что я могу получить результаты, такие как:

Counter({
         'RED': 10,
         'GREEN': 10, 
         'YELLOW': 3,
         'RED': 10,
         'GREEN': 10, 
         'YELLOW': 3,
         'RED': 10,
         'GREEN': 10, 
         'YELLOW': 3
        })

вместо: Счетчик ({"КРАСНЫЙ": 30, "ЗЕЛЕНЫЙ": 30, "ЖЕЛТЫЙ": 9 })

2 ответа

Решение

Я хотел бы использовать itertools.groupby за это. Он сгруппирует смежные серии одного и того же элемента, после чего вы сможете проверить длину каждого цикла.

>>> from itertools import groupby
>>> last_phases= ['red', 'red', 'yellow', 'red', 'red', 'green']
>>> [(key, len(list(group))) for key,group in groupby(last_phases)]
[('red', 2), ('yellow', 1), ('red', 2), ('green', 1)]

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

Я рекомендую вам использовать itertools.groupby,

Но, для справки, ниже приведено решение, использующее collections.defaultdict,

from collections import defaultdict
from itertools import islice, chain, zip_longest

d = defaultdict(lambda: defaultdict(int))

last_phases= ['red', 'red', 'yellow', 'red', 'red', 'green']

c = 0
for i, j in zip_longest(last_phases, islice(last_phases, 1, None)):
    d[c][i] += 1
    if i != j:
        c += 1

res = list(chain.from_iterable(i.items() for i in d.values()))

[('red', 2), ('yellow', 1), ('red', 2), ('green', 1)]
Другие вопросы по тегам