pandas
python
dataframe
data-analysis
indexing

Selecting a row of pandas series/dataframe by integer index

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In pandas, integer position and index label are two different concepts. If you want the row at position 2, the usual tool is .iloc. If you want the row whose index label equals 2, the tool is .loc. Mixing those two ideas is one of the most common pandas indexing mistakes.

Use .iloc for Integer Position

.iloc always interprets integers as zero-based positions.

python
1import pandas as pd
2
3students = pd.DataFrame(
4    {
5        "name": ["Ada", "Ben", "Cara", "Drew"],
6        "score": [91, 84, 95, 88],
7        "city": ["Toronto", "Seattle", "Boston", "Austin"],
8    }
9)
10
11print(students.iloc[2])
12print()
13print(students.iloc[[0, 3]])
14print()
15print(students.iloc[1:4])

A single position returns a Series. A list of positions or a slice returns a DataFrame.

.loc Uses Labels, Even When Labels Are Integers

This is where people get caught. .loc uses index labels, not positions.

python
1import pandas as pd
2
3students = pd.DataFrame(
4    {
5        "name": ["Ada", "Ben", "Cara", "Drew"],
6        "score": [91, 84, 95, 88],
7    },
8    index=[10, 20, 30, 40],
9)
10
11print(students.loc[20])
12print()
13print(students.iloc[1])

These happen to point at the same row only because the label 20 is currently at position 1. After sorting or resetting the index, that coincidence can disappear immediately.

The Same Rule Applies to Series

Series objects follow the same distinction.

python
1import pandas as pd
2
3s = pd.Series([100, 200, 300], index=[5, 6, 7])
4
5print(s.iloc[1])
6print(s.loc[6])

Again, the first uses position and the second uses label. Writing the access method explicitly makes the code clearer and safer.

Use .iat for a Single Scalar

If you need exactly one cell by row and column position, .iat is the most direct choice.

python
1import pandas as pd
2
3students = pd.DataFrame(
4    {
5        "name": ["Ada", "Ben", "Cara"],
6        "score": [91, 84, 95],
7    }
8)
9
10value = students.iat[1, 0]
11print(value)

This is often a better fit than selecting a row first and then indexing again.

Write Defensive Position-Based Code

If the requested row position may be out of range, wrap the access so the failure mode is explicit.

python
1import pandas as pd
2
3
4def row_at(df: pd.DataFrame, pos: int):
5    if pos < 0 or pos >= len(df):
6        return None
7    return df.iloc[pos]
8
9students = pd.DataFrame({"name": ["Ada", "Ben"]})
10print(row_at(students, 1))
11print(row_at(students, 5))

This is useful in notebooks, ETL code, or exploratory scripts where an invalid position should not crash the whole workflow.

Select Rows and Columns Together

.iloc can also select by row and column position in one step.

python
1import pandas as pd
2
3students = pd.DataFrame(
4    {
5        "name": ["Ada", "Ben", "Cara", "Drew"],
6        "score": [91, 84, 95, 88],
7        "city": ["Toronto", "Seattle", "Boston", "Austin"],
8    }
9)
10
11subset = students.iloc[1:3, 0:2]
12print(subset)

This selects rows at positions 1 and 2, and columns at positions 0 and 1.

Be Careful After Sorting or Resetting the Index

Position-based code depends on row order, so it can silently change meaning after operations such as sort_values, filtering, or reset_index.

python
1import pandas as pd
2
3students = pd.DataFrame(
4    {"name": ["Ada", "Ben", "Cara"], "score": [91, 84, 95]}
5)
6
7sorted_students = students.sort_values("score", ascending=False)
8print(sorted_students.iloc[0])

This now returns the highest-scoring row, not necessarily the row that originally appeared first. When row identity matters, a stable key column is often safer than positional selection.

Common Pitfalls

A common mistake is using .loc when you actually mean position. This bug is especially easy to miss when the index itself contains integers.

Another is forgetting that .iloc[1:3] follows normal Python slicing rules, so the stop bound is excluded.

Developers also sometimes expect .iloc[2] to return a one-row DataFrame. It returns a Series. If you need a DataFrame, use .iloc[[2]] instead.

Summary

  • Use .iloc when integers represent row positions.
  • Use .loc when values represent index labels.
  • Use .iat for a single scalar by row and column position.
  • Remember that .iloc slices exclude the stop bound.
  • Use .iloc[[n]] when you need a one-row DataFrame rather than a Series.

Course illustration
Course illustration

All Rights Reserved.