100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Python

Pivot Tables

How pandas' pivot_table reshapes long-format data into a summarized cross-tabulation, aggregating values across row and column groupings similar to a spreadsheet pivot table.

Combining & Reshaping DataIntermediate10 min readJul 8, 2026
Analogies

Pivot Tables

A pivot table reshapes data from a long, row-per-observation format into a wide, summarized grid — one axis becomes row categories, another becomes column categories, and the cells hold an aggregated value computed from all matching observations. pandas.pivot_table replicates the spreadsheet pivot-table workflow familiar from Excel, but with the full power of pandas' aggregation functions and multi-level grouping behind it. It is functionally close to a groupby followed by an unstack, but pivot_table packages that combination into a single, more readable call with sensible defaults for missing combinations.

🏏

Cricket analogy: A pivot table reshapes ball-by-ball data into a grid of batsman-vs-bowler with average runs in each cell, the way a coach's whiteboard summarizes a whole series at a glance instead of scrolling through every delivery.

The core parameters: index, columns, values, aggfunc

pd.pivot_table(df, index=..., columns=..., values=..., aggfunc=...) is the standard signature. index specifies which column(s) become row labels, columns specifies which column(s) become column labels, values specifies which column(s) get aggregated, and aggfunc specifies how (defaulting to 'mean'). Any of index, columns, or values can accept a list for multi-level grouping, producing a MultiIndex on the corresponding axis. Passing a list of functions to aggfunc computes multiple summary statistics per cell in one call.

🏏

Cricket analogy: pd.pivot_table(df, index='batsman', columns='format', values='runs', aggfunc='mean') gives each batsman's average runs per format; passing a list like ['mean','max'] to aggfunc shows both average and best innings in one cell per player.

python
import pandas as pd

sales = pd.DataFrame({
    'region': ['North', 'North', 'South', 'South', 'North', 'South'],
    'product': ['Widget', 'Gadget', 'Widget', 'Gadget', 'Widget', 'Widget'],
    'quarter': ['Q1', 'Q1', 'Q1', 'Q1', 'Q2', 'Q2'],
    'revenue': [1000, 1500, 900, 1200, 1100, 950]
})

table = pd.pivot_table(
    sales, index='region', columns='product', values='revenue',
    aggfunc='sum', fill_value=0
)
print(table)
# product  Gadget  Widget
# region
# North      1500    2100
# South      1200    1850

Handling missing combinations and adding totals

Not every combination of row and column category will necessarily exist in the source data — pivot_table fills those empty cells with NaN by default, which fill_value can override (commonly with 0 for count-like aggregations). Setting margins=True adds an 'All' row and column containing the overall aggregate across each row and column, which is convenient for quickly seeing grand totals alongside the breakdown, with margins_name letting you rename the 'All' label if needed.

🏏

Cricket analogy: Not every batsman faced every bowler in a tournament, so pivot_table fills unplayed matchups with NaN by default, or 0 if fill_value is set for a wickets-count grid; margins=True adds an 'All' row/column showing tournament-wide totals.

pivot_table versus pivot versus groupby

DataFrame.pivot (no _table) reshapes data similarly but requires the index/columns combination to be unique — it has no aggfunc and will raise an error if duplicate combinations exist, making it purely a reshaping tool rather than a summarizing one. pivot_table is effectively pivot plus aggregation, tolerant of duplicate combinations because it aggregates them. Compared to groupby, pivot_table produces the same underlying computation but presents the columns dimension as an actual axis in the output (wide format) rather than as an additional row-grouping key (long format from groupby().unstack()).

🏏

Cricket analogy: DataFrame.pivot reshapes a scorecard only if there's exactly one entry per batsman-per-over combination, erroring on duplicates like two entries for the same batsman in the same over, whereas pivot_table would just average or sum them instead.

pivot_table is implemented internally using groupby and unstack, so anything you can do with pivot_table can also be expressed with groupby(...).agg(...).unstack() — pivot_table is simply the more concise, purpose-built interface for this specific reshaping pattern.

Because pivot_table's default aggfunc is 'mean', forgetting to specify aggfunc='sum' (or another appropriate function) for count- or total-oriented analysis is a common mistake that silently produces averages instead of the totals the analyst actually wanted.

  • pivot_table reshapes long-format data into a summarized grid using index, columns, values, and aggfunc parameters.
  • The default aggfunc is 'mean' — always set it explicitly (e.g. 'sum', 'count') if a different summary is intended.
  • fill_value replaces NaN for row/column combinations that don't exist in the source data.
  • margins=True adds 'All' row/column totals for a quick grand-summary view.
  • DataFrame.pivot (no aggregation) requires unique index/columns combinations; pivot_table tolerates and aggregates duplicates.
  • pivot_table is conceptually equivalent to groupby(...).agg(...).unstack(), just packaged as a single convenient call.

Practice what you learned

Was this page helpful?

Topics covered

#Python#PandasNumPyStudyNotes#DataScience#PivotTables#Pivot#Tables#Core#Parameters#StudyNotes#SkillVeris