Subsetting by Row and Column Number in Pandas – .iloc[] Best Practices 2026

Data Manipulation Mar 18, 2026

Subsetting by Row and Column Number in Pandas – .iloc[] Best Practices 2026

When you need to select data by position (row number and column number) rather than by labels, Pandas provides the powerful .iloc[] indexer. In 2026, understanding .iloc[] is essential for tasks like taking the first N rows, selecting specific column ranges, or creating training/test splits.

TL;DR — .iloc[] Syntax

  • df.iloc[row_index] – Select by row position
  • df.iloc[:, col_index] – Select by column position
  • df.iloc[start:end, start:end] – Slice both rows and columns
  • Negative numbers count from the end

1. Basic Row and Column Subsetting with .iloc[]

import pandas as pd

# Create a small demo file so this example runs as-is
# (in your project, sales_data.csv is your real export)
import numpy as np
rng = np.random.default_rng(42)
n = 60
pd.DataFrame({
    "order_date": pd.date_range("2026-01-01", periods=n, freq="3D"),
    "customer_id": rng.integers(101, 121, n),
    "customer_name": rng.choice([" ana", "ben ", "chen", "dana"], n),
    "product_id": rng.integers(1, 9, n),
    "region": rng.choice(["North", "South", "East", "West"], n),
    "category": rng.choice(["Electronics", "Computers", "Books", "Home"], n),
    "payment_method": rng.choice(["card", "cash", "wallet"], n),
    "segment": rng.choice(["consumer", "business"], n),
    "quantity": rng.integers(1, 6, n),
    "amount": rng.uniform(50, 2500, n).round(2),
    "discount": rng.choice([0.0, 0.05, 0.1, 0.2], n),
}).assign(
    price=lambda d: (d["amount"] / d["quantity"]).round(2),
    profit=lambda d: (d["amount"] * rng.uniform(0.05, 0.3, n)).round(2),
).assign(
    ship_date=lambda d: d["order_date"] + pd.Timedelta(days=2),
    status=lambda d: np.where(d["amount"] > 2000, "priority", "standard"),
).to_csv("sales_data.csv", index=False)

df = pd.read_csv("sales_data.csv")

# First 10 rows
first_10 = df.iloc[:10]

# Last 5 rows
last_5 = df.iloc[-5:]

# Row 0 to 19, columns 2 to 5 (exclusive end)
subset = df.iloc[0:20, 2:6]

print(subset.head())

2. Selecting Specific Rows and Columns by Position

# Select rows 5, 10, 15, 20
selected_rows = df.iloc[[5, 10, 15, 20]]

# Select first 5 columns for first 100 rows
first_100_first_5_cols = df.iloc[:100, :5]

# Select last 3 columns for last 50 rows
last_50_last_3 = df.iloc[-50:, -3:]

3. Real-World Examples

# Create training and test sets by position
train = df.iloc[:8000]      # First 80% as training
test  = df.iloc[8000:]      # Remaining as test

# Get only numeric columns (assuming they are in positions 3 to 7)
numeric_features = df.iloc[:, 3:8]

# Get the most recent 100 records and only key columns
recent = df.iloc[-100:, [0, 3, 5, 7]]   # order_date, amount, region, customer_id

4. Best Practices in 2026

  • Use .iloc[] when you need to select by position (row/column number)
  • Use .loc[] when selecting by labels (column names or index values)
  • Prefer df[["col1", "col2"]] for selecting specific named columns when possible
  • Be careful with negative indexing — -1 means the last row/column
  • After heavy slicing, consider resetting the index with .reset_index(drop=True)

Position-Based Selection with iloc

iloc selects by integer position, starting at 0, and its slices behave like Python list slices: the stop position is excluded. It ignores labels completely, which makes it the right tool when you care about order, such as the first five rows or the last column.

iloc accepts integers, slices, lists of integers and boolean arrays, but not labels or boolean Series with an index. If you need to filter by a condition, convert the mask with .to_numpy() or switch to loc.

Worked Example: Rows and Columns by Number

The table below is selected by row and column positions in several common ways.

import pandas as pd

df = pd.DataFrame({
    "region": ["North", "South", "East", "West", "North"],
    "sales": [120, 80, 60, 150, 95],
    "units": [6, 4, 3, 7, 5],
})

print(df.iloc[0])
print(df.iloc[1:3, 0:2])
print(df.iloc[[0, -1], [1, 2]])
print(df.iloc[:, -1].tolist())

mask = (df["sales"] > 90).to_numpy()
print(df.iloc[mask, 0].tolist())

The slice 1:3 returns rows at positions 1 and 2, and -1 refers to the last row or column. The final line shows how a NumPy mask can be used with iloc safely.

Common Mistakes

  • Expecting the stop position to be included.
  • Passing column names to iloc.
  • Using a boolean Series directly with iloc.
  • Relying on positions after sorting or filtering changed the order.

Quick Summary

  • iloc uses zero-based positions.
  • Slice stops are excluded, as in Python lists.
  • Negative positions count from the end.
  • Use loc when you think in labels or conditions.

Conclusion

Subsetting by row and column number using .iloc[] is essential when you need positional access. In 2026, use .iloc[] for tasks like taking the first/last N rows, creating train/test splits, or selecting columns by position. For most day-to-day work, prefer label-based selection with .loc[] or direct column selection, but .iloc[] remains the go-to tool whenever position matters.

Next steps:

  • Practice using .iloc[] to select the first 100 rows and last 5 columns of one of your datasets

Related Tutorials

Share:
Last updated: March 2026
Built-in Functions in Python 2026 str() in Python 2026: String Conversion + Modern Formatting & Best Practices memoryview with JAX in Python 2026: Zero-Copy NumPy → JAX Array Interop + Efficient ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + GPU Pinning & ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + ML Examples memoryview with NumPy & PyTorch in Python 2026: Zero-Copy Views, Efficient Slicing & ML Interop Examples memoryview with NumPy in Python 2026: Zero-Copy Views, Efficient Slicing & Real ML Examples memoryview() in Python 2026: Zero-Copy Magic for Large Binary Data + Real Examples import() in Python 2026: How It Works, Security Risks & Modern Alternatives (importlib) zip() in Python 2026: How to Use It, strict=True Behavior & Real-World Examples vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices super() in Python 2026: Method Resolution & Modern Inheritance Patterns sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices