Iterating with .iloc in pandas DataFrame – Python 2026 with Efficient Code

Efficient Code Mar 15, 2026

Iterating with .iloc in pandas DataFrame – Python 2026 with Efficient Code

Using .iloc to iterate over a pandas DataFrame is a common pattern, but in 2026 it is often a sign of suboptimal code. While .iloc is fast for positional indexing, iterating with it is usually much slower than vectorized alternatives.

This March 15, 2026 guide explains when .iloc iteration is acceptable and, more importantly, how to avoid it for better performance.

TL;DR — Key Takeaways 2026

  • .iloc is great for accessing specific positions, but slow for iteration
  • Avoid for i in range(len(df)) with df.iloc[i]
  • Prefer vectorized operations, .itertuples(), or .apply() (sparingly)
  • Vectorized code is usually 10–100x faster than .iloc loops
  • Use .iloc only when you truly need positional access

1. The Common Anti-Pattern


import pandas as pd

df = pd.DataFrame({"score": [72, 88, 95, 61]})   # demo data

# ❌ Bad: Iterating with .iloc (very slow)
for i in range(len(df)):
    row = df.iloc[i]
    df.loc[i, 'new_col'] = row['score'] * 1.1

2. Better Alternatives in 2026


# ✅ Option 1: Vectorized (Best)
df['new_col'] = df['score'] * 1.1

# ✅ Option 2: itertuples() - Much faster than iloc
for row in df.itertuples():
    # row is a namedtuple - very fast access
    pass

# ✅ Option 3: When you need index and value
for idx, score in enumerate(df['score']):
    pass

3. When .iloc Iteration Is Acceptable


# Demo helper and column for this example
df["status"] = "normal"
def some_complex_condition(row):
    return row["score"] > 90 and row["new_col"] > 100

# Only acceptable in rare cases:
for i in range(len(df)):
    if some_complex_condition(df.iloc[i]):
        df.iloc[i, df.columns.get_loc('status')] = 'special'

Even then, a vectorized approach with np.where() or boolean indexing is usually better.

4. Best Practices in 2026

  • Avoid for i in range(len(df)) with .iloc[i]
  • Use vectorized operations whenever possible
  • Use .itertuples() when you must iterate row by row
  • Use boolean indexing for conditional updates
  • Use .apply() only when vectorization isn’t feasible

Why .iloc in a Loop Is Slow

Every df.iloc[i] call creates a new Series for row i, with index handling and type checks. Inside a loop over thousands of rows, that overhead dominates. Writing back with df.loc[i, col] adds even more cost per iteration.

A vectorised column expression does the same work in a single call. When a row-by-row loop is truly necessary, itertuples is much faster, and collecting results in a list before assigning them as a column avoids repeated writes.

Keep .iloc for what it does well: selecting specific rows or slices by position.

Worked Example: Three Ways to Add a Column

We add a discounted price column three ways and time them.

import timeit
import numpy as np
import pandas as pd

df = pd.DataFrame({"price": np.arange(1, 5_001, dtype=float)})

def iloc_loop():
    out = df.copy()
    for i in range(len(out)):
        out.loc[i, "discounted"] = out.iloc[i]["price"] * 0.9
    return out["discounted"]

def tuples_list():
    return pd.Series([row.price * 0.9 for row in df.itertuples()], index=df.index)

def vectorised():
    return df["price"] * 0.9

print(np.allclose(iloc_loop(), vectorised()), np.allclose(tuples_list(), vectorised()))
for f in (iloc_loop, tuples_list, vectorised):
    print(f"{f.__name__:11} {min(timeit.repeat(f, number=1, repeat=3)):.4f}s")

All three produce the same column. The iloc loop is by far the slowest, itertuples with a list is much faster, and the vectorised version is fastest, usually by several orders of magnitude compared with iloc.

Practical Tips

If you must loop, iterate over plain NumPy arrays taken from the columns with to_numpy(); element access is much cheaper than iloc on the DataFrame.

Measure with %timeit on real data to confirm the gain before refactoring larger scripts.

Common Mistakes

  • Writing df.loc[i, col] inside a loop.
  • Using iloc for row iteration instead of selection.
  • Modifying the DataFrame you are iterating over.
  • Not trying a vectorised expression first.

Quick Summary

  • iloc creates a Series per call.
  • Vectorised columns are fastest.
  • Collect loop results in a list first.
  • Use iloc for positional selection.

Conclusion — Iterating with .iloc in 2026

Iterating over a DataFrame with .iloc is one of the most common performance anti-patterns in pandas. In 2026, the most efficient pandas code almost never uses explicit .iloc loops. Instead, it relies on vectorized operations, boolean indexing, and occasionally .itertuples() when row-by-row logic is truly required.

Next steps:

  • Search your codebase for .iloc loops and replace them with vectorized alternatives

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