Pandas reset_index(): Restore Labels and Prepare Reports

Use reset_index() to restore row labels as columns or discard labels that no longer matter. Preserve business identifiers and prepare grouped results for export.

Create the student table 🔝

Resetting an index changes row labels. It does not sort rows, remove duplicate records or create a permanent business identifier.

import pandas as pd

def student_data():
    return pd.DataFrame({
        "NAME": ["Ravi", "Raju", "Alex", "Ron", "King", "Jack"],
        "ID": [1, 2, 3, 4, 5, 6],
        "MATH": [80, 40, 70, 70, 70, 30],
        "ENGLISH": [80, 70, 40, 50, 60, 30]
    })

df = student_data()
print(df)

Expected output

   NAME  ID  MATH  ENGLISH
0  Ravi   1    80       80
1  Raju   2    40       70
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
5  Jack   6    30       30

Choose reset_index() options 🔝

level chooses which index levels to remove. drop=False preserves removed labels as columns; drop=True discards them. inplace=False returns a new table. names sets the inserted column names. col_level and col_fill control insertion into hierarchical columns. allow_duplicates=True permits duplicate column labels; resolving the naming conflict usually makes subsequent selection clearer.

Reset the default index 🔝

Even an existing sequential index can become a column. Keep it only when those old labels have a purpose.

df = student_data()
reset = df.reset_index()
print(reset)
assert reset["index"].tolist() == list(range(6))
assert "index" not in df.columns

Expected output

   index  NAME  ID  MATH  ENGLISH
0      0  Ravi   1    80       80
1      1  Raju   2    40       70
2      2  Alex   3    70       40
3      3   Ron   4    70       50
4      4  King   5    70       60
5      5  Jack   6    30       30

Restore a named index with drop=False 🔝

Use set_index() to create the labels, then reset_index() to restore them as columns. The named index becomes a column with that name.

indexed = student_data().set_index("NAME")
restored = indexed.reset_index(drop=False)
print(restored)
assert restored["NAME"].tolist() == student_data()["NAME"].tolist()
assert restored.equals(student_data())

Expected output

   NAME  ID  MATH  ENGLISH
0  Ravi   1    80       80
1  Raju   2    40       70
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
5  Jack   6    30       30

Discard old labels with drop=True 🔝

drop=True is useful for row labels that no longer matter after filtering. It discards an identifier stored only in the index, so use it deliberately.

indexed = student_data().set_index("NAME")
discarded = indexed.reset_index(drop=True)
print(discarded)
assert "NAME" not in discarded.columns
assert discarded.index.tolist() == list(range(6))

Expected output

   ID  MATH  ENGLISH
0   1    80       80
1   2    40       70
2   3    70       40
3   4    70       50
4   5    70       60
5   6    30       30

Assign the returned table 🔝

inplace=False leaves the original unchanged. A method call whose result is ignored does not reset the source.

indexed = student_data().set_index("ID")
indexed.reset_index(inplace=False)
print("Source index:", indexed.index.tolist())
result = indexed.reset_index(inplace=False)
print(result)
assert indexed.index.name == "ID"
assert "ID" in result.columns

Expected output

Source index: [1, 2, 3, 4, 5, 6]
   ID  NAME  MATH  ENGLISH
0   1  Ravi    80       80
1   2  Raju    40       70
2   3  Alex    70       40
3   4   Ron    70       50
4   5  King    70       60
5   6  Jack    30       30

Reset a copy in place 🔝

The keyword is inplace. The return value is None, so do not assign the method result back to the table.

working = student_data().set_index("NAME")
returned = working.reset_index(inplace=True)
print(working)
print("Return:", returned)
assert returned is None
assert "NAME" in working.columns

Expected output

   NAME  ID  MATH  ENGLISH
0  Ravi   1    80       80
1  Raju   2    40       70
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
5  Jack   6    30       30
Return: None

Add sequential display numbers 🔝

The sequence here starts at 100. It describes the current row order and can change after filtering or sorting; it is not a persistent record key. rename() changes the inserted column name.

df = student_data().reset_index()
df = df.rename(columns={"index": "New_ID"})
df["New_ID"] = df.index + 100
print(df)
assert df["New_ID"].tolist() == [100, 101, 102, 103, 104, 105]

Expected output

   New_ID  NAME  ID  MATH  ENGLISH
0     100  Ravi   1    80       80
1     101  Raju   2    40       70
2     102  Alex   3    70       40
3     103   Ron   4    70       50
4     104  King   5    70       60
5     105  Jack   6    30       30

Reset labels after selecting rows 🔝

Keep ID as a normal column and discard only the old positional labels. Filtering does not automatically renumber the remaining rows. Continue with query() and loc.

df = student_data()
selected = df.loc[df["MATH"].ge(70)]
print("Before:", selected.index.tolist())
tidy = selected.reset_index(drop=True)
print(tidy)
assert selected.index.tolist() == [0, 2, 3, 4]
assert tidy.index.tolist() == [0, 1, 2, 3]
assert tidy["ID"].tolist() == [1, 3, 4, 5]

Expected output

Before: [0, 2, 3, 4]
   NAME  ID  MATH  ENGLISH
0  Ravi   1    80       80
1  Alex   3    70       40
2   Ron   4    70       50
3  King   5    70       60

Give the restored index an explicit name 🔝

names controls the inserted column label without changing its values. It is useful for preserving source row labels with a descriptive name.

indexed = student_data().set_index("ID")
restored = indexed.reset_index(names="student_id")
print(restored)
assert restored["student_id"].tolist() == [1, 2, 3, 4, 5, 6]

Expected output

   student_id  NAME  MATH  ENGLISH
0           1  Ravi    80       80
1           2  Raju    40       70
2           3  Alex    70       40
3           4   Ron    70       50
4           5  King    70       60
5           6  Jack    30       30

Reset every level of a MultiIndex 🔝

All removed levels become columns unless drop=True. A list supplied to names has one entry per index level.

multi = student_data().set_index(["NAME", "ID"])
restored = multi.reset_index(names=["student_name", "student_id"])
print(restored)
assert restored.columns.tolist() == ["student_name", "student_id", "MATH", "ENGLISH"]
assert restored.index.nlevels == 1

Expected output

  student_name  student_id  MATH  ENGLISH
0         Ravi           1    80       80
1         Raju           2    40       70
2         Alex           3    70       40
3          Ron           4    70       50
4         King           5    70       60
5         Jack           6    30       30

Reset only one index level 🔝

Resetting one level leaves the other levels as the index. This does not necessarily create a sequential RangeIndex.

multi = student_data().set_index(["NAME", "ID"])
partial = multi.reset_index(level="ID")
print(partial)
print("Remaining index name:", partial.index.name)
assert partial.index.name == "NAME"
assert "ID" in partial.columns
assert partial.loc["Raju", "ID"] == 2

Expected output

      ID  MATH  ENGLISH
NAME                   
Ravi   1    80       80
Raju   2    40       70
Alex   3    70       40
Ron    4    70       50
King   5    70       60
Jack   6    30       30
Remaining index name: NAME

Use col_level and col_fill with hierarchical columns 🔝

These options affect the column hierarchy, not which row-index levels are reset. Here the restored student label is inserted into column level 1 and labelled key in level 0.

scores = pd.DataFrame(
    [[80, 81], [40, 70]],
    index=pd.Index(["Ravi", "Raju"], name="student"),
    columns=pd.MultiIndex.from_tuples([("score", "math"), ("score", "english")])
)
restored = scores.reset_index(col_level=1, col_fill="key")
print(restored)
print("Column labels:", restored.columns.tolist())
assert ("key", "student") in restored.columns
assert restored[("key", "student")].tolist() == ["Ravi", "Raju"]

Expected output

      key score        
  student  math english
0    Ravi    80      81
1    Raju    40      70
Column labels: [('key', 'student'), ('score', 'math'), ('score', 'english')]

Resolve a column-name collision 🔝

If the index name already exists as a column, resetting can fail. Rename the inserted column with names. If an index duplicates an existing column exactly, dropping that index may also be appropriate after checking the values.

indexed = student_data().set_index("ID", drop=False)
try:
    indexed.reset_index()
except ValueError as error:
    print(type(error).__name__, str(error))
safe = indexed.reset_index(names="index_ID")
print(safe)
assert safe.columns.is_unique
assert safe["index_ID"].equals(safe["ID"])

Expected output

ValueError cannot insert ID, already exists
   index_ID  NAME  ID  MATH  ENGLISH
0         1  Ravi   1    80       80
1         2  Raju   2    40       70
2         3  Alex   3    70       40
3         4   Ron   4    70       50
4         5  King   5    70       60
5         6  Jack   6    30       30

Avoid accumulating index columns 🔝

Repeated reset_index() calls can add index and level_0 columns, then encounter a naming conflict. If the labels are disposable, reset with drop=True instead.

df = student_data()
once = df.reset_index()
twice = once.reset_index()
print("Once:", once.columns.tolist())
print("Twice:", twice.columns.tolist())
clean = df.reset_index(drop=True).reset_index(drop=True)
print("Disposable labels:", clean.columns.tolist())
assert "level_0" in twice.columns
assert clean.columns.tolist() == df.columns.tolist()

Expected output

Once: ['index', 'NAME', 'ID', 'MATH', 'ENGLISH']
Twice: ['level_0', 'index', 'NAME', 'ID', 'MATH', 'ENGLISH']
Disposable labels: ['NAME', 'ID', 'MATH', 'ENGLISH']

Restore a date index after selecting a month 🔝

Keep dates as a column for a tabular report. See to_datetime(), date_range(), period_range() and date exercises.

daily = pd.DataFrame({
    "date": pd.to_datetime(["2026-09-01", "2026-09-03", "2026-10-01"]),
    "amount": [20, 35, 40]
}).set_index("date").sort_index()
september = daily.loc["2026-09"].reset_index()
print(september)
assert "date" in september.columns
assert september["amount"].sum() == 55

Expected output

        date  amount
0 2026-09-01      20
1 2026-09-03      35

Practical example: make a grouped sales report 🔝

groupby() commonly places group labels in the result index. Reset them into columns before passing a report to an export or merge operation.

sales = pd.DataFrame({
    "region": ["North", "North", "South", "South", "North"],
    "product": ["Pen", "Book", "Pen", "Book", "Pen"],
    "amount": [5, 20, 8, 15, 7]
})
grouped = sales.groupby(["region", "product"]).agg(
    orders=("amount", "size"), revenue=("amount", "sum")
)
report = grouped.reset_index()
print(report)
assert report.columns.tolist() == ["region", "product", "orders", "revenue"]
assert report["orders"].sum() == len(sales)
assert report["revenue"].sum() == sales["amount"].sum() == 55

Expected output

  region product  orders  revenue
0  North    Book       1       20
1  North     Pen       2       12
2  South    Book       1       15
3  South     Pen       1        8

Export the group labels once 🔝

After resetting, index=False avoids an extra row-number column. Alternatively, group with as_index=False when you want group labels as columns immediately. See Input and Output and to_excel().

direct = sales.groupby(["region", "product"], as_index=False).agg(
    orders=("amount", "size"), revenue=("amount", "sum")
)
print(report.to_csv(index=False).rstrip())
print("Same report:", report.equals(direct))
assert report.equals(direct)
# Optional Colab export:
# report.to_csv("regional_product_sales.csv", index=False)

Expected output

region,product,orders,revenue
North,Book,1,20
North,Pen,2,12
South,Book,1,15
South,Pen,1,8
Same report: True

Common questions 🔝

Does reset_index() delete duplicates? No; use drop_duplicates() under a defined rule. Does it sort rows? No. Why did an identifier disappear? drop=True discarded index labels. Why is the return value None? inplace=True updates the table. Why is the index still named after a partial reset? Some MultiIndex levels remain. Can I allow duplicate column names? allow_duplicates=True permits them, but resolving the conflict keeps column selection clearer. Will this fix misaligned arithmetic? Arithmetic aligns on labels; decide how records correspond before changing them. Continue with merge(), iloc and Data Cleaning.

Exercise: preserve IDs after selecting students 🔝

Set ID as the index, select students with Maths marks below 50 and restore ID as a column. Predict the IDs, names and resulting index labels.

Exercise solution 🔝

Raju and Jack have IDs 2 and 6. The restored table has index labels 0 and 1 while retaining those IDs.

indexed = student_data().set_index("ID")
answer = indexed.loc[indexed["MATH"].lt(50)].reset_index()
print(answer)
assert answer["ID"].tolist() == [2, 6]
assert answer["NAME"].tolist() == ["Raju", "Jack"]
assert answer.index.tolist() == [0, 1]

Expected output

   ID  NAME  MATH  ENGLISH
0   2  Raju    40       70
1   6  Jack    30       30

Practice in Google Colab 🔝

Open in Google Colab View on GitHub
Run the examples in order, change filters and grouping keys, then inspect restored labels and exported reports. Save your own copy to keep edits. All sample tables are included in the notebook.

Continue with the Data Cleaning hub, where(), loc, at, NumPy where(), fillna() and iloc.

Reference: Pandas DataFrame.reset_index documentation.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer