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 replay shows table growing one inner key at a time as
each attribute is set under its parent key.
By hand
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.
naive.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):
if k not in table:
table[k] = {}
table[k][a] = v
print('RESULT:', {k: table[k] for k in sorted(table)})
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.