Nested Loops in Python

Data Science Tool Box Mar 18, 2026

Nested Loops in Python

Nested loops (a loop inside another loop) are frequently needed in data science for tasks like comparing pairs of items, creating cross-products, processing multi-dimensional data, or iterating over groups. However, they can quickly become slow and hard to read if not handled carefully.

TL;DR — Key Guidelines

  • Use nested loops only when truly necessary
  • Prefer list comprehensions or itertools.product() for simple cases
  • Keep the inner loop as simple as possible
  • Watch out for O(n²) performance on large datasets

1. Basic Nested Loop

teams = ["A", "B", "C"]
scores = [85, 92, 78]

for team in teams:
    for score in scores:
        print(f"Team {team} - Score: {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: Compare every pair of regions (careful with large data)
regions = df["region"].unique()

for i, region1 in enumerate(regions):
    for region2 in regions[i+1:]:          # Avoid duplicate pairs
        sales1 = df[df["region"] == region1]["amount"].sum()
        sales2 = df[df["region"] == region2]["amount"].sum()
        print(f"{region1} vs {region2}: ${sales1:,.0f} vs ${sales2:,.0f}")

# Example 2: Feature interaction pairs using nested loops
features = ["amount", "quantity", "profit"]
interactions = []

for i, f1 in enumerate(features):
    for f2 in features[i+1:]:
        interactions.append(f"{f1}_x_{f2}")

print("Interaction features:", interactions)

3. Better Alternatives to Nested Loops

from itertools import product

# Cleaner way for Cartesian product
for team, score in product(teams, scores):
    print(f"Team {team} - Score: {score}")

# List comprehension version
pairs = [(team, score) for team in teams for score in scores]

4. Best Practices in 2026

  • Limit nesting depth — two levels is usually the maximum for readability
  • Use itertools.product() or list comprehensions for simple Cartesian products
  • Avoid nested loops on large datasets; consider vectorized Pandas/NumPy operations instead
  • Use early break/continue when possible to reduce unnecessary iterations
  • For pair-wise comparisons, consider scipy.spatial.distance or correlation matrices

When Nested Loops Are Fine

A nested loop runs the inner loop once for every item of the outer loop, so the work grows as the product of the sizes. Two lists of 1,000 items mean a million iterations.

For small data, or when every pair truly must be visited, nested loops are clear and acceptable. For lookups and matching, a dictionary or set usually replaces the inner loop and makes the code dramatically faster.

In pandas, merges and joins are the vectorised replacement for nested matching loops.

Worked Example: Replacing an Inner Loop with a Dictionary

We match orders to customer names with a nested loop and with a dictionary lookup.

customers = [(i, f"cust{i}") for i in range(2_000)]
orders = [(n, n % 2_000) for n in range(5_000)]

import time
t0 = time.perf_counter()
slow = []
for order_id, cid in orders:
    for c_id, name in customers:
        if c_id == cid:
            slow.append((order_id, name))
            break
t1 = time.perf_counter()

names = dict(customers)
fast = [(order_id, names[cid]) for order_id, cid in orders]
t2 = time.perf_counter()

print(slow == fast)
print(f"nested {t1 - t0:.3f}s, dict {t2 - t1:.4f}s")

Both versions produce the same pairs, but the dictionary version does one constant-time lookup per order instead of scanning the customer list. The speed difference grows quickly as the data grows.

Common Mistakes

  • Using nested loops for matching or lookups.
  • Forgetting break once a match is found.
  • Nesting three or more loops without need.
  • Not considering pandas merge for tabular matching.

Quick Summary

  • Nested loops cost outer size times inner size.
  • Use dicts or sets for lookups.
  • Use merge or join in pandas.
  • Keep nested loops for small or truly pairwise work.

Conclusion

Nested loops are a fundamental tool but should be used judiciously in data science. In 2026, the best practice is to replace simple nested loops with list comprehensions or itertools.product() whenever possible, and to keep any remaining nested loops clean and shallow. For performance-critical code on large data, prefer vectorized operations over explicit nested iteration.

Next steps:

  • Review any nested loops in your codebase and see if they can be simplified with comprehensions or itertools

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