
π This article includes 2 downloadable practice files β
Real exports are messy: title rows above the header, numbers stored as text, three spellings of the same city. This guide collects the pandas fixes you will use again and again, in the order you usually apply them.
In this article
- 1. Skip title rows and use the right header
- 2. Clean column names
- 3. Numbers stored as text
- 4. Dates in mixed formats
- 5. Trim and normalise text
- 6. Fix spelling variants with a map
- 7. Remove duplicates
- 8. Missing values: decide, don’t guess
- 9. Split one column into two
- 10. Extract with regex
- 11. Categorise numbers into bands
- 12. Flag outliers
- 13. Validate before saving
- 14. Save with a log of what changed
- 15. Wrap it in a reusable function
- Continue learning
import pandas as pd
import numpy as np
1. Skip title rows and use the right header
df = pd.read_excel("export.xlsx", sheet_name="Sheet1", skiprows=3) # header is on row 4
df = df.dropna(how="all").dropna(axis=1, how="all") # remove fully blank rows/cols
2. Clean column names
df.columns = (df.columns.str.strip().str.lower()
.str.replace(r"[^\w]+", "_", regex=True).str.strip("_"))
# "Invoice No." -> invoice_no, "Amount (INR)" -> amount_inr
3. Numbers stored as text
df["amount_inr"] = pd.to_numeric(
df["amount_inr"].astype(str).str.replace(r"[βΉ,\s]", "", regex=True), errors="coerce")
errors="coerce" turns anything unreadable into NaN instead of crashing β then you can inspect those rows.
4. Dates in mixed formats
df["invoice_date"] = pd.to_datetime(df["invoice_date"], dayfirst=True, errors="coerce")
bad = df[df["invoice_date"].isna()] # rows that need a look
dayfirst=True matters in India: β03/04/2020β is 3 April, not 4 March.
5. Trim and normalise text
for col in ["customer", "city", "region"]:
df[col] = df[col].astype(str).str.strip().str.replace(r"\s+", " ", regex=True).str.title()
6. Fix spelling variants with a map
city_map = {"Bombay": "Mumbai", "Bangalore": "Bengaluru", "Gurgaon": "Gurugram", "Dilli": "Delhi"}
df["city"] = df["city"].replace(city_map)
7. Remove duplicates
df = df.drop_duplicates() # identical rows
df = df.sort_values("updated_at").drop_duplicates("invoice_no", keep="last") # latest per invoice
8. Missing values: decide, don’t guess
df.isna().sum() # where are the gaps?
df["region"] = df["region"].fillna("Unassigned")
df = df.dropna(subset=["invoice_no", "amount_inr"]) # rows useless without these
9. Split one column into two
df[["first_name", "last_name"]] = df["customer_name"].str.split(" ", n=1, expand=True)
10. Extract with regex
df["pan"] = df["remarks"].str.extract(r"([A-Z]{5}\d{4}[A-Z])")
df["mobile"] = df["contact"].str.extract(r"([6-9]\d{9})")
11. Categorise numbers into bands
df["size_band"] = pd.cut(df["amount_inr"], bins=[0, 10_000, 1_00_000, np.inf], labels=["Small", "Medium", "Large"])
12. Flag outliers
q1, q3 = df["amount_inr"].quantile([0.25, 0.75])
df["outlier"] = ~df["amount_inr"].between(q1 - 3 * (q3 - q1), q3 + 3 * (q3 - q1))
13. Validate before saving
assert df["invoice_no"].is_unique, "duplicate invoices!"
assert (df["amount_inr"] >= 0).all(), "negative amounts found"
14. Save with a log of what changed
with pd.ExcelWriter("clean.xlsx") as xl:
df.to_excel(xl, sheet_name="Clean", index=False)
bad.to_excel(xl, sheet_name="Check_dates", index=False)
15. Wrap it in a reusable function
def clean_export(path):
df = pd.read_excel(path, skiprows=3).dropna(how="all")
df.columns = df.columns.str.strip().str.lower().str.replace(r"[^\w]+", "_", regex=True)
df["amount_inr"] = pd.to_numeric(df["amount_inr"].astype(str).str.replace(r"[βΉ,\s]", "", regex=True), errors="coerce")
df["invoice_date"] = pd.to_datetime(df["invoice_date"], dayfirst=True, errors="coerce")
return df.drop_duplicates()
pd.concat([clean_export(p) for p in Path("exports").glob("*.xlsx")]) β see merging Excel files with Python.π Practice files for this article
β¬ Download all 2 files (ZIP Β· 7 KB)
- πMessy ERP export (.xlsx)Title rows, βΉ and commas in amounts, mixed dates, spaces, city spellings, duplicates and text dates β every problem in the guide.β¬ XLSX Β· 7 KB
- πFinished cleaning script (.py)Runs every fix from the guide and writes clean.xlsx with Check sheets for bad dates and duplicates.β¬ PY Β· 1 KB
Free to use for learning. Files with macros (.bas) are plain text β import them with Alt+F11 β File β Import File, and always test on a copy.
Atul (AJ) builds office-automation and local-first AI tools as Atulya AI, and works with Excel, SAP and Oracle EPM every day. He writes about the shortcuts, macros and automations that actually save time β plus the occasional wacky fact. Videos on YouTube: Lazy Automator AI (@LazyAutomatorAI).
More posts by Atul βStuck on a step? Ask a question and the AI answers using this article.