Data Cleaning with Pandas
Cleaning is a transformation from an observed schema to a declared schema. Make the transformation repeatable and make rejected data visible.
Pipeline
- Preserve raw input or its immutable location and provenance.
- Normalize column names and obvious text whitespace.
- Parse types with explicit formats and controlled failure behavior.
- Resolve duplicates and missing values using domain rules.
- Validate keys, ranges, categories, units, and cross-column invariants.
- Emit both cleaned data and a quality report.
clean = (
raw.rename(columns=str.strip)
.assign(
email=lambda x: x["email"].str.strip().str.lower(),
amount=lambda x: pd.to_numeric(x["amount"], errors="coerce"),
occurred_at=lambda x: pd.to_datetime(
x["occurred_at"], format="%Y-%m-%d", errors="coerce", utc=True
),
)
)
errors="coerce" turns parse failures into missing values; it does not make them
acceptable. Count and inspect those failures before continuing.
Text extraction
Use vectorized string methods before Python callbacks:
parts = clean["code"].str.extract(r"^(?P<prefix>[A-Z]+)-(?P<number>\d+)$")
clean = clean.join(parts)
Regular expressions should describe the accepted format, not merely find a substring that looks plausible. Keep the original column until validation passes.
Duplicates
Define the entity key and ordering before calling drop_duplicates:
clean = (
clean.sort_values("updated_at")
.drop_duplicates(subset=["entity_id"], keep="last")
)
This is a business rule. Record why the retained row is authoritative.
Validate explicitly
assert clean["entity_id"].notna().all()
assert clean["entity_id"].is_unique
assert clean["amount"].ge(0).all()
For production boundaries, prefer a reusable schema or validation layer over a collection of ad hoc assertions.