Data Cleaning With Python pandas: The 15 Fixes You Need for Real Excel Files

📎 This article includes 2 downloadable practice files ↓

⏱ 2 min readUpdated 28 September 2026

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. 1. Skip title rows and use the right header
  2. 2. Clean column names
  3. 3. Numbers stored as text
  4. 4. Dates in mixed formats
  5. 5. Trim and normalise text
  6. 6. Fix spelling variants with a map
  7. 7. Remove duplicates
  8. 8. Missing values: decide, don’t guess
  9. 9. Split one column into two
  10. 10. Extract with regex
  11. 11. Categorise numbers into bands
  12. 12. Flag outliers
  13. 13. Validate before saving
  14. 14. Save with a log of what changed
  15. 15. Wrap it in a reusable function
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()
💡 Run the function on every monthly file in a folder with pd.concat([clean_export(p) for p in Path("exports").glob("*.xlsx")]) — see merging Excel files with Python.

📎 Practice files for this article

  • 📗
    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.

✨ Ask AI about this article

Stuck on a step? Ask a question and the AI answers using this article.

Free · AI can be wrong