pandas
DataFrame
data manipulation
Python
data analysis

How do I select rows from a DataFrame based on column values?

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

Row filtering is one of the most important pandas operations because most analysis starts by narrowing data to relevant records. The API is flexible, but small syntax mistakes can produce wrong subsets or hard-to-debug warnings. A clear mental model is that filters are boolean masks aligned to DataFrame rows.

Build a Small Example DataFrame

Use a reproducible sample so every filter result is easy to verify.

python
1import pandas as pd
2
3df = pd.DataFrame(
4    {
5        "name": ["Alice", "Bob", "Charlie", "Diana", "Evan"],
6        "age": [24, 31, 22, 31, 40],
7        "team": ["A", "B", "A", "B", "C"],
8        "active": [True, True, False, True, False],
9        "score": [87.5, 91.0, 72.0, 88.0, 95.0],
10    }
11)
12
13print(df)

Basic Equality and Comparison Filters

The most common filter form is direct comparison on one column.

python
1adults = df[df["age"] >= 30]
2print(adults)
3
4team_b = df[df["team"] == "B"]
5print(team_b)

This syntax returns rows where the mask is True. It keeps original index values unless you reset index manually.

Combine Multiple Conditions Correctly

Use & for logical AND and | for logical OR, with parentheses around each condition.

python
1active_b = df[(df["team"] == "B") & (df["active"])]
2print(active_b)
3
4high_or_inactive = df[(df["score"] >= 90) | (~df["active"])]
5print(high_or_inactive)

Do not use Python and or or with pandas Series. Those operators work on single booleans, not vectorized column comparisons.

Use loc for Readability and Column Selection

loc is often clearer when filtering rows and choosing specific output columns together.

python
subset = df.loc[df["age"] > 25, ["name", "age", "score"]]
print(subset)

This is useful when downstream code expects a narrow schema and you want filter plus projection in one line.

Useful Helpers: isin, between, and String Conditions

For real data, helper methods make filters shorter and less error-prone.

python
1teams = df[df["team"].isin(["A", "C"])]
2print(teams)
3
4age_band = df[df["age"].between(25, 35)]
5print(age_band)
6
7starts_with_a = df[df["name"].str.startswith("A")]
8print(starts_with_a)

These helpers are typically easier to review than long chains of explicit comparisons.

Handle Missing Values Explicitly

Comparisons with missing values can surprise people because NaN is not equal to anything, including itself. Use isna and notna.

python
1df2 = df.copy()
2df2.loc[2, "score"] = None
3
4missing_score = df2[df2["score"].isna()]
5print(missing_score)
6
7has_score = df2[df2["score"].notna()]
8print(has_score)

For conditional logic involving missing data, fill or guard values intentionally before filtering.

query for Complex, Readable Filters

query can be cleaner for longer filter expressions.

python
result = df.query("age >= 30 and active == True and score >= 88")
print(result)

query is especially useful in notebooks because expressions read like SQL-style predicates. Still, plain boolean masks are often easier to debug step by step in production code.

Avoid SettingWithCopy Issues After Filtering

Filtering and then mutating the result can trigger SettingWithCopyWarning if view or copy semantics are unclear.

Preferred pattern:

python
filtered = df.loc[df["team"] == "B"].copy()
filtered["bonus"] = 10
print(filtered)

Using .copy explicitly for mutable subsets prevents ambiguous chained assignment behavior.

Performance Tips for Large DataFrames

When filtering large datasets:

  • Select only needed columns early.
  • Avoid repeated recomputation of the same mask.
  • Convert repeated categorical text columns to category dtype when appropriate.

Example mask reuse:

python
mask = (df["active"]) & (df["score"] > 85)
result = df.loc[mask, ["name", "score"]]
print(result)

This is clearer and can be faster than rebuilding mask expressions multiple times.

Common Pitfalls

A common pitfall is using Python and and or instead of & and | for Series comparisons. Another issue is forgetting parentheses around each condition, which changes operator precedence and produces incorrect filters. Teams also overlook missing-value semantics and accidentally drop valid records due to NaN handling mistakes. Filtering and mutating without .copy is another recurring source of warnings and nondeterministic behavior. Finally, large notebooks often repeat identical filter logic in many cells, making results drift when one predicate is changed and others are not.

Summary

  • Row filtering in pandas is boolean-mask based.
  • Use direct comparisons, loc, and helper methods like isin and between.
  • Combine conditions with & and |, not Python and and or.
  • Handle missing values explicitly with isna and notna.
  • Use .copy when mutating filtered subsets to avoid assignment ambiguity.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free 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.