Python Pandas: How to Add a Level to a MultiIndex (Rows or Columns)
Learn how to add a new level to a Pandas MultiIndex for rows or columns using pd.concat() and pd.MultiIndex.from_product() with practical examples.
Pandas MultiIndex (also known as a hierarchical index) allows you to have multiple levels of indexing on your DataFrame's rows or columns, enabling more complex data organization and analysis. Sometimes, you may need to add a new level to an existing index or column structure.
This guide explains several methods to add a new level to a Pandas MultiIndex for both rows (index) and columns, primarily using pd.concat() and pd.MultiIndex.from_product().
Understanding MultiIndex and Levels
A MultiIndex can be thought of as an index with multiple "layers" or "levels." Each level can have its own name.
Row Index: If a DataFrame has a MultiIndex on its rows, each row is identified by a tuple of values, one from each level.
Column Index: Similarly, columns can have multiple levels, meaning each column header is a tuple of values.
Adding a level means introducing a new layer of grouping or categorization to your existing index or columns.
Original DataFrame (Simple Index): A B C0 1 10 1001 2 20 2002 3 30 3003 4 40 4004 5 50 500
Adding a Level to the DataFrame's Row Index
This adds an outer level to the existing row index.
Using pd.concat() with keys and names (Recommended)
The pd.concat() function can be used to add a new outermost level to the index by wrapping the DataFrame in a list and providing the keys and names arguments.
import pandas as pddf = pd.DataFrame({ 'A': [1, 2, 3, 4, 5], 'B': [10, 20, 30, 40, 50], 'C': [100, 200, 300, 400, 500]})new_outer_level_key = 'Experiment_X'new_outer_level_name = 'Experiment_Set'df_multi_index_rows = pd.concat( [df], # DataFrame(s) to concatenate, wrapped in a list keys=[new_outer_level_key], # Key(s) for the new outermost level names=[new_outer_level_name] # Name(s) for the new level(s))print("DataFrame with New Outer Row Index Level:")print(df_multi_index_rows)print()print("New index names:")print(df_multi_index_rows.index.names)
Output:
DataFrame with New Outer Row Index Level: A B CExperiment_Set Experiment_X 0 1 10 100 1 2 20 200 2 3 30 300 3 4 40 400 4 5 50 500New index names:['Experiment_Set', None]
[df]: You pass a list containing the DataFrame(s). If just one, it's [df].
keys=['X']: This provides the label(s) for the new outermost level. For each DataFrame in the input list, concat uses a corresponding key.
names=['First Level']: This assigns a name to the newly created level.
You can also pass a dictionary to pd.concat() where keys become the new outer index level:
Using pd.MultiIndex.from_frame() (More Manual Control)
This method gives you more control if you need to insert the new level at a specific position (not just outermost) or if the new level's values are more complex.
import pandas as pddf = pd.DataFrame({ 'A': [1, 2, 3, 4, 5], 'B': [10, 20, 30, 40, 50], 'C': [100, 200, 300, 400, 500]})index_df = df.index.to_frame(name='OriginalIndex') # Give the original index level a namenew_level_values = ['SetA'] * 2 + ['SetB'] * 3 # Example: different values for the new levelnew_level_name = 'New_Level'index_df.insert(0, new_level_name, new_level_values)print("DataFrame representing the new MultiIndex structure:")print(index_df)print()df.index = pd.MultiIndex.from_frame(index_df)print("DataFrame with New Index Level via from_frame():")print(df)
Output:
DataFrame representing the new MultiIndex structure: New_Level OriginalIndex0 SetA 01 SetA 12 SetB 23 SetB 34 SetB 4DataFrame with New Index Level via from_frame(): A B CNew_Level OriginalIndex SetA 0 1 10 100 1 2 20 200SetB 2 3 30 300 3 4 40 400 4 5 50 500
Adding a Level to the DataFrame's Column Index
This adds an outer level to the existing column headers.
Using pd.concat() with axis=1
Similar to adding a row index level, but you specify axis=1 to operate on columns.
DataFrame with New Outer Column Level:Category Metrics A B C0 1 10 1001 2 20 2002 3 30 3003 4 40 4004 5 50 500New column MultiIndex:MultiIndex([('Metrics', 'A'), ('Metrics', 'B'), ('Metrics', 'C')], names=['Category', None])
Using pd.MultiIndex.from_product()
This is very useful for creating a new column MultiIndex when the new level is the same for all existing columns, or when you want to create a Cartesian product of levels.
import pandas as pddf = pd.DataFrame({ 'A': [1, 2, 3, 4, 5], 'B': [10, 20, 30, 40, 50], 'C': [100, 200, 300, 400, 500]})new_outer_level = ['Measurements']df.columns = pd.MultiIndex.from_product( [new_outer_level, df.columns], # Iterables for the levels names=['Data_Type', 'Original_Metric'] # Optional names for the levels)print("DataFrame with New Column Level via from_product():")print(df)print()print("New column MultiIndex:")print(df.columns)
Output:
DataFrame with New Column Level via from_product():Data_Type Measurements Original_Metric A B C0 1 10 1001 2 20 2002 3 30 3003 4 40 4004 5 50 500New column MultiIndex:MultiIndex([('Measurements', 'A'), ('Measurements', 'B'), ('Measurements', 'C')], names=['Data_Type', 'Original_Metric'])
Conclusion
Adding levels to a Pandas MultiIndex allows for more sophisticated data structuring:
For Row Indexes:
pd.concat([df], keys=['NewOuterKey'], names=['NewLevelName']) is straightforward for adding a new outermost level.
Converting the index to a DataFrame (df.index.to_frame()), inserting the new level's data, and then converting back (pd.MultiIndex.from_frame()) offers more control over placement and values.
For Column Indexes:
pd.concat([df], keys=['NewOuterKey'], names=['NewLevelName'], axis=1) adds a new outermost column level.
pd.MultiIndex.from_product([['OuterLevelName'], df.columns]) is excellent for adding a uniform outer level to existing columns or creating new hierarchical structures.
Choose the method that best fits the complexity of the new level you're adding and whether you're targeting rows or columns.