Conditionals in Generator Expressions – Memory-Efficient Filtering 2026

Data Science Tool Box Mar 18, 2026

Conditionals in Generator Expressions – Memory-Efficient Filtering 2026

Adding conditionals to generator expressions combines the power of filtering with lazy evaluation. This pattern is extremely useful in data science when you need to process large datasets while keeping memory usage minimal.

TL;DR — Two Ways to Add Conditions

  • Filtering: (expr for item in iterable if condition)
  • Conditional Value: (expr_if_true if condition else expr_if_false for item in iterable)

1. Basic Filtering with if

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

# Generator that yields only high scores
high_scores = (score for score in scores if score >= 90)

for score in high_scores:
    print(score)

2. Conditional Expressions (if-else) in Generators

scores = [85, 92, 78, 95, 65, 45]

# Categorize on-the-fly without creating a full list
categories = (
    "High" if s >= 90 
    else "Medium" if s >= 75 
    else "Low" 
    for s in scores
)

for cat in categories:
    print(cat)

3. Real-World Data Science Examples

import pandas as pd
# Create a small demo file so this example runs as-is
# (small stand-in for large_sales_data.csv; in your project this is your real large file)
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("large_sales_data.csv", index=False)

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

# Example 1: Memory-efficient high-value transaction filter
high_value_revenue = sum(
    row.amount * 1.1 
    for row in df.itertuples() 
    if row.amount > 1500 and row.region == "North"
)

# Example 2: Lazy feature engineering with conditions
processed_amounts = (
    0 if amount <= 0 
    else round(amount ** 0.5, 2) 
    for amount in df["amount"] 
    if pd.notna(amount)
)

# Example 3: Streaming customer categorization
premium_customers = (
    row.customer_id 
    for row in df.itertuples() 
    if row.amount > 2000 
    and row.region in ("North", "West")
)

for cust_id in premium_customers:
    print(f"Premium customer: {cust_id}")

4. Best Practices in 2026

  • Use filtering if at the end for selecting rows
  • Use if-else inside for value transformation
  • Combine with sum(), max(), or a single for loop to keep memory low
  • Avoid deeply nested conditionals — they reduce readability
  • For very complex logic, use a traditional generator function with yield

Conditions Inside Generators

Generator expressions accept the same two kinds of conditions as list comprehensions: a trailing if filters items, and an if-else expression chooses a value for each item. The only difference is that the work happens lazily.

Filtering lazily means that rejected items are never stored. Combined with next() or any(), it lets you stop as soon as the first match is found, without scanning the rest of the data.

For clarity, give complex filters a name with a small function, and keep each generator focused on one step.

Worked Example: First Match and Running Counts

We find the first large order lazily and count categories without building lists.

orders = [120, 45, 980, 0, 310, 1500, 75]

first_large = next((o for o in orders if o >= 900), None)
print(first_large)

large_count = sum(1 for o in orders if o >= 300)
print(large_count)

labels = ("large" if o >= 300 else "small" for o in orders if o > 0)
print(list(labels))

checked = 0
def is_huge(o):
    global checked
    checked += 1
    return o >= 1000
print(any(is_huge(o) for o in orders), "checked:", checked)

The first large order is 980, and three orders reach 300 or more. The counter shows that any() checked six values and stopped at 1500 without looking at the last one.

Common Mistakes

  • Building a list just to count matching items.
  • Putting if-else after the for clause.
  • Expecting a filtered generator to keep its length.
  • Writing filters with side effects in real code.

Quick Summary

  • Trailing if filters lazily.
  • if-else chooses values lazily.
  • next() and any() stop early.
  • sum(1 for ...) counts without lists.

Conclusion

Conditionals in generator expressions give you the best of both worlds: clean, readable filtering/transformation combined with excellent memory efficiency. In 2026 data science workflows, this pattern is preferred when processing large files, streaming data, or building memory-conscious pipelines.

Next steps:

  • Replace list comprehensions that only get iterated once with conditional generator expressions to reduce memory footprint in your data processing code

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