Using enumerate() in Python

Data Science Tool Box Mar 18, 2026

Using enumerate() in Python

The enumerate() function is one of the most useful built-in tools in Python for data science. It allows you to loop over an iterable while keeping track of the index (position) at the same time, making your code cleaner and more Pythonic.

TL;DR — Why Use enumerate()

  • Replaces manual counter variables
  • Provides both index and value in each iteration
  • Supports custom starting index with start=
  • Makes code more readable and less error-prone

1. Basic Usage

scores = [85, 92, 78, 95, 88, 76]

# Without enumerate (old style)
for i in range(len(scores)):
    print(f"Rank {i+1}: {scores[i]}")

# With enumerate (modern, cleaner)
for rank, score in enumerate(scores, start=1):
    print(f"Rank {rank}: {score}")

2. Real-World Data Science Examples

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_id": range(1001, 1001 + n),
    "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")

# Example 1: Adding row numbers
for idx, row in enumerate(df.itertuples(), start=1):
    if row.amount > 1000:
        print(f"Row {idx}: High value sale by customer {row.customer_id}")

# Example 2: Processing features with their positions
features = ["amount", "quantity", "profit", "region", "category"]

for position, feature in enumerate(features, start=1):
    print(f"Feature {position:2d}: {feature:12} (dtype: {df[feature].dtype})")

# Example 3: Creating a ranked list
top_sales = df.nlargest(10, "amount")

for rank, (_, row) in enumerate(top_sales.iterrows(), start=1):
    print(f"#{rank:2d} | ${row['amount']:8.2f} | {row['region']}")

3. Best Practices in 2026

  • Use enumerate(sequence, start=1) when you need 1-based ranking
  • Prefer enumerate() over manual counters like i = 0; i += 1
  • Use it with itertuples() for better performance on large DataFrames
  • Combine with unpacking: for idx, value in enumerate(data)
  • Keep the loop body clean — don't make it too complex

Positions Without Counters

enumerate(iterable, start=0) wraps any iterable and yields pairs of position and item. It replaces a manual counter variable that you would otherwise have to create and increment yourself.

The start argument makes human-friendly numbering easy, such as row 1 instead of row 0 in a report. enumerate works lazily, so it is fine for large files and generators.

A common use in data work is reporting the line number of bad records while reading a file, so problems can be found and fixed at the source.

Worked Example: Reporting Bad Lines

We parse lines and report the line number of any value that cannot be converted.

lines = ["amount", "120.5", "80", "n/a", "300", ""]

values, problems = [], []
for line_no, text in enumerate(lines[1:], start=2):
    try:
        values.append(float(text))
    except ValueError:
        problems.append(line_no)

print(values)
print("bad lines:", problems)

for rank, name in enumerate(["Ana", "Ben", "Cara"], start=1):
    print(f"{rank}. {name}")

Starting at 2 matches the file line numbers, because line 1 is the header. Lines 4 and 6 fail to convert, so they are reported for review.

Practical Tips

enumerate also pairs well with zip when you need a position and items from two lists at the same time: for i, (a, b) in enumerate(zip(xs, ys)) keeps all three values clearly named in one loop header.

Common Mistakes

  • Maintaining a manual counter alongside a for loop.
  • Forgetting start=1 for human-readable numbering.
  • Using range(len(seq)) and then indexing.
  • Mismatching numbers after slicing off a header.

Quick Summary

  • enumerate yields position and item pairs.
  • Use start= for readable numbering.
  • It works lazily on any iterable.
  • Great for reporting bad line numbers.

Conclusion

enumerate() is a small but incredibly useful built-in function that eliminates the need for manual index counters. In data science, it is commonly used when processing rows, ranking results, iterating over feature lists, or creating numbered outputs. Using enumerate() makes your code more Pythonic, readable, and less prone to off-by-one errors.

Next steps:

  • Search your codebase for loops that use manual counters and replace them with enumerate()

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