How to Remove Duplicate Rows in Pandas

pandas' drop_duplicates() handles whole-row deduplication out of the box, but the two parameters that make it flexible - subset and keep - are worth understanding properly before you run it on real data.

Quick answer

df.drop_duplicates() removes rows that are exact duplicates across every column, keeping the first occurrence by default. Use subset=['column'] to dedupe based on only specific columns, and keep='last' or keep=False to change which duplicate (if any) survives. Inspect duplicates first with df[df.duplicated(keep=False)] before deleting anything.

Need this for a plain list instead of a DataFrame?

See the full reference for Python, JavaScript, SQL, Google Sheets, and more.

See every language →

The default: whole-row deduplication

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Alice'],
    'email': ['alice@x.com', 'bob@x.com', 'alice@x.com'],
})

df_unique = df.drop_duplicates()
print(df_unique)
# Only the first Alice row remains - both columns matched exactly

Without a subset, drop_duplicates() considers every column - two rows only count as duplicates if all of their values match exactly. keep='first' is the default, so the first occurrence of each duplicate group survives.

Deduplicating on specific columns: subset

df_unique = df.drop_duplicates(subset=['email'])

Passing subset narrows the comparison to just the listed columns - two rows with the same email but a different name are still treated as duplicates and one is dropped, since only email is being compared.

Choosing which row survives: keep

df.drop_duplicates(subset=['email'], keep='last')   # keep the last occurrence
df.drop_duplicates(subset=['email'], keep=False)    # keep NEITHER - drop all involved

keep='last' is handy when your data is chronologically ordered and you want the most recent record per key. keep=False is different from the other two - instead of keeping one row per duplicate group, it removes every row that has any duplicate at all, leaving only genuinely unique rows behind.

Inspecting duplicates before deleting

dupes = df[df.duplicated(subset=['email'], keep=False)]
print(dupes)  # every row involved in a duplicate group, for review

duplicated() returns a boolean mask instead of dropping anything, using the same subset/keep logic as drop_duplicates(). Filtering the DataFrame with this mask first lets you see exactly what would be removed before committing to the deletion.

Ignoring case or whitespace when comparing

df['email_norm'] = df['email'].str.strip().str.lower()
df_unique = df.drop_duplicates(subset=['email_norm']).drop(columns=['email_norm'])

drop_duplicates() compares exact string values, so "Alice@X.com" and " alice@x.com " would not be treated as duplicates without normalizing first. Building a normalized helper column, deduping on that, then dropping the helper column keeps the original casing and whitespace in your final output.

Counting occurrences instead of removing them

print(df['email'].value_counts())

Sometimes you want to understand the duplication before acting on it - value_counts() returns how many times each distinct value appears, sorted from most to least frequent, which is useful for spotting which values are duplicated the most before deciding on a subset/keep strategy.

Just have a plain list, not a DataFrame? Paste it here.

Open Remove Duplicate Lines →

Working with a plain Python list instead of a DataFrame?

See the plain-list Python guide - dict.fromkeys(), sets, and dicts by key.

Read the Python guide →

Frequently Asked Questions

What is the standard way to remove duplicate rows in pandas?
df.drop_duplicates() removes rows that are exact duplicates across every column, keeping the first occurrence of each by default. It returns a new DataFrame rather than modifying the original in place unless you pass inplace=True.
How do I remove duplicates based on only some columns?
Pass the subset parameter: df.drop_duplicates(subset=['email']) considers two rows duplicates if they share the same email, regardless of whether other columns differ.
How do I control which duplicate row is kept?
The keep parameter controls this: keep='first' (the default) keeps the first occurrence, keep='last' keeps the last, and keep=False drops every row that has any duplicate at all, keeping none of them.
How do I see which rows are duplicates before deleting them?
df.duplicated() returns a boolean Series marking which rows are duplicates (by the same subset/keep rules as drop_duplicates), so df[df.duplicated(keep=False)] shows you every row involved in a duplicate group for review first.
How do I deduplicate ignoring case or whitespace differences?
drop_duplicates() compares exact values, so normalize first: df['email_norm'] = df['email'].str.strip().str.lower(), then df.drop_duplicates(subset=['email_norm']). Drop the helper column afterward if you don't need it.
How do I see how many times each value appears, not just remove duplicates?
df['column'].value_counts() returns a count of each distinct value, sorted from most to least frequent by default - useful when you want to understand the duplication pattern rather than just clean it up.
What if my data is a CSV export, not already in pandas?
Paste the raw list into the Remove Duplicate Lines tool for a quick browser-based dedup, or the Data Cleaner if you also need to normalize whitespace or formatting before deduplicating.

Share this tool

Featured on ToolDirs
© 2026 ListDiff

Free online list and text processing tools.