df.iloc[row_positions, column_positions] selects data by zero-based integer position. It is useful for inspecting imported files, taking slices and editing known positions. Use square brackets, not iloc(). A row label that looks like an integer is still a label: iloc always follows the current order.
Start with the student dataset to learn row and column selection, then inspect a sales extract and practise updating values. Each example includes its expected output so you can check your results.
Watch the video for a demonstration of positional selection, then try the examples below with Boolean arrays and callable selectors.
Show Table of ContentsThe four columns are NAME, ID, MATH and ENGLISH, at positions 0, 1, 2 and 3. Row positions run from 0 through 5. Use this small dataset to predict each selection. The larger student dataset provides more practice after these examples.
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": [30, 40, 50, 60, 70, 80],
"ENGLISH": [20, 30, 40, 50, 60, 70]
})
df = student_data()
print(df)Expected output
NAME ID MATH ENGLISH
0 Ravi 1 30 20
1 Raju 2 40 30
2 Alex 3 50 40
3 Ron 4 60 50
4 King 5 70 60
5 Jack 6 80 70Use square brackets: iloc is an indexer, not a function call. A single integer selects one row and returns a Series. Its name is the original row label. Position 1 means the second row, regardless of the index labels.
df = student_data()
print("First row:")
print(df.iloc[0])
print("Second row:")
print(df.iloc[1])Expected output
First row:
NAME Ravi
ID 1
MATH 30
ENGLISH 20
Name: 0, dtype: object
Second row:
NAME Raju
ID 2
MATH 40
ENGLISH 30
Name: 1, dtype: objectA list of positions preserves the two-dimensional DataFrame shape. This matters when a later operation expects column names and a table, rather than a Series. Compare iloc[0] and iloc[[0]] before exporting or joining a selection.
df = student_data()
print(df.iloc[[0]])
print(type(df.iloc[0]).__name__)
print(type(df.iloc[[0]]).__name__)Expected output
NAME ID MATH ENGLISH
0 Ravi 1 30 20
Series
DataFrameThe first coordinate is the row, the second is the column. iloc[1, 2] reads Raju's MATH score, 40. Keep track of the column order; moving a column changes its position.
df = student_data()
print(df.iloc[1, 2])
print(df.iloc[1, 3])
assert df.iloc[1, 2] == 40Expected output
40
30Negative positions count backwards from the end. -1 is the last row; -2 is the second-last. A slice such as -2: returns a DataFrame. For a simple preview you can also use tail().
df = student_data()
print("Last row:")
print(df.iloc[-1])
print("Last MATH score:")
print(df.iloc[-1, 2])
print("Second-last row:")
print(df.iloc[-2])
print("Last two rows:")
print(df.iloc[-2:])Expected output
Last row:
NAME Jack
ID 6
MATH 80
ENGLISH 70
Name: 5, dtype: object
Last MATH score:
80
Second-last row:
NAME King
ID 5
MATH 70
ENGLISH 60
Name: 4, dtype: object
Last two rows:
NAME ID MATH ENGLISH
4 King 5 70 60
5 Jack 6 80 70iloc[2:5] returns positions 2, 3 and 4: Alex, Ron and King. The stop position 5 is excluded. Leaving the start blank begins at 0. Leaving the stop blank continues to the end. Compare head() for a quick first-rows preview.
df = student_data()
print("First two rows:")
print(df.iloc[:2])
print("Positions 2 through 4:")
print(df.iloc[2:5])
print("All except the last two:")
print(df.iloc[:-2])Expected output
First two rows:
NAME ID MATH ENGLISH
0 Ravi 1 30 20
1 Raju 2 40 30
Positions 2 through 4:
NAME ID MATH ENGLISH
2 Alex 3 50 40
3 Ron 4 60 50
4 King 5 70 60
All except the last two:
NAME ID MATH ENGLISH
0 Ravi 1 30 20
1 Raju 2 40 30
2 Alex 3 50 40
3 Ron 4 60 50A list selects exactly the positions you request, in the requested order. It can repeat a position. Use this for a small inspection sample; it is not a random sample and may miss problems elsewhere in the dataset.
df = student_data()
print("First and last:")
print(df.iloc[[0, -1]])
print("A reordered sample:")
print(df.iloc[[4, 0, 2]])Expected output
First and last:
NAME ID MATH ENGLISH
0 Ravi 1 30 20
5 Jack 6 80 70
A reordered sample:
NAME ID MATH ENGLISH
4 King 5 70 60
0 Ravi 1 30 20
2 Alex 3 50 40A colon selects every position on that axis. df.iloc[:, 2] returns the MATH column as a Series. A list of column positions keeps a DataFrame. Column positions are zero-based, just like row positions.
df = student_data()
print("All rows and columns:")
print(df.iloc[::])
assert df.iloc[::].equals(df.iloc[:, :])
print("MATH as a Series:")
print(df.iloc[:, 2])
print("Two rows, NAME and MATH:")
print(df.iloc[:2, [0, 2]])Expected output
All rows and columns:
NAME ID MATH ENGLISH
0 Ravi 1 30 20
1 Raju 2 40 30
2 Alex 3 50 40
3 Ron 4 60 50
4 King 5 70 60
5 Jack 6 80 70
MATH as a Series:
0 30
1 40
2 50
3 60
4 70
5 80
Name: MATH, dtype: int64
Two rows, NAME and MATH:
NAME MATH
0 Ravi 30
1 Raju 40The third slice component is the step. ::2 selects positions 0, 2 and 4; ::3 selects 0 and 3. A negative step reverses the rows. This samples by current position, not by an ID value or a calendar interval.
df = student_data()
print("Every second row:")
print(df.iloc[::2])
print("Every third row:")
print(df.iloc[::3])
print("Reversed rows:")
print(df.iloc[::-1])Expected output
Every second row:
NAME ID MATH ENGLISH
0 Ravi 1 30 20
2 Alex 3 50 40
4 King 5 70 60
Every third row:
NAME ID MATH ENGLISH
0 Ravi 1 30 20
3 Ron 4 60 50
Reversed rows:
NAME ID MATH ENGLISH
5 Jack 6 80 70
4 King 5 70 60
3 Ron 4 60 50
2 Alex 3 50 40
1 Raju 2 40 30
0 Ravi 1 30 20The :0 slice returns no rows while preserving columns and dtypes. This is useful when preparing a schema for a later operation. It does not provide a new dataset with observations.
df = student_data()
empty = df.iloc[:0].copy()
print(empty)
assert empty.columns.tolist() == df.columns.tolist()
assert empty.dtypes.equals(df.dtypes)Expected output
Empty DataFrame
Columns: [NAME, ID, MATH, ENGLISH]
Index: []loc selects by labels; iloc selects by current order. Here label 0 does not exist, but position 0 still selects Ravi. loc label slices include the stop label when slicing this ordered index; iloc position slices exclude the stop position. See set_index() for choosing labels.
df = student_data()
df.index = [101, 205, 309, 412, 518, 623]
print("Position 0:")
print(df.iloc[[0]])
print("Label 205:")
print(df.loc[[205]])
print("Two positions:")
print(df.iloc[0:2])
print("Labels 101 through 309, inclusive:")
print(df.loc[101:309])Expected output
Position 0:
NAME ID MATH ENGLISH
101 Ravi 1 30 20
Label 205:
NAME ID MATH ENGLISH
205 Raju 2 40 30
Two positions:
NAME ID MATH ENGLISH
101 Ravi 1 30 20
205 Raju 2 40 30
Labels 101 through 309, inclusive:
NAME ID MATH ENGLISH
101 Ravi 1 30 20
205 Raju 2 40 30
309 Alex 3 50 40A row mask needs one True/False value per row; a column mask needs one per column. iloc uses the mask in positional order. The sample has four columns, so its column mask contains four Boolean values.
df = student_data()
print("NAME and MATH columns:")
print(df.iloc[:, [True, False, True, False]])
df2 = df.iloc[:3]
print("First and third of the three selected rows:")
print(df2.iloc[[True, False, True]])Expected output
NAME and MATH columns:
NAME MATH
0 Ravi 30
1 Raju 40
2 Alex 50
3 Ron 60
4 King 70
5 Jack 80
First and third of the three selected rows:
NAME ID MATH ENGLISH
0 Ravi 1 30 20
2 Alex 3 50 40A comparison returns a Boolean Series. For iloc, pass a positional Boolean array with to_numpy(). For label-aware selection, loc accepts the Series directly. Build the mask from the same current DataFrame, especially after filtering or sorting.
df = student_data()
mask = df["MATH"].ge(50)
print(df.iloc[mask.to_numpy()])
assert df.iloc[mask.to_numpy()].equals(df.loc[mask])
assert df.loc[mask].equals(df[df["MATH"] >= 50])Expected output
NAME ID MATH ENGLISH
2 Alex 3 50 40
3 Ron 4 60 50
4 King 5 70 60
5 Jack 6 80 70The callable receives the current DataFrame and returns positions or a Boolean array. This example includes alternate positions, selected positions, even student IDs and scores above a threshold. Positions [1, 3, 5] select the second, fourth and sixth rows of this dataset. Avoid a single callable returning a tuple; use separate row and column selectors.
df = student_data()
print("Alternate positions:")
print(df.iloc[lambda x: list(range(0, len(x), 2))])
print("Selected positions:")
print(df.iloc[lambda x: [1, 3, 5], :])
print("Even IDs:")
print(df.iloc[lambda x: x["ID"].mod(2).eq(0).to_numpy()])
print("MATH greater than 60:")
print(df.iloc[lambda x: x["MATH"].gt(60).to_numpy(), :])Expected output
Alternate positions:
NAME ID MATH ENGLISH
0 Ravi 1 30 20
2 Alex 3 50 40
4 King 5 70 60
Selected positions:
NAME ID MATH ENGLISH
1 Raju 2 40 30
3 Ron 4 60 50
5 Jack 6 80 70
Even IDs:
NAME ID MATH ENGLISH
1 Raju 2 40 30
3 Ron 4 60 50
5 Jack 6 80 70
MATH greater than 60:
NAME ID MATH ENGLISH
4 King 5 70 60
5 Jack 6 80 70A single invalid position raises IndexError. A slice may extend beyond the end and simply return available rows. Check empty before asking for the first or last row when a filter may return no matches.
df = student_data()
try:
df.iloc[6]
except IndexError:
print("Position 6 is outside this six-row DataFrame.")
print("A slice safely stops at the end:")
print(df.iloc[4:20])
matches = df.loc[df["MATH"] > 100]
if matches.empty:
print("No matching students; there is no last row to select.")Expected output
Position 6 is outside this six-row DataFrame.
A slice safely stops at the end:
NAME ID MATH ENGLISH
4 King 5 70 60
5 Jack 6 80 70
No matching students; there is no last row to select.Assign directly to the source DataFrame in one indexing operation. get_loc finds the current position of a named column; it assumes column names are unique. Position 3 is the fourth row, not necessarily label 3. For one scalar, iat is another positional option.
df = student_data()
math_position = df.columns.get_loc("MATH")
df.iloc[3, math_position] = 100
print(df)
assert df.iloc[3, math_position] == 100
df = student_data()
df.iloc[:, df.columns.get_loc("MATH")] = 100
print("All MATH scores updated:")
print(df)Expected output
NAME ID MATH ENGLISH
0 Ravi 1 30 20
1 Raju 2 40 30
2 Alex 3 50 40
3 Ron 4 100 50
4 King 5 70 60
5 Jack 6 80 70
All MATH scores updated:
NAME ID MATH ENGLISH
0 Ravi 1 100 20
1 Raju 2 100 30
2 Alex 3 100 40
3 Ron 4 100 50
4 King 5 100 60
5 Jack 6 100 70Convert a Boolean condition to a positional array before using it with iloc. The examples start from fresh data: set qualifying scores to 100, then replace a matching name. Prefer loc if your task is naturally expressed with column labels.
df = student_data()
mask = df["MATH"].ge(70).to_numpy()
df.iloc[mask, df.columns.get_loc("MATH")] = 100
print(df)
assert df["MATH"].tolist() == [30, 40, 50, 60, 100, 100]
df = student_data()
name_mask = df["NAME"].eq("Raju").to_numpy()
df.iloc[name_mask, df.columns.get_loc("NAME")] = "Updated name"
print(df.iloc[[1]])Expected output
NAME ID MATH ENGLISH
0 Ravi 1 30 20
1 Raju 2 40 30
2 Alex 3 50 40
3 Ron 4 60 50
4 King 5 100 60
5 Jack 6 100 70
NAME ID MATH ENGLISH
1 Updated name 2 40 30If a loop is necessary, iterate over range(len(df)), not df.index. Index labels can be strings or nonconsecutive integers. The direct whole-column assignment shown afterwards is clearer when every row receives the same value.
df = student_data()
df.index = [101, 205, 309, 412, 518, 623]
for position in range(len(df)):
df.iloc[position, 3] = 51
print(df)
direct = student_data()
direct.iloc[:, 3] = 51
assert df["ENGLISH"].tolist() == direct["ENGLISH"].tolist()Expected output
NAME ID MATH ENGLISH
101 Ravi 1 30 51
205 Raju 2 40 51
309 Alex 3 50 51
412 Ron 4 60 51
518 King 5 70 51
623 Jack 6 80 51Position is a property of the current row order. This synthetic sales extract has IDs and amounts that deliberately arrive out of order. Sort by amount, then select the first three rows and the columns required for a review. In an actual import, inspect column names before using positional column selections.
sales = pd.DataFrame({
"order_id": [1004, 1001, 1005, 1002, 1003],
"customer": ["Office A", "Shop B", "Office C", "Shop D", "Office E"],
"amount": [80, 250, 120, 40, 175]
})
ranked = sales.sort_values("amount", ascending=False)
review = ranked.iloc[:3, [0, 2]].copy()
print(review)
assert review["order_id"].tolist() == [1001, 1003, 1005]
print("Current first-row label:", ranked.index[0])
print("Current first-row order:", ranked.iloc[0, 0])Expected output
order_id amount
1 1001 250
4 1003 175
2 1005 120
Current first-row label: 1
Current first-row order: 1001Use copy() when you want a separate review table. Updating that review table should not update the original source. To change the source itself, assign directly to it with iloc or loc. Avoid chained assignments such as df.iloc[:2]["MATH"] = 0.
df = student_data()
review = df.iloc[:2].copy()
review.iloc[:, review.columns.get_loc("MATH")] = 0
print("Edited review:")
print(review)
print("Original scores:", df["MATH"].tolist())
assert df["MATH"].tolist() == [30, 40, 50, 60, 70, 80]Expected output
Edited review:
NAME ID MATH ENGLISH
0 Ravi 1 0 20
1 Raju 2 0 30
Original scores: [30, 40, 50, 60, 70, 80]Select every second student starting at position 1, with only NAME and ENGLISH. Predict the names and values first. Then verify the shape is three rows by two columns. Expected names: Raju, Ron and Jack; scores: 30, 50 and 70.
The slice 1::2 starts at the second row and advances two positions at a time. The column list [0, 3] selects NAME and ENGLISH while preserving a DataFrame.
df = student_data()
answer = df.iloc[1::2, [0, 3]]
print(answer)
assert answer.shape == (3, 2)
assert answer["NAME"].tolist() == ["Raju", "Ron", "Jack"]
assert answer["ENGLISH"].tolist() == [30, 50, 70]Expected output
NAME ENGLISH
1 Raju 30
3 Ron 50
5 Jack 70Return to these examples when choosing the output shape. The comments identify positions using the actual sample column order. Each expression below is valid for the six-row student dataset.
df = student_data()
print(df.iloc[::]) # All rows
print(df.iloc[0]) # First row as Series
print(df.iloc[1]) # Second row as Series
print(df.iloc[1, 3]) # Second row, fourth column: ENGLISH = 30
print(df.iloc[:, 1]) # ID column as Series
print(df.iloc[-1]) # Last row as Series
print(df.iloc[[-1]]) # Last row as DataFrame
print(df.iloc[:2]) # First two rows
print(df.iloc[:-2]) # All except the last two
print(df.iloc[-2]) # Second-last row
print(df.iloc[-2:]) # Last two rows
print(df.iloc[2:5]) # Positions 2, 3 and 4
print(df.iloc[[0, -1]]) # First and last
print(df.iloc[::2]) # Positions 0, 2 and 4
print(df.iloc[::3]) # Positions 0 and 3Expected output
NAME ID MATH ENGLISH
0 Ravi 1 30 20
1 Raju 2 40 30
2 Alex 3 50 40
3 Ron 4 60 50
4 King 5 70 60
5 Jack 6 80 70
NAME Ravi
ID 1
MATH 30
ENGLISH 20
Name: 0, dtype: object
NAME Raju
ID 2
MATH 40
ENGLISH 30
Name: 1, dtype: object
30
0 1
1 2
2 3
3 4
4 5
5 6
Name: ID, dtype: int64
NAME Jack
ID 6
MATH 80
ENGLISH 70
Name: 5, dtype: object
NAME ID MATH ENGLISH
5 Jack 6 80 70
NAME ID MATH ENGLISH
0 Ravi 1 30 20
1 Raju 2 40 30
NAME ID MATH ENGLISH
0 Ravi 1 30 20
1 Raju 2 40 30
2 Alex 3 50 40
3 Ron 4 60 50
NAME King
ID 5
MATH 70
ENGLISH 60
Name: 4, dtype: object
NAME ID MATH ENGLISH
4 King 5 70 60
5 Jack 6 80 70
NAME ID MATH ENGLISH
2 Alex 3 50 40
3 Ron 4 60 50
4 King 5 70 60
NAME ID MATH ENGLISH
0 Ravi 1 30 20
5 Jack 6 80 70
NAME ID MATH ENGLISH
0 Ravi 1 30 20
2 Alex 3 50 40
4 King 5 70 60
NAME ID MATH ENGLISH
0 Ravi 1 30 20
3 Ron 4 60 50Use loc when labels or aligned conditions express your intent. Use head() and tail() for previews. Use reset_index() when you intentionally want to change the labels; it is not required for iloc. Explore DataFrame attributes to learn about indexers and other table properties. For array slicing, compare NumPy indexing and slicing.
Open in Google Colab View on GitHub
All sample data is embedded. Run the cells in order, change positions and compare Series versus DataFrame outputs.
Reference: Pandas DataFrame.iloc documentation.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.