Hierarchical indexing (also called multi-indexing) allows you to set multiple columns as the index of a DataFrame, creating a tree-like structure of labels. This is powerful for organizing complex datasets, such as data grouped by region and state, or by year and quarter, enabling efficient slicing, aggregation, and restructuring.
This guide explains how to create, navigate, and manipulate hierarchical indexes in Pandas.
What Is a Hierarchical Index?
A standard DataFrame has a single-level index. A hierarchical index has two or more levels, where each level represents a different category:
Standard Index: Hierarchical Index:
Level 0 Level 1
Index Value Region State Value
0 100 East New York 100
1 200 East New Jersey 200
2 300 West California 300
3 400 West Oregon 400The outer level (Region) groups data, and the inner level (State) provides specifics within each group.
Creating a Hierarchical Index
Using set_index() with Multiple Columns
Pass a list of column names to set_index() to create a multi-level index:
import pandas as pd
df = pd.DataFrame({
'Region': ['East', 'East', 'West', 'West', 'South', 'South'],
'State': ['New York', 'New Jersey', 'California', 'Oregon', 'Texas', 'Florida'],
'Population': [19500000, 9300000, 39500000, 4200000, 29100000, 21500000],
'GDP': [1900, 650, 3600, 270, 1900, 1100]
})
print("Original DataFrame:")
print(df)
df_multi = df.set_index(['Region', 'State'])
print("\nWith Hierarchical Index:")
print(df_multi)Output:
Original DataFrame:
Region State Population GDP
0 East New York 19500000 1900
1 East New Jersey 9300000 650
2 West California 39500000 3600
3 West Oregon 4200000 270
4 South Texas 29100000 1900
5 South Florida 21500000 1100
With Hierarchical Index:
Population GDP
Region State
East New York 19500000 1900
New Jersey 9300000 650
West California 39500000 3600
Oregon 4200000 270
South Texas 29100000 1900
Florida 21500000 1100The Region column becomes level 0 and State becomes level 1 of the index.
Sorting the Hierarchical Index
For efficient slicing, always sort a multi-index after creating it:
df_multi = df_multi.sort_index()
print(df_multi)Output:
Population GDP
Region State
East New Jersey 9300000 650
New York 19500000 1900
South Florida 21500000 1100
Texas 29100000 1900
West California 39500000 3600
Oregon 4200000 270Selecting Data with a Hierarchical Index
Selecting by the Outer Level (Level 0)
Use loc[] with the outer level value to get all rows in that group:
east = df_multi.loc['East']
print(east)Output:
Population GDP
State
New Jersey 9300000 650
New York 19500000 1900The result drops the outer index level and uses the inner level (State) as the index.
Selecting Multiple Outer Level Values
Pass a list to select multiple groups:
east_west = df_multi.loc[['East', 'West']]
print(east_west)Output:
Population GDP
Region State
East New Jersey 9300000 650
New York 19500000 1900
West California 39500000 3600
Oregon 4200000 270Selecting by Both Levels Using Tuples
To select specific combinations of outer and inner index values, use tuples:
selected = df_multi.loc[('East', 'New York')]
print(selected)Output:
Population 19500000
GDP 1900
Name: (East, New York), dtype: int64For multiple specific combinations, pass a list of tuples:
selected = df_multi.loc[[('East', 'New York'), ('West', 'California')]]
print(selected)Output:
Region State
East New York 19500000 1900
West California 39500000 3600Common Mistake: Selecting by Inner Level Only
You cannot directly use loc[] with only the inner level value:
try:
result = df_multi.loc['New York']
except KeyError as e:
print(f"KeyError: {e}")Output:
KeyError: 'New York'The correct approaches:
result = df_multi.xs('New York', level='State')
print("Using xs():")
print(result)
result = df_multi.loc[('East', 'New York')]
print("\nUsing tuple:")
print(result)Output:
Using xs():
Population GDP
Region
East 19500000 1900
Using tuple:
Population 19500000
GDP 1900
Name: (East, New York), dtype: int64Using xs() for Cross-Section Selection
The xs() method is designed for selecting data at a specific level of a multi-index:
import pandas as pd
df = pd.DataFrame({
'Year': [2023, 2023, 2024, 2024],
'Quarter': ['Q1', 'Q2', 'Q1', 'Q2'],
'Revenue': [100, 150, 120, 180],
'Profit': [20, 35, 25, 45]
})
df_multi = df.set_index(['Year', 'Quarter']).sort_index()
print(df_multi)
q1_data = df_multi.xs('Q1', level='Quarter')
print("\nAll Q1 data:")
print(q1_data)Output:
Revenue Profit
Year Quarter
2023 Q1 100 20
Q2 150 35
2024 Q1 120 25
Q2 180 45
All Q1 data:
Revenue Profit
Year
2023 100 20
2024 120 25Resetting the Index
To convert the hierarchical index back to regular columns, use reset_index():
df_flat = df_multi.reset_index()
print(df_flat)Output:
Year Quarter Revenue Profit
0 2023 Q1 100 20
1 2023 Q2 150 35
2 2024 Q1 120 25
3 2024 Q2 180 45To reset only a specific level:
df_partial = df_multi.reset_index(level='Quarter')
print(df_partial)Output:
Quarter Revenue Profit
Year
2023 Q1 100 20
2023 Q2 150 35
2024 Q1 120 25
2024 Q2 180 45Aggregating with Hierarchical Indexes
Multi-indexes work naturally with groupby() for hierarchical aggregation:
import pandas as pd
df = pd.DataFrame({
'Region': ['East', 'East', 'West', 'West', 'East', 'West'],
'State': ['NY', 'NJ', 'CA', 'OR', 'NY', 'CA'],
'Sales': [100, 80, 200, 50, 120, 180]
})
summary = df.groupby(['Region', 'State'])['Sales'].agg(['sum', 'mean', 'count'])
print(summary)Output:
sum mean count
Region State
East NJ 80 80.0 1
NY 220 110.0 2
West CA 380 190.0 2
OR 50 50.0 1The result automatically has a hierarchical index.
Swapping Index Levels
Use swaplevel() to change the order of index levels:
import pandas as pd
df = pd.DataFrame({
'Region': ['East', 'East', 'West', 'West'],
'State': ['NY', 'NJ', 'CA', 'OR'],
'Population': [19500000, 9300000, 39500000, 4200000]
})
df_multi = df.set_index(['Region', 'State']).sort_index()
print("Original order:")
print(df_multi)
swapped = df_multi.swaplevel().sort_index()
print("\nSwapped levels:")
print(swapped)Output:
Original order:
Population
Region State
East NJ 9300000
NY 19500000
West CA 39500000
OR 4200000
Swapped levels:
Population
State Region
CA West 39500000
NJ East 9300000
NY East 19500000
OR West 4200000Quick Reference
| Operation | Code |
|---|---|
| Create hierarchical index | df.set_index(['col1', 'col2']) |
| Sort the multi-index | df.sort_index() |
| Select by outer level | df.loc['value'] |
| Select by both levels (tuple) | df.loc[('outer', 'inner')] |
| Select by inner level | df.xs('value', level='level_name') |
| Reset all levels to columns | df.reset_index() |
| Reset one level | df.reset_index(level='level_name') |
| Swap index levels | df.swaplevel() |
| Get index level names | df.index.names |
| Get index level values | df.index.get_level_values('level_name') |
Hierarchical indexing transforms flat DataFrames into structured, multi-dimensional data that mirrors real-world hierarchies. By mastering set_index(), loc[] with tuples, and xs(), you can efficiently organize, slice, and aggregate complex datasets in Pandas.