pandas
MultiIndex
data manipulation
Python programming
data analysis

Prepend a level to a pandas MultiIndex

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

To prepend a new level to a pandas MultiIndex, use pd.MultiIndex.from_arrays() combining the new level with the existing index levels, or use pd.concat() with a keys parameter that adds an outer level. The most straightforward approach is pd.concat({key: df}, names=['new_level']) which wraps the DataFrame with an additional index level. For column MultiIndex, assign a new pd.MultiIndex constructed from the existing columns plus the new level.

Prepend a Row Index Level

Using pd.concat with keys

python
1import pandas as pd
2
3df = pd.DataFrame(
4    {"value": [10, 20, 30]},
5    index=pd.Index(["a", "b", "c"], name="letter")
6)
7
8# Prepend a level using concat
9result = pd.concat({"group1": df}, names=["group"])
10print(result)
11#               value
12# group  letter
13# group1 a         10
14#        b         20
15#        c         30

pd.concat({key: df}) adds key as the outermost index level. The names parameter names the new level.

Using pd.MultiIndex.from_arrays

python
1import pandas as pd
2
3df = pd.DataFrame(
4    {"value": [10, 20, 30]},
5    index=pd.Index(["a", "b", "c"], name="letter")
6)
7
8# Build a new MultiIndex with prepended level
9new_level = ["X"] * len(df)  # Same value for all rows
10new_index = pd.MultiIndex.from_arrays(
11    [new_level, df.index],
12    names=["category", df.index.name]
13)
14df.index = new_index
15
16print(df)
17#                 value
18# category letter
19# X        a         10
20#          b         20
21#          c         30

Using set_index with a New Column

python
1import pandas as pd
2
3df = pd.DataFrame(
4    {"letter": ["a", "b", "c"], "value": [10, 20, 30]}
5).set_index("letter")
6
7# Add a column and set it as the first index level
8df["group"] = "group1"
9df = df.set_index("group", append=True).swaplevel()
10
11print(df)
12#               value
13# group  letter
14# group1 a         10
15#        b         20
16#        c         30

append=True adds the new column as an additional index level. swaplevel() moves it to the front.

Prepend to an Existing MultiIndex

python
1import pandas as pd
2import numpy as np
3
4# DataFrame with existing MultiIndex
5arrays = [["A", "A", "B", "B"], [1, 2, 1, 2]]
6index = pd.MultiIndex.from_arrays(arrays, names=["group", "id"])
7df = pd.DataFrame({"value": [10, 20, 30, 40]}, index=index)
8
9print(df)
10#          value
11# group id
12# A     1     10
13#       2     20
14# B     1     30
15#       2     40
16
17# Prepend a new level
18new_level = ["2025"] * len(df)
19new_index = pd.MultiIndex.from_arrays(
20    [new_level] + [df.index.get_level_values(i) for i in range(df.index.nlevels)],
21    names=["year"] + list(df.index.names)
22)
23df.index = new_index
24
25print(df)
26#               value
27# year group id
28# 2025 A     1     10
29#            2     20
30#      B     1     30
31#            2     40

Prepend a Column Index Level

python
1import pandas as pd
2
3df = pd.DataFrame({
4    "price": [10, 20, 30],
5    "quantity": [100, 200, 300]
6})
7
8# Prepend a level to column index
9new_columns = pd.MultiIndex.from_product(
10    [["2025"], df.columns],
11    names=["year", "metric"]
12)
13df.columns = new_columns
14
15print(df)
16# year       2025
17# metric    price quantity
18# 0            10      100
19# 1            20      200
20# 2            30      300

Using pd.concat for Column Levels

python
1import pandas as pd
2
3df = pd.DataFrame({
4    "price": [10, 20],
5    "quantity": [100, 200]
6})
7
8# Prepend column level using concat with axis=1
9result = pd.concat({"store_A": df}, axis=1)
10print(result)
11#   store_A
12#     price quantity
13# 0      10      100
14# 1      20      200

Multiple DataFrames with Different Outer Levels

python
1import pandas as pd
2
3df_2024 = pd.DataFrame({"sales": [100, 200]}, index=["Q1", "Q2"])
4df_2025 = pd.DataFrame({"sales": [150, 250]}, index=["Q1", "Q2"])
5
6# Concat creates outer level from dict keys
7combined = pd.concat(
8    {"2024": df_2024, "2025": df_2025},
9    names=["year", "quarter"]
10)
11print(combined)
12#                sales
13# year quarter
14# 2024 Q1         100
15#      Q2         200
16# 2025 Q1         150
17#      Q2         250

Reusable Function

python
1import pandas as pd
2
3def prepend_index_level(df, values, level_name="new_level"):
4    """Prepend a new level to a DataFrame's row index."""
5    if isinstance(values, str):
6        values = [values] * len(df)
7
8    arrays = [values] + [
9        df.index.get_level_values(i) for i in range(df.index.nlevels)
10    ]
11    names = [level_name] + list(df.index.names)
12    df = df.copy()
13    df.index = pd.MultiIndex.from_arrays(arrays, names=names)
14    return df
15
16# Usage
17df = pd.DataFrame({"val": [1, 2, 3]}, index=["a", "b", "c"])
18result = prepend_index_level(df, "group1", "group")
19print(result)
20#          val
21# group
22# group1 a   1
23#        b   2
24#        c   3

Common Pitfalls

  • Index name collisions: If the new level name matches an existing level name, pandas creates duplicate level names. This causes ambiguous behavior in xs(), loc[], and groupby(). Always use unique level names.
  • Forgetting names in pd.concat: pd.concat({key: df}) creates an unnamed outer level (name=None). Pass names=["level_name"] to give it a meaningful name.
  • Modifying index in place: df.index = new_index modifies the DataFrame directly. If you need the original, use df.copy() first or assign to a new variable.
  • Length mismatch: When using from_arrays, all arrays must have the same length as the DataFrame. A mismatch raises ValueError: All arrays must be of the same length.
  • swaplevel only swaps two levels: swaplevel() swaps the two innermost levels by default. For a 3+ level MultiIndex, specify the levels explicitly: df.swaplevel(0, 2) to move level 2 to position 0.

Summary

  • Use pd.concat({key: df}, names=["level"]) for the simplest way to prepend a row index level
  • Use pd.MultiIndex.from_arrays() for full control over the new index structure
  • Use set_index("col", append=True).swaplevel() when the new level comes from a column
  • For column MultiIndex, use pd.concat({key: df}, axis=1) or construct a new pd.MultiIndex
  • Always name your levels to avoid None level names
  • Use get_level_values(i) to extract existing levels when building a new MultiIndex

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.