Case & Whitespace Cleaning
Standardise text with lower, upper, title and capitalize, and remove unwanted spaces with strip, lstrip, rstrip and a regex that collapses repeated spaces.
Watch the lesson
1 video on CodeWithMunnaX
To a human, "Mumbai", "mumbai", " MUMBAI " and "Mumbai " are the same city. To pandas they are four different values. value_counts(), groupby(), drop_duplicates() and merge() all treat them separately, so your "matches per city" table is silently wrong. Case and whitespace normalisation is the first thing to do with any text column — Munna uses ipl['venue'].str.lower() in the IPL notebook for exactly this check.
Definition
Case normalisation converts every string to one consistent letter case (e.g. Title Case), and whitespace trimming removes spaces, tabs and newlines from the start and end of each string, so that values which mean the same thing become exactly equal.
- Consistent letter case — pick one style per column:
lowerfor codes/emails,titlefor names and cities,upperfor short codes likeMI. - Start and end —
strip()does not touch spaces inside the string ("Eden Gardens"keeps both spaces). Use a regex replace for those. - Exactly equal — the goal is that equal meaning ⇒ equal string.
Syntax
python
s.str.lower() s.str.upper() s.str.title()
s.str.capitalize() s.str.swapcase() s.str.casefold()
s.str.strip(to_strip=None) # both ends
s.str.lstrip(to_strip=None) # left end only
s.str.rstrip(to_strip=None) # right end only
s.str.replace(r"\s+", " ", regex=True) # collapse inner spaces| Method | Result for "virat KOHLI" | Typical use |
|---|---|---|
lower() | virat kohli | comparisons, emails, matching |
upper() | VIRAT KOHLI | codes (MI, CSK) |
title() | Virat Kohli | names, cities, venues |
capitalize() | Virat kohli | sentences (first letter only) |
swapcase() | VIRAT kohli | rarely needed |
casefold() | virat kohli | like lower() but stronger for non-English letters (ß → ss) |
| Parameter | Meaning | Default |
|---|---|---|
to_strip | a string of characters to remove from the ends (each character, any order) | None = whitespace |
Example — case changes
python
names = pd.Series(["virat KOHLI", "ROHIT sharma", "ms dhoni", "AB de Villiers"])
print(pd.DataFrame({
"lower": names.str.lower(),
"upper": names.str.upper(),
"title": names.str.title(),
"capitalize": names.str.capitalize(),
}))text
lower upper title capitalize
0 virat kohli VIRAT KOHLI Virat Kohli Virat kohli
1 rohit sharma ROHIT SHARMA Rohit Sharma Rohit sharma
2 ms dhoni MS DHONI Ms Dhoni Ms dhoni
3 ab de villiers AB DE VILLIERS Ab De Villiers Ab de villiersHow it works: title() capitalises the first letter of every word and lowercases the rest — notice "AB de Villiers" becomes "Ab De Villiers" and "ms dhoni" becomes "Ms Dhoni". Title case is great for cities but can damage names with special capitalisation; fix those few with replace afterwards.
Example — the counting problem, before and after
python
cities = pd.Series(["Mumbai", "mumbai", " MUMBAI ", "Mumbai ", "Delhi", "delhi "])
print(cities.value_counts())
print()
clean = cities.str.strip().str.title()
print(clean.value_counts())text
Mumbai 1
mumbai 1
MUMBAI 1
Mumbai 1
Delhi 1
delhi 1
Name: count, dtype: int64
Mumbai 4
Delhi 2
Name: count, dtype: int64How it works: before cleaning, pandas sees 6 different values. After strip() (remove edge spaces) and title() (one case style) there are just two cities with the right counts.
Example — strip, lstrip, rstrip and to_strip
python
raw = pd.Series([" Eden Gardens ", "**Wankhede**", "...Chepauk", "Kotla\n"])
print(raw.str.strip().tolist())
print(raw.str.lstrip().tolist())
print(raw.str.rstrip().tolist())
print(raw.str.strip("*.").tolist())text
['Eden Gardens', '**Wankhede**', '...Chepauk', 'Kotla']
['Eden Gardens ', '**Wankhede**', '...Chepauk', 'Kotla\n']
[' Eden Gardens', '**Wankhede**', '...Chepauk', 'Kotla']
[' Eden Gardens ', 'Wankhede', 'Chepauk', 'Kotla\n']How it works:
strip()with no argument removes whitespace (spaces, tabs\t, newlines\n) from both ends.lstrip()/rstrip()only clean the left / right end.strip("*.")removes any*or.characters from the ends — the argument is a set of characters, not a whole word.
Example — spaces inside the string
strip() never touches inner spaces. Use a regular expression: \s+ means "one or more whitespace characters".
python
venues = pd.Series(["Eden Gardens", " M Chinnaswamy Stadium ", "Wankhede\tStadium"])
fixed = venues.str.replace(r"\s+", " ", regex=True).str.strip()
print(fixed.tolist())text
['Eden Gardens', 'M Chinnaswamy Stadium', 'Wankhede Stadium']Example — case-insensitive comparison without changing the data
Sometimes you want to compare ignoring case but keep the original text:
python
teams = pd.Series(["Mumbai Indians", "MUMBAI INDIANS", "Chennai Super Kings"])
print(teams[teams.str.lower() == "mumbai indians"])
print(teams.str.lower().nunique(), "unique teams ignoring case")text
0 Mumbai Indians
1 MUMBAI INDIANS
dtype: str
2 unique teams ignoring caseThis is the check from Munna's notebook: ipl3['venue'].str.lower().nunique() — if it is smaller than nunique() on the raw column, you have case duplicates.
Common mistakes
- ❌ Expecting
strip()to remove spaces between words. ✅str.replace(r"\s+", " ", regex=True). - ❌
strip("Mr. ")to remove a prefix — it removes the charactersM,r,., space from both ends, which can eat real letters. ✅ Usestr.removeprefix("Mr. ")or a regex replace with^. - ❌ Cleaning only for display, then grouping on the dirty column. ✅ Assign it back:
df["city"] = df["city"].str.strip().str.title(). - ❌ Blindly title-casing names like
"AB de Villiers"or"MS Dhoni". ✅ Title-case, then fix exceptions withreplace.
Comparison — which case for which column?
| Column | Recommended | Why |
|---|---|---|
| city, venue, player name | title() | readable |
| team short code | upper() | MI, CSK are acronyms |
| email, username, matching keys | lower() / casefold() | case does not matter |
Interview tip
Q: value_counts() shows Mumbai and mumbai separately. How do you fix it?
A: Normalise first — df["city"] = df["city"].str.strip().str.title() — then count again.
Key points to remember
- Text that means the same must be exactly equal, or value_counts/groupby/merge split it.
- lower(), upper(), title(), capitalize(), swapcase() and casefold() change letter case.
- strip() removes whitespace from both ends; lstrip()/rstrip() clean one side only.
- strip('*.') removes a SET of characters from the ends, not a whole word.
- strip() never touches inner spaces — use str.replace(r'\s+', ' ', regex=True) to collapse them.
- Always assign the cleaned column back: df['city'] = df['city'].str.strip().str.title().
Try it yourself
One city, five spellings
All the case methods side by side
Practice: tidy messy venue names
Revision Flashcards
Tap a card to flip it and see the answer.
