pandas
boolean indexing
logical operators
data analysis
python programming

Logical operators for Boolean indexing in Pandas

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

Boolean indexing is one of pandas' most useful features, but it has one syntax rule that trips people up repeatedly: you must use bitwise operators for element-wise conditions, not Python's plain and, or, and not. Once that rule is clear, complex filters become predictable and readable.

Use &, |, and ~

Each pandas comparison returns a boolean Series. Combine those Series with:

  • '& for element-wise and'
  • '| for element-wise or'
  • '~ for element-wise not'

Example:

python
1import pandas as pd
2
3df = pd.DataFrame(
4    {
5        "name": ["Ava", "Ben", "Cara", "Dan"],
6        "age": [23, 31, 29, 41],
7        "country": ["CA", "US", "CA", "US"],
8        "score": [88, 72, 95, 60],
9    }
10)
11
12mask = (df["age"] >= 25) & (df["country"] == "CA")
13print(df[mask])

Parentheses matter because operator precedence can otherwise produce incorrect expressions.

Why and and or Fail

Python's and and or expect single truth values, but pandas comparisons produce whole Series objects. That is why this is wrong:

python
# Wrong
# df[(df["age"] > 25) and (df["score"] > 80)]

The correct version is:

python
filtered = df[(df["age"] > 25) & (df["score"] > 80)]
print(filtered)

The same rule applies to or versus |.

Named Masks Improve Readability

For non-trivial filters, build the masks in steps:

python
1is_adult = df["age"] >= 18
2is_high_score = df["score"] >= 85
3is_canadian = df["country"] == "CA"
4
5selected = df[is_adult & is_high_score & is_canadian]
6print(selected)

This is easier to review and debug than one dense line of conditions.

Negation Uses ~

To invert a boolean mask, use ~:

python
not_canadian = ~(df["country"] == "CA")
print(df[not_canadian])

Do not use Python's not on a Series. That is the same kind of mistake as using and or or.

Handle Missing Values Explicitly

Missing values can change filter behavior in ways that are easy to miss. If your logic should treat missing values as false or as a fallback value, say so explicitly:

python
1df2 = pd.DataFrame(
2    {
3        "name": ["Eli", "Fay", "Gus"],
4        "score": [90, None, 75],
5        "active": [True, True, None],
6    }
7)
8
9mask = (df2["score"].fillna(0) >= 80) & (df2["active"].fillna(False))
10print(df2[mask])

Explicit handling makes the rule easier to understand than leaving the effect of missing data implicit.

query() Is an Alternative, Not a Replacement

For exploratory analysis, DataFrame.query() can be easier to read:

python
min_age = 25
result = df.query("age >= @min_age and country == 'CA' and score >= 80")
print(result)

This can look more SQL-like, but boolean masks are still the fundamental model. Use whichever form keeps the code clearest for the context.

Debug Masks One Step at a Time

If a filter returns unexpected rows, inspect each mask independently:

python
1age_mask = df["age"] >= 25
2country_mask = df["country"] == "CA"
3score_mask = df["score"] >= 80
4
5print("age true:", age_mask.sum())
6print("country true:", country_mask.sum())
7print("score true:", score_mask.sum())
8
9combined = age_mask & country_mask & score_mask
10print("combined true:", combined.sum())
11print(df[combined])

That quickly reveals whether one condition is too strict or simply wrong.

Common Pitfalls

  • Using and or or with pandas Series objects.
  • Forgetting parentheses around each comparison.
  • Using not instead of ~ for negation.
  • Ignoring missing values that affect boolean logic.
  • Writing one very long filter expression that nobody can debug later.

Summary

  • Use &, |, and ~ for pandas boolean indexing.
  • Wrap each comparison in parentheses.
  • Build named masks when the logic becomes complex.
  • Handle missing values deliberately instead of implicitly.
  • Use query() when it improves readability, but understand the underlying boolean-mask model.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.