0Pricing
Pandas & NumPy Academy · Lesson

Boolean Indexing on DataFrames

Filter rows by applying a boolean condition to a column and combining multiple conditions with & and | operators.

Boolean Indexing on DataFrames is a free Pandas & NumPy Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Pandas & NumPy Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Is Boolean Indexing?

Boolean indexing lets you filter rows in a DataFrame by applying a condition that produces a Series of True and False values. Only rows where the condition is True are returned. This is one of the most frequently used selection techniques in Pandas because it is both readable and fast.

For example, given a sales DataFrame, you can instantly retrieve all rows where revenue exceeded 1000 without writing a loop.

import pandas as pd

df = pd.DataFrame({
    'product': ['A', 'B', 'C', 'D'],
    'revenue': [500, 1500, 800, 2000]
})

# Boolean condition produces a Series of True/False
mask = df['revenue'] > 1000
print(mask)
# 0    False
# 1     True
# 2    False
# 3     True

Applying the Boolean Mask

Once you have a boolean mask, you pass it inside square brackets on the DataFrame to select only the matching rows. The result is a new DataFrame — the original is never modified. The row indices from the original are preserved, so you always know where each row came from.

This pattern — create mask, then apply — is the standard Pandas idiom for row filtering.

import pandas as pd

df = pd.DataFrame({
    'product': ['A', 'B', 'C', 'D'],
    'revenue': [500, 1500, 800, 2000]
})

mask = df['revenue'] > 1000
filtered = df[mask]
print(filtered)
#   product  revenue
# 1       B     1500
# 3       D     2000

Inline Conditions Without a Mask Variable

You do not need to assign the boolean Series to a variable. You can write the condition inline directly inside the brackets: df[df['col'] > value]. This one-liner style is very common in data analysis notebooks because it keeps code compact and readable.

Both approaches — mask variable and inline — produce identical results. Use a variable when the condition is complex or reused; use inline for simple one-off filters.

import pandas as pd

df = pd.DataFrame({
    'city': ['NYC', 'LA', 'Chicago', 'Houston'],
    'population': [8_336_817, 3_979_576, 2_693_976, 2_320_268]
})

# Inline boolean filter
large_cities = df[df['population'] > 3_000_000]
print(large_cities)
#   city  population
# 0  NYC   8336817
# 1   LA   3979576

Combining Conditions with & (AND)

To require that both conditions are true, combine them with the & operator — not Python's and keyword, which does not work element-wise on arrays. Each condition must be wrapped in parentheses because & has higher operator precedence than comparison operators.

The result keeps only rows where every sub-condition evaluates to True.

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Carol', 'Dave'],
    'age': [25, 35, 28, 45],
    'salary': [50000, 90000, 70000, 120000]
})

# Both conditions must be true
result = df[(df['age'] > 27) & (df['salary'] > 60000)]
print(result)
#     name  age  salary
# 1    Bob   35   90000
# 2  Carol   28   70000

Combining Conditions with | (OR)

Use the | operator to keep rows where at least one condition is true. As with &, each sub-condition must be in parentheses. The | operator performs element-wise logical OR across the boolean arrays.

Mixing & and | is perfectly valid — just be careful with parentheses to enforce the correct evaluation order and avoid subtle logic bugs.

import pandas as pd

df = pd.DataFrame({
    'product': ['Laptop', 'Mouse', 'Monitor', 'Keyboard'],
    'price': [1200, 25, 300, 80],
    'category': ['Electronics', 'Accessories', 'Electronics', 'Accessories']
})

# Rows where price > 200 OR category is Accessories
result = df[(df['price'] > 200) | (df['category'] == 'Accessories')]
print(result)
#     product  price     category
# 0    Laptop   1200  Electronics
# 1     Mouse     25  Accessories
# 2   Monitor    300  Electronics
# 3  Keyboard     80  Accessories

Negating a Condition with ~

The tilde ~ operator inverts a boolean Series — it turns True into False and vice versa. Use ~ to express 'NOT' logic: keep rows that do not match a condition. This is cleaner than constructing the opposite condition manually.

For example, df[~df['status'].isin(['cancelled', 'refunded'])] keeps all rows except those two statuses.

import pandas as pd

df = pd.DataFrame({
    'order_id': [1, 2, 3, 4],
    'status': ['shipped', 'cancelled', 'delivered', 'refunded']
})

# Keep only rows that are NOT cancelled
active = df[~(df['status'] == 'cancelled')]
print(active)
#    order_id     status
# 0         1    shipped
# 2         3  delivered
# 3         4   refunded

Filtering on String Values

Boolean indexing works just as well on text columns. You can filter for an exact match with ==, or use .str methods like .str.startswith(), .str.contains(), or .str.upper() inside the condition for flexible text filtering.

String comparisons in Pandas are case-sensitive by default, so 'NYC' and 'nyc' are treated as different values. Normalise case first if needed.

import pandas as pd

df = pd.DataFrame({
    'country': ['USA', 'Germany', 'UK', 'USA', 'France'],
    'sales': [400, 200, 150, 600, 300]
})

# Filter rows where country equals USA
us_sales = df[df['country'] == 'USA']
print(us_sales)
#   country  sales
# 0     USA    400
# 3     USA    600

# Filter rows where country starts with 'G'
g_countries = df[df['country'].str.startswith('G')]
print(g_countries)
#    country  sales
# 1  Germany    200

Filtering on Multiple Columns

Complex analyses often require conditions across multiple columns combined with & and |. Break very long conditions into named boolean Series for readability — each acting as a named sub-filter that you combine at the end.

This approach makes your intent clear: each line reads like an English sentence describing one part of the filter logic.

import pandas as pd

df = pd.DataFrame({
    'region': ['North', 'South', 'North', 'East'],
    'year': [2022, 2023, 2023, 2022],
    'profit': [5000, -200, 8000, 3000]
})

is_north = df['region'] == 'North'
is_2023 = df['year'] == 2023
is_profitable = df['profit'] > 0

result = df[is_north & is_2023 & is_profitable]
print(result)
#   region  year  profit
# 2  North  2023    8000

Using np.where for Conditional Columns

Boolean indexing filters rows, but sometimes you want to add a new column based on a condition rather than drop rows. np.where(condition, value_if_true, value_if_false) is the vectorised equivalent of an if/else applied element-wise across a column, and it is much faster than using apply with a lambda.

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'score': [45, 72, 88, 61, 95]
})

# Create a new column based on a condition
df['grade'] = np.where(df['score'] >= 70, 'Pass', 'Fail')
print(df)
#    score grade
# 0     45  Fail
# 1     72  Pass
# 2     88  Pass
# 3     61  Fail
# 4     95  Pass

Assigning Values to a Filtered Subset

You can update values in place for filtered rows using .loc[mask, column]. This is the safe way to set values on a subset — using chained indexing like df[mask]['col'] = value may raise a SettingWithCopyWarning because it operates on a copy.

Always prefer df.loc[mask, 'col'] = value when you want to modify the original DataFrame in place.

import pandas as pd

df = pd.DataFrame({
    'item': ['A', 'B', 'C', 'D'],
    'stock': [0, 5, 0, 12]
})

# Mark out-of-stock items
df.loc[df['stock'] == 0, 'status'] = 'Out of Stock'
df.loc[df['stock'] > 0, 'status'] = 'Available'
print(df)
#   item  stock        status
# 0    A      0  Out of Stock
# 1    B      5     Available
# 2    C      0  Out of Stock
# 3    D     12     Available

Counting and Summing the Mask

Since boolean values are 1 (True) and 0 (False) under the hood, you can call .sum() on a mask to count matching rows, or .mean() to get the fraction of rows that match. These quick counts help you verify your filter logic before applying it to select rows.

import pandas as pd

df = pd.DataFrame({'value': [10, 55, 30, 80, 20, 70]})

mask = df['value'] > 40
print('Count of rows > 40:', mask.sum())      # 3
print('Fraction > 40:', mask.mean().round(2)) # 0.5

# Now apply
print(df[mask])
#    value
# 1     55
# 3     80
# 5     70

Quick Check

Test your understanding of boolean indexing on DataFrames.

Lesson Recap

In this lesson you learned: boolean masks filter rows by applying a condition to a column, & and | operators combine multiple conditions (not Python's and/or), and ~ inverts a boolean mask for NOT logic. Use df.loc[mask, col] = value to safely assign values to filtered rows. Next up we explore the query() method for writing filters as readable strings.

Frequently asked questions

Is the “Boolean Indexing on DataFrames” lesson free?

Yes — the full text of “Boolean Indexing on DataFrames” is free to read here on the web, and the Pandas & NumPy Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Pandas & NumPy Academy course, upgrade to CoddyKit PRO.

What will I learn in “Boolean Indexing on DataFrames”?

Filter rows by applying a boolean condition to a column and combining multiple conditions with & and | operators. You practise Pandas & NumPy Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Pandas & NumPy Academy?

No prior experience is required. Pandas & NumPy Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Boolean Indexing on DataFrames” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Pandas & NumPy Academy lesson?

Yes. Every Pandas & NumPy Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Boolean Indexing on DataFrames
  2. The query() Method
  3. isin() and between() Filters
  4. Selecting Columns by Pattern
← Back to Pandas & NumPy Academy