Compute the mean value per category from parallel label and value lists. Each category accumulates a running total and a count; dividing at the end gives the mean. The trace shows totals and counts evolving key by key before the final means dict is assembled in one comprehension.

By hand

Walk cats and vals together with zip. Keep two dicts, totals and counts. On the first visit to a category seed both to 0, then add val to totals[cat] and increment counts[cat]. After the loop, divide total by count for each key to produce means.

naive.py
Replay: real traced execution (multi-file project)
cats = ['a', 'b', 'a', 'c', 'b', 'a', 'c', 'b']
vals = [2, 9, 4, 6, 3, 6, 10, 6]
totals = {}
counts = {}
for cat, val in zip(cats, vals):
    if cat not in totals:
        totals[cat] = 0
        counts[cat] = 0
    totals[cat] = totals[cat] + val
    counts[cat] = counts[cat] + 1
means = {k: totals[k] / counts[k] for k in sorted(totals)}
print('RESULT:', means)
  1. cats ← ['a', 'b', 'a', 'c', 'b', 'a', 'c', 'b']

    1cats = ['a', 'b', 'a', 'c', 'b', 'a', 'c', 'b']2vals = [2, 9, 4, 6, 3, 6, 10, 6]
    values this step['a', 'b', 'a', 'c', 'b', 'a', 'c', 'b']cats
  2. vals ← [2, 9, 4, 6, 3, 6, 10, 6]

    1cats = ['a', 'b', 'a', 'c', 'b', 'a', 'c', 'b']2vals = [2, 9, 4, 6, 3, 6, 10, 6]3totals = {}
    values this step[2, 9, 4, 6, 3, 6, 10, 6]vals
  3. totals ← {}

    2vals = [2, 9, 4, 6, 3, 6, 10, 6]3totals = {}4counts = {}
    values this step{}totals
  4. counts ← {}

    3totals = {}4counts = {}5for cat, val in zip(cats, vals):
    values this step{}counts
  5. cat ← 'a', val ← 2, totals ← {'a': 2}, counts ← {'a': 1}

    pass 1 of 2
    4counts = {}5for cat, val in zip(cats, vals):6    if cat not in totals:7        totals[cat] = 08        counts[cat] = 09    totals[cat] = totals[cat] + val10    counts[cat] = counts[cat] + 111means = {k: totals[k] / counts[k] for k in sorted(totals)}
    values this step'a'cat2val{'a': 0} {'a': 2}totals{'a': 0} {'a': 1}counts
  6. cat ← 'b', val ← 9, totals ← {'a': 2, 'b': 9}, counts ← {'a': 1, 'b': 1}

    pass 2 of 2
    4counts = {}5for cat, val in zip(cats, vals):6    if cat not in totals:7        totals[cat] = 08        counts[cat] = 09    totals[cat] = totals[cat] + val10    counts[cat] = counts[cat] + 111means = {k: totals[k] / counts[k] for k in sorted(totals)}
    values this step'a' 'b'cat2 9val{'a': 2, 'b': 0} {'a': 2, 'b': 9}totals{'a': 1, 'b': 0} {'a': 1, 'b': 1}counts
  7. cat ← 'a', val ← 4

    4counts = {}5for cat, val in zip(cats, vals):6    if cat not in totals:
    values this step'b' 'a'cat9 4val
  8. if cat not in totals:

    5for cat, val in zip(cats, vals):6    if cat not in totals:7        totals[cat] = 0
  9. totals ← {'a': 6, 'b': 9}

    8    counts[cat] = 09totals[cat] = totals[cat] + val10counts[cat] = counts[cat] + 1
    values this step{'a': 2, 'b': 9} {'a': 6, 'b': 9}totals
  10. counts ← {'a': 2, 'b': 1}

    9    totals[cat] = totals[cat] + val10    counts[cat] = counts[cat] + 111means = {k: totals[k] / counts[k] for k in sorted(totals)}
    values this step{'a': 1, 'b': 1} {'a': 2, 'b': 1}counts
  11. cat ← 'c', val ← 6

    4counts = {}5for cat, val in zip(cats, vals):6    if cat not in totals:
    values this step'a' 'c'cat4 6val
  12. if cat not in totals:

    5for cat, val in zip(cats, vals):6    if cat not in totals:7        totals[cat] = 0
  13. totals ← {'a': 6, 'b': 9, 'c': 0}

    6if cat not in totals:7    totals[cat] = 08    counts[cat] = 0
    values this step{'a': 6, 'b': 9} {'a': 6, 'b': 9, 'c': 0}totals
  14. counts ← {'a': 2, 'b': 1, 'c': 0}

    7    totals[cat] = 08    counts[cat] = 09totals[cat] = totals[cat] + val
    values this step{'a': 2, 'b': 1} {'a': 2, 'b': 1, 'c': 0}counts
  15. totals ← {'a': 6, 'b': 9, 'c': 6}, counts ← {'a': 2, 'b': 1, 'c': 1}

    pass 1 of 4
    4counts = {}5for cat, val in zip(cats, vals):6    if cat not in totals:7        totals[cat] = 08        counts[cat] = 09    totals[cat] = totals[cat] + val10    counts[cat] = counts[cat] + 111means = {k: totals[k] / counts[k] for k in sorted(totals)}
    values this step{'a': 6, 'b': 9, 'c': 0} {'a': 6, 'b': 9, 'c': 6}totals{'a': 2, 'b': 1, 'c': 0} {'a': 2, 'b': 1, 'c': 1}counts'c' 'b'cat6 3val
    All 4 passes — pass 1 is the card above
    passtotalscountscatval
    1{'a': 6, 'b': 9, 'c': 0} {'a': 6, 'b': 9, 'c': 6}{'a': 2, 'b': 1, 'c': 0} {'a': 2, 'b': 1, 'c': 1}'c' 'b'6 3
    2{'a': 6, 'b': 9, 'c': 6} {'a': 6, 'b': 12, 'c': 6}{'a': 2, 'b': 1, 'c': 1} {'a': 2, 'b': 2, 'c': 1}'b' 'a'3 6
    3{'a': 6, 'b': 12, 'c': 6} {'a': 12, 'b': 12, 'c': 6}{'a': 2, 'b': 2, 'c': 1} {'a': 3, 'b': 2, 'c': 1}'a' 'c'6 10
    4{'a': 12, 'b': 12, 'c': 6} {'a': 12, 'b': 12, 'c': 16}{'a': 3, 'b': 2, 'c': 1} {'a': 3, 'b': 2, 'c': 2}'c' 'b'10 6
  16. totals ← {'a': 12, 'b': 18, 'c': 16}

    8    counts[cat] = 09totals[cat] = totals[cat] + val10counts[cat] = counts[cat] + 1
    values this step{'a': 12, 'b': 12, 'c': 16} {'a': 12, 'b': 18, 'c': 16}totals
  17. counts ← {'a': 3, 'b': 3, 'c': 2}

    9    totals[cat] = totals[cat] + val10    counts[cat] = counts[cat] + 111means = {k: totals[k] / counts[k] for k in sorted(totals)}
    values this step{'a': 3, 'b': 2, 'c': 2} {'a': 3, 'b': 3, 'c': 2}counts
  18. for cat, val in zip(cats, vals):

    4counts = {}5for cat, val in zip(cats, vals):6    if cat not in totals:
  19. means ← {'a': 4.0, 'b': 6.0, 'c': 8.0}

    10    counts[cat] = counts[cat] + 111means = {k: totals[k] / counts[k] for k in sorted(totals)}12print('RESULT:', means)
    values this step{'a': 4.0, 'b': 6.0, 'c': 8.0}means
  20. stdout ← RESULT: {'a': 4.0, 'b': 6.0, 'c': 8.0}

    11means = {k: totals[k] / counts[k] for k in sorted(totals)}12print('RESULT:', means)
    values this stepRESULT: {'a': 4.0, 'b': 6.0, 'c': 8.0}stdout

The Pythonic way

One pass with defaultdict(list) collects every value into its group list. A dict comprehension over sorted keys calls statistics.mean on each group, with float() to normalise the return type.

library.py
import statistics
from collections import defaultdict
cats = ['a', 'b', 'a', 'c', 'b', 'a', 'c', 'b']
vals = [2, 9, 4, 6, 3, 6, 10, 6]
groups = defaultdict(list)
for cat, val in zip(cats, vals):
    groups[cat].append(val)
means = {k: float(statistics.mean(groups[k])) for k in sorted(groups)}
print('RESULT:', means)
RESULT: {'a': 4.0, 'b': 6.0, 'c': 8.0}

Implementation notes

  • statistics.mean returns int (not float) when the mean divides evenly for an all-integer list; wrapping with float() normalises to float so the result type is consistent with the naive version.
  • For the same aggregation as a pandas one-liner, see the python-pandas track lesson groupby-mean, which uses df.groupby('cat')['val'].mean() — mechanism here, API there.
  • means is built via {k: ... for k in sorted(totals)} so keys appear in alphabetical order regardless of insertion order.