pandas
DataFrame
indexing
Python
duplicate-post

start index at 1 for Pandas DataFrame

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

Pandas uses zero-based integer indexes by default, but you can relabel the index so it starts at 1. The key thing to remember is that the pandas index is a set of labels, not a change to positional behavior, so .loc and .iloc continue to mean different things after the relabeling.

Set A 1-Based Index Directly

If you already have a DataFrame and want labels 1, 2, 3, ..., assign a new index:

python
1import pandas as pd
2
3df = pd.DataFrame({
4    "Name": ["Alice", "Bob", "Charlie"],
5    "Age": [25, 30, 35]
6})
7
8df.index = range(1, len(df) + 1)
9print(df)

This is the simplest answer for an existing DataFrame.

You can also use an explicit RangeIndex:

python
df.index = pd.RangeIndex(start=1, stop=len(df) + 1, step=1)

Both approaches produce the same visible effect.

You Can Also Set It At Creation Time

If you already know the desired labels when building the DataFrame, pass them in immediately:

python
1df = pd.DataFrame(
2    {"Name": ["Alice", "Bob", "Charlie"], "Age": [25, 30, 35]},
3    index=range(1, 4)
4)
5
6print(df)

This avoids an extra reassignment later and makes the label convention explicit from the start.

After Filtering, Reset Then Shift

After filtering or concatenation, indexes often contain gaps or duplicates. If you want a fresh 1-based index after those operations, reset first and then shift.

python
1filtered = df[df["Age"] > 25]
2
3filtered = filtered.reset_index(drop=True)
4filtered.index = filtered.index + 1
5
6print(filtered)

That pattern is useful when the index is primarily for display or export rather than for preserving original row identity.

.loc And .iloc Still Mean Different Things

Changing the DataFrame index to start at 1 does not change how positional access works.

python
1df.index = range(1, len(df) + 1)
2
3print(df.loc[1])   # label 1
4print(df.iloc[0])  # first row by position

After the change, df.loc[0] will usually fail because the label 0 no longer exists. This is the most important behavioral consequence of using a 1-based index in pandas.

Sometimes A Display Column Is Better Than Reindexing

If you only want row numbers for reports, exports, or notebook output, adding a normal column is often safer than changing the actual index.

python
1import pandas as pd
2
3df = pd.DataFrame({
4    "Name": ["Alice", "Bob", "Charlie"],
5    "Age": [25, 30, 35]
6})
7
8df.insert(0, "Row", range(1, len(df) + 1))
9print(df)

This keeps the default pandas index behavior intact while still showing a human-friendly 1-based row number. It is a good choice when later code depends on zero-based positional access or when you do not want index labels to carry business meaning.

Use A Real Column When It Has Meaning

If the data already includes a natural 1-based row number, it may be better to use that column as the index instead of generating one artificially.

python
1df = pd.DataFrame({
2    "RowNum": [1, 2, 3],
3    "Name": ["Alice", "Bob", "Charlie"]
4})
5
6df = df.set_index("RowNum")
7print(df)

That is often more meaningful than reassigning labels after the fact, especially when the row numbering comes from an external system.

Common Pitfalls

  • Forgetting that range(1, len(df)) stops too early and misses the last row.
  • Changing the index labels and then still trying to access rows with .loc[0].
  • Forgetting to reset the index after filtering, which leaves gaps in the labels.
  • Reindexing the DataFrame when a plain display column would have been simpler and less error-prone.
  • Using a 1-based display index when the original index carried important identity information.
  • Confusing label changes with positional changes in pandas.

Summary

  • Set df.index = range(1, len(df) + 1) to start the index at 1.
  • Use RangeIndex or provide the index during DataFrame creation if you want the same effect more explicitly.
  • After filtering, reset_index(drop=True) and then shift by 1 for a clean 1-based label sequence.
  • Remember that .loc uses labels while .iloc still uses zero-based positions.
  • Consider adding a numbered column instead of changing the index when the goal is presentation.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the 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.