Pandas — Filtering & Cleaning Missing/Duplicate Data
Real data is never clean. This session is the analyst's most-practiced skill: finding missing values, deciding to drop or fill them, removing duplicates, and filtering rows.
Learning Objectives
- Detect missing values with
isnull()/isna(). - Handle missing values with
dropna()vsfillna()— and choose correctly. - Remove duplicate rows with
drop_duplicates(). - Filter rows using boolean indexing.
- Combine conditions with
&(AND),|(OR), andisin().
0. The Sample DataFrame
Note the deliberate problems: missing values (NaN) and a duplicate row.
import pandas as pd
import numpy as np
df = pd.DataFrame({
"name": ["Aarav", "Priya", "Rahul", "Sneha", "Aarav"],
"city": ["Mumbai", "Delhi", None, "Bengaluru", "Mumbai"],
"balance": [25000, 120000, 8000, None, 25000]
})
1. Detecting Missing Values
df.isnull() # boolean DataFrame — True where missing
df.isnull().sum() # count of missing values per column
df.info() # also shows non-null counts
In Pandas, missing values are represented by NaN (or None).
2. dropna vs fillna — the Key Choice
When you find missing values, you have two main options: remove them or fill them.
dropna — remove rows/columns with missing values
df.dropna() # drop rows with ANY missing value
df.dropna(subset=["city"]) # drop only rows missing in "city"
df.dropna(axis=1) # drop columns with missing values
fillna — replace missing values
df.fillna(0) # fill all missing with 0
df["city"].fillna("Unknown") # fill missing city with "Unknown"
df["balance"].fillna(df["balance"].mean()) # fill with the column mean
3. Removing Duplicates
df.drop_duplicates() # remove fully duplicate rows
df.drop_duplicates(subset=["name"]) # dedupe based on "name" only
df.drop_duplicates(keep="last") # keep the last occurrence instead of first
subset controls which columns define a duplicate. For example,
deduping on name alone treats two rows with the same name as duplicates.
4. Boolean Indexing — Filtering Rows
The most common way to filter: build a boolean mask and pass it inside [].
df[df["balance"] > 50000] # rows with balance over 50,000
df[df["city"] == "Mumbai"] # rows in Mumbai
Combining conditions
Use & for AND and | for OR — and wrap each condition in parentheses.
# Mumbai AND balance over 20,000
df[(df["city"] == "Mumbai") & (df["balance"] > 20000)]
# Mumbai OR Delhi
df[(df["city"] == "Mumbai") | (df["city"] == "Delhi")]
isin — match a list of values
df[df["city"].isin(["Mumbai", "Delhi"])]
and/or instead of &/|.
Python's and/or work on single booleans, not arrays — use &/|
(with parentheses) for element-wise filtering.
5. Interview Questions (with Model Answers)
The data-cleaning and filtering questions interviewers ask. Self-test before revealing.
IQ1. How do you check for missing values in a DataFrame?
Model answer: "I use df.isnull() (or isna()) to get a boolean
DataFrame, and df.isnull().sum() to count missing values per column. df.info() also
shows non-null counts."
IQ2. What's the difference between dropna and fillna?
Model answer: "dropna() removes rows or columns containing missing values;
fillna() replaces them with a specified value. One discards data, the other preserves it."
IQ3. When would you drop vs fill missing data?
Model answer: "I'd drop when missing rows are few and not critical, and fill when I need to keep the row. The method depends on why data is missing — and I'd never fill with the mean blindly, because it distorts the distribution."
IQ4. How do you remove duplicate rows?
Model answer: "With df.drop_duplicates(). I can use subset to define
which columns make a row a duplicate, and keep='last' to keep the last occurrence."
IQ5. How do you filter rows using boolean indexing?
Model answer: "I build a boolean mask and pass it inside brackets — like
df[df['balance'] > 50000]. It returns only the rows where the condition is True."
IQ6. How do you combine multiple filter conditions?
Model answer: "With & for AND and | for OR, wrapping each condition in
parentheses — df[(df['city']=='Mumbai') & (df['balance'] > 20000)]."
IQ7. What's the difference between == and isin for filtering?
Model answer: "== compares against one value; isin() matches against a
list of values. So df[df['city'].isin(['Mumbai','Delhi'])] is cleaner than chaining two
== conditions with |."
IQ8. How do you fill missing values with the column mean?
Model answer: "df['balance'].fillna(df['balance'].mean()) — I compute the column
mean and use it to fill its own missing values."
IQ9. Why is handling missing data important before analysis?
Model answer: "Because missing values distort statistics and can break operations — means get skewed, aggregations silently ignore or error on NaN, and models fail. Cleaning first prevents wrong conclusions."
Hands-On Project: Clean a Messy DataFrame
Using the sample DataFrame (with missing values and a duplicate), complete the following.
Steps
- Count the missing values per column.
- Remove the duplicate row.
- Fill missing
cityvalues with "Unknown". - Fill missing
balancevalues with the column mean. - Filter rows where balance is greater than 30,000.
- Filter rows in Mumbai or Delhi using
isin().
View Solution / Walkthrough
# 1. Missing counts
df.isnull().sum()
# 2. Remove duplicates
df = df.drop_duplicates()
# 3. Fill missing city
df["city"] = df["city"].fillna("Unknown")
# 4. Fill missing balance with mean
df["balance"] = df["balance"].fillna(df["balance"].mean())
# 5. Filter balance > 30000
high = df[df["balance"] > 30000]
# 6. Filter Mumbai or Delhi
subset = df[df["city"].isin(["Mumbai", "Delhi"])]
Key Takeaways
isnull().sum() is your first check for missing data.
dropna removes; fillna replaces — choose based on why data is missing.
drop_duplicates() with subset controls what counts as a duplicate.
Boolean indexing filters rows; use &/| (not and/or).
isin() matches a list of values cleanly.
Objective Questions — Test Your Understanding
Q1. How do you check for missing values in a DataFrame?
Q2. Which method removes rows with missing values?
Q3. Which method replaces missing values with a specified value?
Q4. How do you remove duplicate rows?
Q5. Which expression filters rows where balance is greater than 50,000?