Module 1: DataFrame Fundamentals50 min

Combining Conditions

Progress Tracking

Log in to save this lesson and continue from where you left off.

Log in

Multiple Conditions

A single boolean filter gets you pretty far, but real questions usually need more:

  • Find employees in HR who earn over 80,000
  • Show me orders from March or April
  • Get everyone except the Admin department

That’s where &, |, and ~ come in. They let you combine multiple conditions into one filter.

& (AND): Both Conditions Must Be True

Python
orders[
    (orders["order_details"] == "Coat") &
    (orders["total_order_cost"] > 100)
]
Table: techcorp_workforce
idfirst_namelast_namedepartmentsalaryphone_numberjoining_date
1SarahMitchellHR95000555-01012021-03-15
2MichaelChenHR88000555-01022022-06-01
3EmilyRodriguezHR825002021-09-20
4DavidParkHR80000555-01042023-01-10
5LisaThompsonHR650002021-04-05
1
Filter by Department and Salary

Filter `techcorp_workforce` to HR employees earning over 80000.

Tables: techcorp_workforce

Both conditions must be true for a row to be included.

Parentheses Are Required

In pandas, each condition must be wrapped in parentheses when combining with & or |. Without them, Python’s operator precedence causes errors. This is the most common mistake with combined filters.

Chaining Multiple Conditions

You can chain as many & conditions as you need:

2
Chain Three Conditions

Find Admin employees earning at least 80,000 who joined after January 1, 2022.

Tables: techcorp_workforce

Each & adds another filter, narrowing your results.

Churro Activity Date

Table: los_angeles_restaurant_health_inspections
serial_numberactivity_datefacility_namescoregradeservice_codeservice_descriptionemployee_idfacility_addressfacility_cityfacility_idfacility_statefacility_zipowner_idowner_namepe_descriptionprogram_element_peprogram_nameprogram_statusrecord_id
DAQHRSETQ2017-06-08MARGARITAS CAFE93A1ROUTINE INSPECTIONEE00000065026 S CRENSHAW BLVDLOS ANGELESFA0023656CA90043OW0004133BAZAN, ASCENCIONRESTAURANT (61-150) SEATS HIGH RISK1638MARGARITAS CAFEACTIVEPR0011718
DA2GQRJOS2017-03-07LAS MOLENDERAS97A1ROUTINE INSPECTIONEE00009972635 WHITTIER BLVDLOS ANGELESFA0160416CA90023OW0125379MARISOL FEREGRINORESTAURANT (0-30) SEATS HIGH RISK1632LAS MOLENDERASINACTIVEPR0148504
DAMQTA46T2016-03-22SANDRA'S TAMALES93A1ROUTINE INSPECTIONEE00010495390 WHITTIER BLVDLOS ANGELESFA0171769CA90022-4032OW0178828SANDRA'S TAMALES INC.RESTAURANT (0-30) SEATS MODERATE RISK1631SANDRA'S TAMALESACTIVEPR0164225
DAXMBTIRZ2018-02-12CAFE GRATITUDE97A1ROUTINE INSPECTIONEE0000828639 N LARCHMONT BLVD STE #102LOS ANGELESFA0058921CA90004OW0005704CAFE GRATITUDE LARCHMONT LLCRESTAURANT (61-150) SEATS HIGH RISK1638CAFE GRATITUDEACTIVEPR0019854
DAK8TBMS02015-09-10THE WAFFLE90A1ROUTINE INSPECTIONEE00007096255 W SUNSET BLVD STE #105LOS ANGELESFA0051830CA90028OW0035796THE WAFFLE, LLCRESTAURANT (61-150) SEATS HIGH RISK1638THE WAFFLEACTIVEPR0010922
3
Churro Activity Date
View solution

Find the inspection date and risk category (`pe_description`) of facilities named 'STREET CHURROS' that received a score below 95.

Tables: los_angeles_restaurant_health_inspections

| (OR): Either Condition Can Be True

Python
orders[
    (orders["total_order_cost"] > 500) |
    (orders["order_details"] == "Coat")
]
4
Filter HR or Admin Employees

Filter `techcorp_workforce` to employees in HR or Admin using |.

Tables: techcorp_workforce

A row only needs to match one condition to be included. HR employees are in, Admin employees are in, Engineering employees are out.

Use .isin() for Multiple Values on the Same Column

When checking the same column for multiple values, .isin() (covered later in this lesson) is often cleaner than chaining | conditions. For example: df[df["department"].isin(["HR", "Admin"])].

Lyft Driver Wages

Table: lyft_drivers
indexstart_dateend_dateyearly_salary
02018-04-0248303
12018-05-3067973
22015-04-0556685
32015-01-0851320
42017-03-0967507
5
Lyft Driver Wages
View solution

Find all Lyft drivers who earn either equal to or less than 30k USD or equal to or more than 70k USD. Output all details related to retrieved records.

Tables: lyft_drivers

Matching a List of Values with .isin()

Chaining | for the same column gets ugly fast. .isin() replaces it with a single clean expression.

Instead of chaining multiple == comparisons with |, use .isin():

Python
# Instead of this:
df[(df["dept"] == "HR") | (df["dept"] == "Admin")]

# Do this:
df[df["dept"].isin(["HR", "Admin"])]

Matching Multiple Values

6
Filter Multiple Departments

Filter `techcorp_workforce` to employees in HR and Admin using `.isin()`.

Tables: techcorp_workforce
Python
# Exclude with ~
techcorp_workforce[
    ~techcorp_workforce["department"].isin(["HR", "Admin"])
]
Use .isin() Instead of Chaining |

.isin() is cleaner and faster than chaining multiple | comparisons. It also works with any iterable: lists, sets, or another column’s values.

Athletes Over 40 With Bronze or Silver

Table: olympics_athletes_events
idnamesexageheightweightteamnocgamesyearseasoncitysporteventmedal
3520Guillermo J. AmparanMMexicoMEX1924 Summer1924SummerParisAthleticsAthletics Men's 800 metres
35394Henry John FinchettMGreat BritainGBR1924 Summer1924SummerParisGymnasticsGymnastics Men's Rings
21918Georg Frederik Ahrensborg ClausenM28DenmarkDEN1924 Summer1924SummerParisCyclingCycling Men's Road Race Individual
110345Marinus Cornelis Dick SigmondM26NetherlandsNED1924 Summer1924SummerParisFootballFootball Men's Football
54193Thodore Tho JeitzM26LuxembourgLUX1924 Summer1924SummerParisGymnasticsGymnastics Men's Individual All-Around
7
Find all athletes who were older than 40 years when they won either Bronze or Silver medals
View solution

Find all athletes who were older than 40 years when they won either Bronze or Silver medals.

Tables: olympics_athletes_events

Range Filtering with .between()

Python
orders[orders["total_order_cost"].between(50, 200)]
8
Filter Salaries in a Range

Filter `techcorp_workforce` to salaries between 80000 and 120000.

Tables: techcorp_workforce
.between() Is Inclusive

.between(80000, 120000) includes both endpoints. It’s equivalent to (df["salary"] >= 80000) & (df["salary"] <= 120000).

Python
# .between() with dates
techcorp_workforce[techcorp_workforce["joining_date"].between("2022-01-01", "2022-12-31")]

Date Ranges

.between() works with dates too — pass date strings in YYYY-MM-DD format.

9
Find Orders in a Date Range

Find all orders placed during March 2019.

Tables: orders

~ (NOT): Exclude Matches

Negation is surprisingly useful. Instead of listing every department you want, sometimes it’s easier to say "everything except Admin." ~ flips True to False and False to True — giving you the opposite of any condition.

~ inverts a boolean condition. It gives you everything that doesn’t match.

Python
# Everyone except Admin
techcorp_workforce[~(techcorp_workforce["department"] == "Admin")]

This is equivalent to using !=:

Python
techcorp_workforce[techcorp_workforce["department"] != "Admin"]

Both work. ~ is especially useful combined with .isin() and .str.contains(), as you’ve already seen.

Negating a Condition

10
Exclude with `~`

Rewrite the filter to exclude HR employees using ~ instead of !=.

Tables: techcorp_workforce

Combining & and |

Mixing AND and OR is where most bugs happen. The issue is operator precedence: & binds tighter than |, just like multiplication before addition. If you don’t add parentheses, you’ll get silently wrong results — the query runs fine, but the logic isn’t what you intended.

Let’s say you want employees in HR or Engineering, but only those earning over 80,000. You might write:

Python
# This doesn't do what you think!
techcorp_workforce[
    (techcorp_workforce["department"] == "HR") |
    (techcorp_workforce["department"] == "Engineering") &
    (techcorp_workforce["salary"] > 80000)
]

Because & is evaluated first, Python reads this as:

Python
# What Python actually evaluates:
# HR (any salary) OR (Engineering AND salary > 80000)

So you get ALL HR employees (regardless of salary) plus Engineering employees earning over 80,000. Not what you wanted.

Use Parentheses to Control Order

11
Fix Operator Precedence

Find employees in HR or Engineering who earn more than 80,000. Add parentheses to make sure the salary filter applies to both departments.

Tables: techcorp_workforce
When in Doubt, Use Parentheses

Parentheses make your intent clear and prevent bugs. Even if they’re not strictly necessary, they help whoever reads your code next — including future you.

High Earners in Support Departments

Table: techcorp_workforce
idfirst_namelast_namedepartmentsalaryphone_numberjoining_date
1SarahMitchellHR95000555-01012021-03-15
2MichaelChenHR88000555-01022022-06-01
3EmilyRodriguezHR825002021-09-20
4DavidParkHR80000555-01042023-01-10
5LisaThompsonHR650002021-04-05
12
High Earners in Support Departments
View solution

The HR team is reviewing compensation packages for employees in support functions. They want to identify high earners in the HR and Admin departments for a salary benchmarking study. Find all employees who earn more than $80,000 and work in either the HR or Admin department. Return `first name`, `last name`, `department`, and `salary`.

Tables: techcorp_workforce

Keeping It Readable

Production code gets read far more than it gets written. A filter that takes 30 seconds to decipher costs time whenever someone revisits it. These two patterns — named variables and line breaks — make your filters self-documenting.

Break conditions into variables

Python
is_hr = techcorp_workforce["department"] == "HR"
is_admin = techcorp_workforce["department"] == "Admin"
high_earner = techcorp_workforce["salary"] > 80000

techcorp_workforce[(is_hr | is_admin) & high_earner]

Naming your conditions makes complex filters much easier to read and debug.

Use backslash for line breaks

Python
techcorp_workforce[
    (techcorp_workforce["department"] == "HR") &
    (techcorp_workforce["salary"] > 80000) &
    (techcorp_workforce["joining_date"] > "2022-01-01")
]

Putting each condition on its own line with consistent indentation keeps things scannable.

Common Mistakes

Missing parentheses around conditions

Python
# Wrong: missing parentheses around conditions
techcorp_workforce[
    techcorp_workforce["department"] == "HR" &
    techcorp_workforce["salary"] > 80000
]

# Correct: each condition wrapped in parentheses
techcorp_workforce[
    (techcorp_workforce["department"] == "HR") &
    (techcorp_workforce["salary"] > 80000)
]

Without parentheses, & binds to "HR" and techcorp_workforce instead of the full conditions. This is the number one cause of errors with combined filters.

Using and/or instead of &/|

Python
# Wrong: Python keywords don't work on Series
techcorp_workforce[
    (techcorp_workforce["department"] == "HR") and
    (techcorp_workforce["salary"] > 80000)
]

# Correct: use & and | for element-wise operations
techcorp_workforce[
    (techcorp_workforce["department"] == "HR") &
    (techcorp_workforce["salary"] > 80000)
]

Python’s and/or keywords work on single True/False values. For filtering DataFrames, you need &/|, which work element-wise across entire columns.

Key Takeaways

  • & requires all conditions to be true (AND).
  • | requires at least one condition to be true (OR).
  • ~ inverts a condition (NOT).
  • Every condition must be wrapped in parentheses when combining with & or |.
  • & is evaluated before | — use extra parentheses to control the order.
  • Use & and |, never Python’s and and or, when filtering DataFrames.
  • .isin([list]) matches multiple values; ~ inverts any filter.
  • .between(low, high) filters inclusive ranges — works with numbers and dates.

What’s Next

Missing values are one of the most common headaches in data analysis. In the next lesson, you'll learn what NaN really means, why comparisons break, and how to handle it correctly.