Identify and Remove Duplicate Rows
Master identifying and removing duplicate rows in Python with pandas. This lesson offers a hands-on tutorial, practical steps, troubleshooting tips, and next steps in the Data Analysis with Python track.
Focus: identify and remove duplicate rows
You’ve spent hours cleaning a dataset—fixing types, renaming columns, filling missing values—and then you run a summary statistic and realize the numbers look… off. The same customer appears twice, the same transaction is logged three times, and your counts are inflated. Duplicate rows are silent saboteurs: they skew averages, overstate frequencies, and derail any analysis built on top of them. In this lesson, you’ll learn how to identify and remove duplicate rows in pandas quickly and accurately, so your downstream analysis is built on clean, trustworthy data.
The problem this lesson solves
Duplicate rows are more common than you think. They sneak in from:
- Merged datasets — joining multiple sources often creates repeated records
- Manual data entry — a user submits the same form twice
- Web scraping — refreshing a page can re-scrape identical rows
- Sensor logs — devices can send duplicate readings
Why does this matter? If you failed to remove duplicates, consider what happens:
- Aggregations lie:
sum(),mean(), andcount()treat each duplicate as a separate observation, inflating results - Relationships break: joining on a key that has duplicates produces a Cartesian explosion
- Machine learning degrades: models learn from overrepresented rows, biasing predictions
- Reports mislead: stakeholders make decisions based on wrong numbers
In short, duplicates poison every step of the data analysis pipeline. The longer you wait, the more costly the bug becomes. By the end of this lesson, you’ll be able to identify them, remove them, and keep your analysis honest.
Core concept / mental model
Think of your DataFrame as a database table. Each row represents an observation (a customer, a sale, a reading). A duplicate is a row that repeats the same information—either exactly or for a subset of columns you care about.
Pandas gives you two core tools:
duplicated()— a detective: flags all rows that are duplicates (returns a boolean Series)drop_duplicates()— a cleaner: removes those flagged rows (or keeps them, depending on how you set it up)
Here’s the mental image: imagine a guest list. If two people arrive with the exact same name and address, you have a duplicate. If you only care about names (maybe two different guests share a name), you can decide to treat one as a duplicate based on just that column. The default is to consider all columns—only rows that are identical across every field are considered duplicates.
Pro tip: The first occurrence is kept by default. The
keepparameter lets you control which one to keep—first, last, or none.
How it works step by step
Let’s walk through the process logically.
Step 1: Inspect your data
Before removing anything, always look at what you’re dealing with. Use df.head() and df.shape to get a feel for the size and structure.
Step 2: Identify duplicates
Call df.duplicated() to get a boolean Series. Rows that are duplicates (occurring after the first occurrence) are marked True. You can quickly count them with .sum().
But not all duplicates are exact. Sometimes you only care about a subset of columns—like customer_id or order_id. Use the subset parameter to specify those columns.
Step 3: Decide on a removal strategy
- Keep the first (
keep='first') — the most common choice; keeps the earliest record - Keep the last (
keep='last') — useful if later entries are more complete - Drop all duplicates (
keep=False) — if any duplicate exists, remove all copies; good for audit scenarios
Step 4: Remove and verify
Call drop_duplicates() (or drop_duplicates(subset=...)) to create a cleaned DataFrame. Then re-check with duplicated().sum() to confirm zero duplicates remain.
Step 5: Decide whether to mutate or copy
drop_duplicates() returns a new DataFrame by default. If you want to modify the original in place, set inplace=True—but be careful, in-place operations are often discouraged for clarity.
The key is to do no harm: copy, inspect, remove, and verify.
Hands-on walkthrough
Let’s put it all together with a real example.
Prepare the data
First, create a sample DataFrame with some duplicates.
import pandas as pd
# Sample data with duplicates
data = {
'customer_id': [101, 102, 101, 103, 102, 104],
'name': ['Alice', 'Bob', 'Alice', 'Charlie', 'Bob', 'Diana'],
'purchase_amount': [250, 150, 250, 300, 150, 400]
}
df = pd.DataFrame(data)
print("Original shape:", df.shape)
print(df)
Expected output:
Original shape: (6, 3)
customer_id name purchase_amount
0 101 Alice 250
1 102 Bob 150
2 101 Alice 250
3 103 Charlie 300
4 102 Bob 150
5 104 Diana 400
Rows 0 and 2 are identical, and rows 1 and 4 are identical.
Identify duplicates (all columns)
# Flag duplicate rows
duplicate_flags = df.duplicated()
print("Duplicate flags:")
print(duplicate_flags)
print("\nNumber of duplicates:", duplicate_flags.sum())
Expected output:
Duplicate flags:
0 False
1 False
2 True
3 False
4 True
5 False
dtype: bool
Number of duplicates: 2
Remove duplicates (exact matches)
# Drop exact duplicates, keeping the first occurrence
clean_df = df.drop_duplicates()
print("Cleaned shape:", clean_df.shape)
print(clean_df)
Expected output:
Cleaned shape: (4, 3)
customer_id name purchase_amount
0 101 Alice 250
1 102 Bob 150
3 103 Charlie 300
5 104 Diana 400
Identify duplicates based on a subset of columns
Sometimes you only care about, say, customer_id. If a customer appears twice with different amounts, you might want to treat the second as a duplicate.
# Flag duplicates based on customer_id only
subset_flags = df.duplicated(subset=['customer_id'])
print("\nDuplicates based on customer_id:")
print(subset_flags)
print("Duplicates count:", subset_flags.sum())
Expected output:
Duplicates based on customer_id:
0 False
1 False
2 True
3 False
4 True
5 False
dtype: bool
Duplicates count: 2
Remove duplicates on a subset, keeping the last occurrence
clean_subset_last = df.drop_duplicates(subset=['customer_id'], keep='last')
print("\nAfter dropping (subset, keep last):")
print(clean_subset_last)
Expected output:
After dropping (subset, keep last):
customer_id name purchase_amount
2 101 Alice 250
4 102 Bob 150
3 103 Charlie 300
5 104 Diana 400
Drop all duplicate rows (keep none)
# Drop every row that is part of a duplicate set
unique_only = df.drop_duplicates(keep=False)
print("\nRows without any duplicates:")
print(unique_only)
Expected output:
Rows without any duplicates:
customer_id name purchase_amount
3 103 Charlie 300
5 104 Diana 400
Hands-on tip: After cleaning, always confirm with
duplicated().sum() == 0.
Compare options / when to choose what
Different scenarios call for different keep and subset choices. Here’s a quick table:
| Scenario | Recommended approach | Reason |
|---|---|---|
| Transaction log with exact repeats | drop_duplicates() |
Removes exact duplicates, keeps the first reliable record |
| Customer data where only ID matters | drop_duplicates(subset=['customer_id']) |
Newer updates should override old rows (use keep='last') |
| Audit or fraud detection | drop_duplicates(keep=False) |
Flags that any duplicate existed; leaves only genuinely unique records |
| Data with timestamps | Combine with sorting: sort_values('timestamp') then drop_duplicates(subset=['id']) |
Ensures the most recent record wins |
Alternatives to pandas' built-in methods include:
df[~df.duplicated()]— manual boolean filtering, useful in complex conditionsgroupby().first()or.last()— when you need a specific aggregate after deduplication- SQL
DISTINCT— if you move to a database, it’s the same concept
Troubleshooting & edge cases
Even with simple tools, things can go sideways. Here are common pitfalls and fixes.
Empty DataFrame after drop_duplicates(keep=False)
If you use keep=False on a dataset where every row appears at least twice, you’ll get an empty DataFrame. That’s expected but can be surprising. Check your data first:
print(df.duplicated().value_counts())
subset column contains NaN
Pandas treats NaN as a value; two rows with NaN in the subset column are considered duplicates. If you want to treat missing values as distinct, fill them before deduplication.
Fix: Use df[subset_col] = df[subset_col].fillna('') or a sentinel value.
Duplicates not removed because of invisible differences
A trailing space ('Alice ' vs 'Alice') or differing case can make two rows look unique. Normalize text first:
df['name'] = df['name'].str.strip().str.lower()
# then drop duplicates
inplace=True not working as expected
inplace=True modifies the original object, but it is easy to misuse. If you see no change, you may be calling it on a slice. Prefer returning a new object:
df_clean = df.drop_duplicates() # safer, more readable
Duplicates keep reappearing in resampled data
If you join multiple tables and duplicates reappear, it’s often because you didn’t deduplicate the source tables before joining. Clean upstream first.
What you learned & what's next
You’ve mastered the essential skill of identifying and removing duplicate rows in pandas. You now know:
- How to use
duplicated()to flag duplicates and count them - How to use
drop_duplicates()to remove them, with control overkeepandsubset - How to choose between exact and subset-based deduplication
- How to troubleshoot common issues like text inconsistencies and
NaNbehavior
These are the building blocks of clean data—and clean data is the foundation of every reliable analysis. Next in the track, you’ll learn how to handle missing values with isna() and fillna(), another critical cleaning step. With duplicates gone, you’ll be ready to tackle incomplete data head-on.
Now that you can identify and remove duplicate rows, you’re one step closer to producing analysis that stakeholders can trust. Keep practicing—every dataset you clean makes you faster and more confident.
Practice recap
Practice by creating a small DataFrame with both exact and partial duplicates, then remove them using both keep='first' and keep='last' with a subset parameter. Check your work with df.duplicated().sum(). Next lesson: handling missing values with isna() and fillna().
Common mistakes
- Forgetting to check if duplicates exist before cleaning—use
df.duplicated().sum()to get a baseline. - Using
keep=Falsewhen you actually want to keep one row per duplicate set—this can wipe out legitimate records if every row appears twice. - Not using the
subsetparameter when you only care about a few columns—you'll only remove exact matches, leaving duplicates that vary in other fields. - Ignoring text inconsistencies (spaces, case) that prevent exact match detection—always
strip()andlower()your text before deduplication. - Calling
drop_duplicates()on a slice or a single column and expecting the original to change—make sure you assign the result or useinplace=Truecorrectly.
Variations
- Use boolean indexing with
df[~df.duplicated(subset=['id'])]for more control in complex pipelines. - Combine
sort_values()anddrop_duplicates(subset=['id'], keep='last')to keep the most recent record based on a timestamp. - Use
groupby('id').first()or.last()to achieve deduplication with additional aggregation logic.
Real-world use cases
- Cleaning a CRM export where the same customer appears multiple times due to repeated imports, using
drop_duplicates(subset='customer_id', keep='last'). - Deduplicating web-scraped product listings by URL or SKU to avoid inflating price stats and conversion metrics.
- Preparing training data for a machine learning model by removing exact duplicate rows to prevent data leakage and overfitting.
Key takeaways
df.duplicated()returns a boolean Series that flags duplicate rows; sum it to count them.drop_duplicates()removes duplicates; usekeep='first'(default),keep='last', orkeep=Falseas needed.- The
subsetparameter lets you deduplicate based on specific columns—essential when exact duplicates aren't the whole story. - Clean your data before aggregations and joins—duplicates silently inflate counts and skew results.
- Always verify after cleaning with
duplicated().sum() == 0to ensure you've succeeded. - Watch for invisible differences like trailing spaces and case—normalize text before deduplication.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.