Reshaping
Stack and Unstack
Stack converts a wide DataFrame into a long Series by folding column labels into the row index, producing a MultiIndex. Unstack is the exact inverse — it lifts one index level back out to become column labels again.
By hand
Iterate over sorted(grid) (row keys), then sorted(grid[row]) (column
keys), appending (row, col, value) tuples into stacked. This is the
manual equivalent of stack — flattening a 2-D grid into a labelled sequence.
naive.py
Replay: real traced execution (multi-file project)
grid = {'a': {'x': 1, 'y': 2}, 'b': {'x': 3, 'y': 4}}
stacked = []
for row in sorted(grid):
for col in sorted(grid[row]):
stacked.append((row, col, grid[row][col]))
print('RESULT:', stacked)
grid ← {'a': {'x': 1, 'y': 2}, 'b': {'x': 3, 'y': 4}}
1grid = {'a': {'x': 1, 'y': 2}, 'b': {'x': 3, 'y': 4}}2stacked = []values this step{'a': {'x': 1, 'y': 2}, 'b': {'x': 3, 'y': 4}}gridstacked ← []
1grid = {'a': {'x': 1, 'y': 2}, 'b': {'x': 3, 'y': 4}}2stacked = []3for row in sorted(grid):values this step[]stackedrow ← 'a'
2stacked = []3for row in sorted(grid):4 for col in sorted(grid[row]):values this step'a'rowcol ← 'x'
3for row in sorted(grid):4 for col in sorted(grid[row]):5 stacked.append((row, col, grid[row][col]))values this step'x'colstacked ← [('a', 'x', 1)]
4 for col in sorted(grid[row]):5 stacked.append((row, col, grid[row][col]))6print('RESULT:', stacked)values this step[] → [('a', 'x', 1)]stackedcol ← 'y'
3for row in sorted(grid):4 for col in sorted(grid[row]):5 stacked.append((row, col, grid[row][col]))values this step'x' → 'y'colstacked ← [('a', 'x', 1), ('a', 'y', 2)]
4 for col in sorted(grid[row]):5 stacked.append((row, col, grid[row][col]))6print('RESULT:', stacked)values this step[('a', 'x', 1)] → [('a', 'x', 1), ('a', 'y', 2)]stackedfor col in sorted(grid[row]):
3for row in sorted(grid):4 for col in sorted(grid[row]):5 stacked.append((row, col, grid[row][col]))row ← 'b'
2stacked = []3for row in sorted(grid):4 for col in sorted(grid[row]):values this step'a' → 'b'rowcol ← 'x'
3for row in sorted(grid):4 for col in sorted(grid[row]):5 stacked.append((row, col, grid[row][col]))values this step'y' → 'x'colstacked ← [('a', 'x', 1), ('a', 'y', 2), ('b', 'x', 3)]
4 for col in sorted(grid[row]):5 stacked.append((row, col, grid[row][col]))6print('RESULT:', stacked)values this step[('a', 'x', 1), ('a', 'y', 2)] → [('a', 'x', 1), ('a', 'y', 2), ('b', 'x', 3)]stackedcol ← 'y'
3for row in sorted(grid):4 for col in sorted(grid[row]):5 stacked.append((row, col, grid[row][col]))values this step'x' → 'y'colstacked ← [('a', 'x', 1), ('a', 'y', 2), ('b', 'x', 3), ('b', 'y', 4)]
4 for col in sorted(grid[row]):5 stacked.append((row, col, grid[row][col]))6print('RESULT:', stacked)values this step[('a', 'x', 1), ('a', 'y', 2), ('b', 'x', 3)] → [('a', 'x', 1), ('a', 'y', 2), ('b', 'x', 3), ('b', 'y', 4)]stackedfor col in sorted(grid[row]):
3for row in sorted(grid):4 for col in sorted(grid[row]):5 stacked.append((row, col, grid[row][col]))for row in sorted(grid):
2stacked = []3for row in sorted(grid):4 for col in sorted(grid[row]):stdout ← RESULT: [('a', 'x', 1), ('a', 'y', 2), ('b', 'x', 3), ('b', 'y', 4)]
5 stacked.append((row, col, grid[row][col]))6print('RESULT:', stacked)values this stepRESULT: [('a', 'x', 1), ('a', 'y', 2), ('b', 'x', 3), ('b', 'y', 4)]stdout
With pandas
df.stack() folds the column labels into a new inner index level, returning
a Series with a two-level MultiIndex. stacked.unstack() reverses this,
restoring the original wide DataFrame.
library.py
import pandas as pd
from dalib.display import set_display
set_display()
df = pd.DataFrame({'x': [1, 3], 'y': [2, 4]}, index=['a', 'b'])
stacked = df.stack()
unstacked = stacked.unstack()
pairs = [(idx[0], idx[1], int(v)) for idx, v in stacked.items()]
print('df index:', df.index.tolist())
print('df columns:', df.columns.tolist())
print('stacked index:', stacked.index.tolist())
print('stacked values:', stacked.tolist())
print('unstacked columns:', unstacked.columns.tolist())
print('RESULT:', sorted(pairs))
df index: ['a', 'b']
df columns: ['x', 'y']
stacked index: [('a', 'x'), ('a', 'y'), ('b', 'x'), ('b', 'y')]
stacked values: [1, 2, 3, 4]
unstacked columns: ['x', 'y']
RESULT: [('a', 'x', 1), ('a', 'y', 2), ('b', 'x', 3), ('b', 'y', 4)]
Implementation notes
stack()moves the innermost column level into the innermost row index level. On a single-level column index this produces a two-level MultiIndex(original_row, col_label).unstack()(no args) reverses the laststack(). Pass a level number or name to control which level to un-pivot back to columns.- By default
stack()drops individualNaNentries from the resulting Series (dropna=True). Passdropna=Falseto keep them. - Cross-reference:
melt-wide-to-long(this chapter) for a similar wide-to- long transformation usingpd.melt;pivot-table-simplefor the reverse.