Pandas Melt-Then-Pivot: The Shape I Always Need

I keep reaching for melt-then-pivot to reshape wide tables for charting. Here is the pandas-style transform written in pure stdlib Python so it runs anywhere, plus the multi-key pivot variant.

Python
Compiler
3 snippets
py-collections
data-pipeline
code-template
carlosherrera

By @carlosherrera

May 10, 2026

·

Updated May 20, 2026

705 views

21

4.3 (14)

from __future__ import annotations

# A pandas-style 'melt' in 15 lines of stdlib Python.
# Wide input: each row is one entity, each non-id column is a measurement.
# Long output: one row per (entity, measurement_name, measurement_value).

def melt(rows, id_vars, value_vars=None):
    if value_vars is None:
        value_vars = [k for k in rows[0].keys() if k not in id_vars]
    out = []
    for row in rows:
        base = {k: row[k] for k in id_vars}
        for v in value_vars:
            out.append({**base, 'variable': v, 'value': row[v]})
    return out

wide = [
    {'user': 'alice', 'jan': 12, 'feb': 18, 'mar': 25},
    {'user': 'bob',   'jan':  3, 'feb':  9, 'mar': 14},
]

long = melt(wide, id_vars=['user'])
for r in long:
    print(r)

Melt is the 'unpivot' direction: every metric column becomes a row, with the original column name living in a variable field and its cell in value. I reach for it before charting, because most plotting libraries want one row per data point rather than a wide grid. The 15-line stdlib version is enough for any in-memory workload up to a few hundred thousand rows; past that I switch to pandas or polars. The contract matches pandas.melt(df, id_vars=...) exactly so when the dataset grows you can swap implementations without rewriting downstream code.