Data Analysis with pandas · pandas Fundamentals
Cleaning Messy Data
Handle missing values, duplicates, inconsistent text and wrong types: the cleaning steps almost every real dataset needs.
What you'll build
A cleaning pipeline for a customer export with every common problem: inconsistent capitalisation and spaces, duplicate rows, missing values, numbers stored as text with currency symbols, and invalid dates.
The idea
- Missing values (
NaN,NaT,None): find withisna(), fill withfillna(), or drop withdropna(); decide per column. - Text cleanup with the
.straccessor:strip(),lower(),replace(). - Types:
pd.to_numeric(..., errors="coerce")andpd.to_datetime(..., errors="coerce")turn bad values into missing values instead of crashing. - Duplicates:
duplicated()anddrop_duplicates(subset=...).
The code
from io import StringIO
import pandas as pd
raw = pd.read_csv(StringIO('''email,country,spent,signup
[email protected] ,de,€120,2026-01-03
[email protected],US,€45.5,2026-01-05
[email protected],DE,€120,2026-01-03
[email protected],,unknown,2026-02-30
[email protected],fr,€80,
'''))
print("missing per column:", raw.isna().sum().to_dict())
clean = (
raw.assign(
email=raw["email"].str.strip().str.lower(),
country=raw["country"].str.upper().fillna("UNKNOWN"),
spent=pd.to_numeric(raw["spent"].str.replace("€", ""), errors="coerce"),
signup=pd.to_datetime(raw["signup"], errors="coerce"),
)
.drop_duplicates(subset="email")
.reset_index(drop=True)
)
print(clean)
print(clean.dtypes)
Output
missing per column: {'email': 0, 'country': 1, 'spent': 0, 'signup': 1}
email country spent signup
0 [email protected] DE 120.0 2026-01-03
1 [email protected] US 45.5 2026-01-05
2 [email protected] UNKNOWN NaN NaT
3 [email protected] FR 80.0 NaT
email str
country str
spent float64
signup datetime64[us]
dtype: objectCommon mistakes
dropna()on the whole table, silently deleting rows that are only missing an unimportant column.- Filling missing numbers with 0 when "unknown" isn't the same as "zero"; it skews averages.
- Removing duplicates before normalising text, so
[email protected]and[email protected]both survive. - Using
errors="ignore", which leaves the column as text; use"coerce"and inspect the resulting NaNs.
Key points
- Count missing values per column and decide how to handle each.
- Normalise text before deduplicating.
- Convert types with errors="coerce" and check what became missing.
Further reading: Working with missing data · Working with text data
Practice questions
What does errors='coerce' do in pd.to_numeric?
Answer
Values that can't be converted become NaN instead of raising an error, so you can count and inspect them.
Why normalise text before drop_duplicates?
Answer
Values that differ only in case or whitespace are different strings, so duplicates survive unless you normalise first.
When is filling missing values with 0 wrong?
Answer
When missing means unknown rather than zero; it biases sums and averages. Fill with a sensible value or keep NaN and handle it explicitly.
Open in the app to mark lessons complete, take notes and track your progress.