What you'll learn
- Fix types, missing values and duplicates
- Normalise text columns with the .str accessor
- Convert dirty text to numbers with pd.to_numeric and errors="coerce"
- Wrap cleaning steps in a rerunnable function that leaves the raw data untouched
Fix text, types, missing values and duplicates with a rerunnable pandas cleaning function.
Why it matters
Real data is messy. Survey answers have stray spaces, sensor logs contain "ERR", exports duplicate rows, and somebody typed "unknown" into a number column. Cleaning is often most of the work in an analysis, and mistakes here quietly change every result after it. The goal is a clean table produced by code you can rerun, never by hand edits.
Find the problems first
df.info() # dtypes and non-null counts
df.isna().sum() # missing values per column
df.duplicated().sum() # fully duplicated rows
df["city"].value_counts() # spelling and spacing variantsSuppose the data looks like this:
city temp_c
0 Boston 21.5
1 boston 22.0
2 Denver ERR
3 Boston 21.5
4 Austin 30.25Problems: "boston" and " Boston" differ in case and spacing, temp_c is text because of "ERR" (pandas already reads empty cells, NA and n/a as missing, but not arbitrary codes), and row 3 may duplicate row 0 once the city is cleaned.
Fixing text: the .str accessor
String methods on a whole column live under .str:
df["city"] = df["city"].str.strip().str.title()
# " boston " -> "Boston"Other useful ones: .str.lower(), .str.replace("$", "", regex=False), .str.contains("lab"), .str.split("-").
Fixing types
pd.to_numeric converts text to numbers. With errors="coerce", anything that cannot be converted becomes NaN (missing) instead of raising an error:
df["temp_c"] = pd.to_numeric(df["temp_c"], errors="coerce")Strip units or symbols first if needed:
df["price"] = pd.to_numeric(df["price"].str.replace("$", "", regex=False), errors="coerce")For dates, use pd.to_datetime(df["date"], errors="coerce"). Use .astype(int) only when you are sure there are no missing values.
Handling missing values
NaN means "no value". Most pandas statistics skip it (mean() ignores NaNThe marker pandas uses for a missing value; most statistics such as mean() skip it.), but you still have to decide what missing means in your data.
df.dropna(subset=["temp_c"]) # drop rows missing temp_c
df["temp_c"].fillna(0) # fill with a value (rarely right for measurements)
df["temp_c"].fillna(df["temp_c"].median()) # fill with the medianRules of thumb:
- Drop when the row cannot be used without that value and there are few such rows.
- Fill when a sensible value exists, such as 0 for "no sales recorded", and say so in your notes.
- Keep as NaN when later steps can skip it and dropping would throw away useful columns.
- Count before and after. Report how many rows you removed and why.
Removing duplicates
df = df.drop_duplicates() # rows identical in every column
df = df.drop_duplicates(subset=["order_id"]) # one row per order_id (keeps the first)Clean text before removing duplicates, otherwise "Boston" and "boston " look different and survive.
Putting it in a function
Wrap the steps in a function that returns a new DataFrame, so the raw data stays untouched and you can rerun the cleaning:
def clean_temps(raw):
df = raw.copy()
df["city"] = df["city"].str.strip().str.title()
df["temp_c"] = pd.to_numeric(df["temp_c"], errors="coerce")
df = df.dropna(subset=["temp_c"])
df = df.drop_duplicates()
return df.reset_index(drop=True)reset_index(drop=True) renumbers rows 0, 1, 2, ... after rows were removed.
Common mistakes
- Editing the original in place without meaning to. Start with
df = raw.copy(). - Assigning to a filtered slice.
df[df["x"] > 0]["y"] = 1may not changedf. Usedf.loc[df["x"] > 0, "y"] = 1. - Filling measurements with 0. A missing temperature is not 0 degrees.
- Deduplicating before normalising text. Near-duplicates survive.
- Silently dropping rows. Always know how many rows you lost.
Quick recap
- Inspect with
info,isna().sum(),duplicated().sum()andvalue_counts(). .str.strip().str.title()normalises text;pd.to_numeric(..., errors="coerce")fixes numbers.- Decide per column whether to drop, fill or keep missing values, and record the choice.
- Normalise text first, then
drop_duplicates, inside a function that returns a copy.
Try it
In the Do tab you will write clean(df) for a messy city-temperature table: fix the city names, convert temperatures, drop unusable rows and remove duplicates.
Key terms (5)
- .str accessor
- The pandas attribute that applies string methods such as strip, title and replace to every value in a column.
- pd.to_numeric
- A pandas function that converts values to numbers; with errors="coerce" anything unconvertible becomes NaN.
- NaN
- The marker pandas uses for a missing value; most statistics such as mean() skip it.
- drop_duplicates
- A pandas method that removes repeated rows, comparing all columns or only those given in subset.
- reset_index
- A pandas method that renumbers rows 0, 1, 2 and so on; drop=True discards the old labels.