Reshaping by Hand
Pivot Long to Wide
Six (key, attr, val) triples in long format folded into a nested dict-of-dicts
in wide format. The trace shows table growing one inner key at a time as
each attribute is set under its parent key.
By hand
For each triple, seed an empty inner dict for k if it has not been seen yet,
then set table[k][a] = v. The if k not in table guard prevents overwriting
an inner dict that already holds earlier attributes.
naive.py
Replay: real traced execution (multi-file project)
keys = ['p', 'p', 'q', 'q', 'r', 'r']
attrs = ['x', 'y', 'x', 'y', 'x', 'y']
vals = [1, 2, 3, 4, 5, 6]
table = {}
for k, a, v in zip(keys, attrs, vals):
if k not in table:
table[k] = {}
table[k][a] = v
print('RESULT:', {k: table[k] for k in sorted(table)})
keys ← ['p', 'p', 'q', 'q', 'r', 'r']
1keys = ['p', 'p', 'q', 'q', 'r', 'r']2attrs = ['x', 'y', 'x', 'y', 'x', 'y']values this step['p', 'p', 'q', 'q', 'r', 'r']keysattrs ← ['x', 'y', 'x', 'y', 'x', 'y']
1keys = ['p', 'p', 'q', 'q', 'r', 'r']2attrs = ['x', 'y', 'x', 'y', 'x', 'y']3vals = [1, 2, 3, 4, 5, 6]values this step['x', 'y', 'x', 'y', 'x', 'y']attrsvals ← [1, 2, 3, 4, 5, 6]
2attrs = ['x', 'y', 'x', 'y', 'x', 'y']3vals = [1, 2, 3, 4, 5, 6]4table = {}values this step[1, 2, 3, 4, 5, 6]valstable ← {}
3vals = [1, 2, 3, 4, 5, 6]4table = {}5for k, a, v in zip(keys, attrs, vals):values this step{}tablea ← 'x', k ← 'p', v ← 1
4table = {}5for k, a, v in zip(keys, attrs, vals):6 if k not in table:values this step'x'a'p'k1vif k not in table:
5for k, a, v in zip(keys, attrs, vals):6 if k not in table:7 table[k] = {}table ← {'p': {}}
6if k not in table:7 table[k] = {}8table[k][a] = vvalues this step{} → {'p': {}}tabletable ← {'p': {'x': 1}}
7 table[k] = {}8 table[k][a] = v9print('RESULT:', {k: table[k] for k in sorted(table)})values this step{'p': {}} → {'p': {'x': 1}}tablea ← 'y', v ← 2
4table = {}5for k, a, v in zip(keys, attrs, vals):6 if k not in table:values this step'x' → 'y'a1 → 2vif k not in table:
5for k, a, v in zip(keys, attrs, vals):6 if k not in table:7 table[k] = {}table ← {'p': {'x': 1, 'y': 2}}
7 table[k] = {}8 table[k][a] = v9print('RESULT:', {k: table[k] for k in sorted(table)})values this step{'p': {'x': 1}} → {'p': {'x': 1, 'y': 2}}tablea ← 'x', k ← 'q', v ← 3
4table = {}5for k, a, v in zip(keys, attrs, vals):6 if k not in table:values this step'y' → 'x'a'p' → 'q'k2 → 3vif k not in table:
5for k, a, v in zip(keys, attrs, vals):6 if k not in table:7 table[k] = {}table ← {'p': {'x': 1, 'y': 2}, 'q': {}}
6if k not in table:7 table[k] = {}8table[k][a] = vvalues this step{'p': {'x': 1, 'y': 2}} → {'p': {'x': 1, 'y': 2}, 'q': {}}tabletable ← {'p': {'x': 1, 'y': 2}, 'q': {'x': 3}}
7 table[k] = {}8 table[k][a] = v9print('RESULT:', {k: table[k] for k in sorted(table)})values this step{'p': {'x': 1, 'y': 2}, 'q': {}} → {'p': {'x': 1, 'y': 2}, 'q': {'x': 3}}tablea ← 'y', v ← 4
4table = {}5for k, a, v in zip(keys, attrs, vals):6 if k not in table:values this step'x' → 'y'a3 → 4vif k not in table:
5for k, a, v in zip(keys, attrs, vals):6 if k not in table:7 table[k] = {}table ← {'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}}
7 table[k] = {}8 table[k][a] = v9print('RESULT:', {k: table[k] for k in sorted(table)})values this step{'p': {'x': 1, 'y': 2}, 'q': {'x': 3}} → {'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}}tablea ← 'x', k ← 'r', v ← 5
4table = {}5for k, a, v in zip(keys, attrs, vals):6 if k not in table:values this step'y' → 'x'a'q' → 'r'k4 → 5vif k not in table:
5for k, a, v in zip(keys, attrs, vals):6 if k not in table:7 table[k] = {}table ← {'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}, 'r': {}}
6if k not in table:7 table[k] = {}8table[k][a] = vvalues this step{'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}} → {'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}, 'r': {}}tabletable ← {'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}, 'r': {'x': 5}}
7 table[k] = {}8 table[k][a] = v9print('RESULT:', {k: table[k] for k in sorted(table)})values this step{'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}, 'r': {}} → {'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}, 'r': {'x': 5}}tablea ← 'y', v ← 6
4table = {}5for k, a, v in zip(keys, attrs, vals):6 if k not in table:values this step'x' → 'y'a5 → 6vif k not in table:
5for k, a, v in zip(keys, attrs, vals):6 if k not in table:7 table[k] = {}table ← {'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}, 'r': {'x': 5, 'y': 6}}
7 table[k] = {}8 table[k][a] = v9print('RESULT:', {k: table[k] for k in sorted(table)})values this step{'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}, 'r': {'x': 5}} → {'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}, 'r': {'x': 5, 'y': 6}}tablefor k, a, v in zip(keys, attrs, vals):
4table = {}5for k, a, v in zip(keys, attrs, vals):6 if k not in table:stdout ← RESULT: {'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}, 'r': {'x': 5, 'y': 6}}
8 table[k][a] = v9print('RESULT:', {k: table[k] for k in sorted(table)})values this stepRESULT: {'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}, 'r': {'x': 5, 'y': 6}}stdout
The Pythonic way
dict.setdefault(k, {}) returns the existing inner dict for k when present,
or inserts an empty dict and returns it. Chaining [a] = v onto the return
value collapses the seed-or-get guard into a single line per iteration.
library.py
keys = ['p', 'p', 'q', 'q', 'r', 'r']
attrs = ['x', 'y', 'x', 'y', 'x', 'y']
vals = [1, 2, 3, 4, 5, 6]
table = {}
for k, a, v in zip(keys, attrs, vals):
table.setdefault(k, {})[a] = v
print('RESULT:', {k: table[k] for k in sorted(table)})
RESULT: {'p': {'x': 1, 'y': 2}, 'q': {'x': 3, 'y': 4}, 'r': {'x': 5, 'y': 6}}
Implementation notes
- Long format stores one observation per row (three columns: key, attribute, value). Wide format stores all attributes for a key on one row — one column per attribute. Pivoting trades row count for column count.
- The loop may interleave rows for different keys freely. The seed-or-get
pattern handles this because
setdefaultis idempotent: calling it twice for the same key leaves the existing inner dict unchanged. - In pandas,
df.pivot(index='key', columns='attr', values='val')performs this transformation in one call. The pandas pivot-table lesson (roadmap) extends this to aggregated values when multiple rows share the same (key, attr) pair.