Grouping
Group Mean
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)
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']catsvals ← [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]valstotals ← {}
2vals = [2, 9, 4, 6, 3, 6, 10, 6]3totals = {}4counts = {}values this step{}totalscounts ← {}
3totals = {}4counts = {}5for cat, val in zip(cats, vals):values this step{}countscat ← 'a', val ← 2, totals ← {'a': 2}, counts ← {'a': 1}
pass 1 of 24counts = {}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}countscat ← 'b', val ← 9, totals ← {'a': 2, 'b': 9}, counts ← {'a': 1, 'b': 1}
pass 2 of 24counts = {}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}countscat ← 'a', val ← 4
4counts = {}5for cat, val in zip(cats, vals):6 if cat not in totals:values this step'b' → 'a'cat9 → 4valif cat not in totals:
5for cat, val in zip(cats, vals):6 if cat not in totals:7 totals[cat] = 0totals ← {'a': 6, 'b': 9}
8 counts[cat] = 09totals[cat] = totals[cat] + val10counts[cat] = counts[cat] + 1values this step{'a': 2, 'b': 9} → {'a': 6, 'b': 9}totalscounts ← {'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}countscat ← 'c', val ← 6
4counts = {}5for cat, val in zip(cats, vals):6 if cat not in totals:values this step'a' → 'c'cat4 → 6valif cat not in totals:
5for cat, val in zip(cats, vals):6 if cat not in totals:7 totals[cat] = 0totals ← {'a': 6, 'b': 9, 'c': 0}
6if cat not in totals:7 totals[cat] = 08 counts[cat] = 0values this step{'a': 6, 'b': 9} → {'a': 6, 'b': 9, 'c': 0}totalscounts ← {'a': 2, 'b': 1, 'c': 0}
7 totals[cat] = 08 counts[cat] = 09totals[cat] = totals[cat] + valvalues this step{'a': 2, 'b': 1} → {'a': 2, 'b': 1, 'c': 0}countstotals ← {'a': 6, 'b': 9, 'c': 6}, counts ← {'a': 2, 'b': 1, 'c': 1}
pass 1 of 44counts = {}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 → 3valAll 4 passes — pass 1 is the card above pass totalscountscatval1 {'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 totals ← {'a': 12, 'b': 18, 'c': 16}
8 counts[cat] = 09totals[cat] = totals[cat] + val10counts[cat] = counts[cat] + 1values this step{'a': 12, 'b': 12, 'c': 16} → {'a': 12, 'b': 18, 'c': 16}totalscounts ← {'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}countsfor cat, val in zip(cats, vals):
4counts = {}5for cat, val in zip(cats, vals):6 if cat not in totals: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}meansstdout ← 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.meanreturnsint(notfloat) when the mean divides evenly for an all-integer list; wrapping withfloat()normalises tofloatso the result type is consistent with the naive version.- For the same aggregation as a pandas one-liner, see the
python-pandastrack lessongroupby-mean, which usesdf.groupby('cat')['val'].mean()— mechanism here, API there. meansis built via{k: ... for k in sorted(totals)}so keys appear in alphabetical order regardless of insertion order.