5

I have a Traffic Light Enum defining possible states:

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

I poll a Traffic Light to get current state every second and I put the values in a deque with this function:

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

I want to group sequences of the same state in order to learn traffic light phases timing.

I tried to use Counter class of collections, like this:

counter = collections.Counter(last_phases)

It groups very well different states, but I'm not able to know when next cycle starts. Is there a similar data structure like Counter that allows repetitions? so I can get results like:

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

instead of: Counter({ 'RED': 30, 'GREEN': 30, 'YELLOW': 9 })

logoff
  • 3,347
  • 5
  • 41
  • 58
  • use a list of lists or a list of tuples? – Patrick May 04 '18 at 14:13
  • What's your exact input for the expected output your posted? Also it's not very clear what does `read_phases()` do? please add the real code completely and explain where you defined `last_phases` and where you call `collections.Counter(last_phases)`? – Mazdak May 04 '18 at 14:22

2 Answers2

3

I would use itertools.groupby for this. It will group contiguous runs of the same element, then you can check the length for each run.

>>> 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)]
Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
0

collections.Counter will not work here as it does not differentiate items by order.

I recommend you use itertools.groupby.

But, for reference, below is a solution using 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)]
jpp
  • 159,742
  • 34
  • 281
  • 339