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 likei = 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()