Data Analysis with pandas · Analysis with pandas

Reshaping: Pivot Tables, Melt and Crosstab

Turn long data into wide summary tables with pivot_table, back into long format with melt, and count combinations with crosstab.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

A country-by-product revenue matrix (like a spreadsheet pivot table) with totals, the same data back in "long" format for plotting or databases, and a count of orders per country and product.

The idea

  • Long format: one row per observation (country, product, value). Wide format: one row per entity, one column per category.
  • pivot_table(index, columns, values, aggfunc, fill_value, margins) aggregates into wide format.
  • melt(id_vars, var_name, value_name) converts wide back to long.
  • pd.crosstab(a, b) counts combinations.

The code

Pivot, melt and crosstabTry it yourself
from io import StringIO
import pandas as pd

csv = StringIO("""order_id,date,customer,country,product,quantity,unit_price
1001,2026-01-03,ana,DE,mug,2,8.50
1002,2026-01-03,ben,US,teapot,1,24.00
1003,2026-01-04,chen,DE,kettle,1,31.90
1004,2026-01-05,ana,DE,teapot,1,24.00
1005,2026-01-07,dara,FR,mug,4,8.50
1006,2026-01-07,ben,US,mug,1,8.50
""")
orders = pd.read_csv(csv, parse_dates=["date"])

orders["total"] = orders["quantity"] * orders["unit_price"]

matrix = orders.pivot_table(index="country", columns="product", values="total",
                            aggfunc="sum", fill_value=0, margins=True, margins_name="all")
print(matrix)

long = matrix.drop(index="all", columns="all").reset_index().melt(
    id_vars="country", var_name="product", value_name="revenue")
print(long[long["revenue"] > 0].sort_values(["country", "product"]).to_string(index=False))

print(pd.crosstab(orders["country"], orders["product"]))

Output

product  kettle   mug  teapot    all
country                             
DE         31.9  17.0    24.0   72.9
FR          0.0  34.0     0.0   34.0
US          0.0   8.5    24.0   32.5
all        31.9  59.5    48.0  139.4
country product  revenue
     DE  kettle     31.9
     DE     mug     17.0
     DE  teapot     24.0
     FR     mug     34.0
     US     mug      8.5
     US  teapot     24.0
product  kettle  mug  teapot
country                     
DE            1    1       1
FR            0    1       0
US            0    1       1

Common mistakes

  • pivot (no aggregation) on data with duplicate index/column pairs, which raises an error; use pivot_table.
  • Forgetting fill_value=0, leaving NaN for missing combinations.
  • Storing wide tables in databases; long format is easier to query and append to.

Key points

  • pivot_table aggregates long data into wide summaries.
  • melt converts wide data back into long format.
  • crosstab counts combinations of two columns.

Further reading: Reshaping and pivot tables

Practice questions

What's the difference between pivot and pivot_table?

Answer

pivot only reshapes and fails on duplicate index/column pairs; pivot_table aggregates duplicates with aggfunc.

What does melt do?

Answer

It converts a wide table into long format: identifier columns stay, other columns become variable/value pairs.

What does margins=True add to a pivot table?

Answer

Row and column totals (an 'All' row and column, renamable with margins_name).

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?