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

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.

naive.py
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)
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 last stack(). Pass a level number or name to control which level to un-pivot back to columns.
  • By default stack() drops individual NaN entries from the resulting Series (dropna=True). Pass dropna=False to keep them.
  • Cross-reference: melt-wide-to-long (this chapter) for a similar wide-to- long transformation using pd.melt; pivot-table-simple for the reverse.