By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
You’re cleaning a dataset, and half the "dates" are in MM/DD/YYYY, some in DD-MM-YY, and a few in YYYYMMDD. A column that should be numeric has "$1,200" and "1.2K" mixed in. A text field contains email addresses, but some are malformed ("[email protected]"). If you don’t handle these inconsistencies, your analysis will fail—silently.
MM/DD/YYYY
DD-MM-YY
YYYYMMDD
"$1,200"
"1.2K"
"[email protected]"
This guide covers:- Strings: The raw material of text data—slicing, formatting, and cleaning.- Dates: Parsing, converting, and manipulating time data (critical for time-series analysis).- Regular Expressions (re): The Swiss Army knife for pattern matching—extracting emails, phone numbers, or structured data from messy text.
re
Why this matters in production:- Data pipelines break if you assume uniform formatting (e.g., pd.to_datetime() fails on "2023/13/01").- Regex is 10x faster than manual string splitting for large datasets (e.g., extracting domains from 1M URLs).- Time zones and daylight savings can skew your analysis if ignored (e.g., "Why does my daily sales report show two entries for March 12, 2023?").
pd.to_datetime()
"2023/13/01"
Real-world scenario:You’re building a customer analytics dashboard. The input CSV has: - A "joined_date" column with mixed formats ("Jan 5, 2020", "05-01-20", "20200105").- A "spend" column with "$1,200", "1.2K", and "500".- A "notes" field containing email addresses and phone numbers you need to extract.
"joined_date"
"Jan 5, 2020"
"05-01-20"
"20200105"
"spend"
"500"
"notes"
Your job: Clean this data before feeding it to pandas or a machine learning model.
pandas
str
.split()
.strip()
.replace()
.lower()
" [email protected] "
f"User {name} joined on {date}"
+
.format()
text[0:5]
text[-5:]
"data.csv"[-3:]
"csv"
datetime
datetime.datetime
datetime.date
datetime.timedelta
strftime
strptime
%Y-%m-%d
timedelta(days=1)
re.compile()
\d
\w
\s
^
$
r"\d{3}"
"123"
"a123b"
r"^\d{3}$"
re.search()
re.match()
re.findall()
search()
match()
findall()
pip install pandas
messy_data.csv
csv user_id,joined_date,spend,notes 1,Jan 5, 2020,$1,200,"Email: [email protected], Phone: 555-1234" 2,05-01-20,1.2K,"Email: [email protected], Phone: 555-5678" 3,20200105,500,"Email: [email protected], Phone: 555-9012"
import pandas as pd df = pd.read_csv("messy_data.csv") print(df.head())
Output:
user_id joined_date spend notes 0 1 Jan 5, 2020 $1,200 Email: [email protected], Phone: 555-1234 1 2 05-01-20 1.2K Email: [email protected], Phone: 555-5678 2 3 20200105 500 Email: [email protected], Phone: 555-9012
joined_date
Problem: Mixed formats ("Jan 5, 2020", "05-01-20", "20200105").
Solution: Use pd.to_datetime() with errors="coerce" to handle failures.
errors="coerce"
df["joined_date"] = pd.to_datetime( df["joined_date"], format="mixed", # Auto-detects formats (Python 3.11+) errors="coerce" # Invalid dates become NaT (Not a Time) ) print(df["joined_date"])
0 2020-01-05 1 2020-05-01 2 2020-01-05 Name: joined_date, dtype: datetime64[ns]
Why this works:- format="mixed" (Python 3.11+) tries multiple formats automatically.- errors="coerce" prevents crashes on invalid dates (e.g., "2023/13/01" becomes NaT).
format="mixed"
NaT
spend
Problem: "$1,200", "1.2K", "500" need to be converted to floats.
Solution: Use regex to extract numbers and handle "K" suffixes.
import re def clean_spend(value): # Remove $ and commas value = re.sub(r"[$,]", "", str(value)) # Handle "1.2K" -> 1200 if "K" in value: return float(value.replace("K", "")) * 1000 return float(value) df["spend"] = df["spend"].apply(clean_spend) print(df["spend"])
0 1200.0 1 1200.0 2 500.0 Name: spend, dtype: float64
Why this works:- re.sub(r"[$,]", "", value) removes $ and , in one pass.- "K" suffixes are converted to thousands (e.g., "1.2K" → 1200).
re.sub(r"[$,]", "", value)
,
"K"
1200
notes
Problem: Extract all valid emails and phone numbers from the notes column.
Solution: Use regex with re.findall().
# Extract emails df["email"] = df["notes"].str.findall(r"[\w\.-]+@[\w\.-]+\.\w+") # Extract phone numbers (US format: 555-1234 or 555-123-4567) df["phone"] = df["notes"].str.findall(r"\d{3}-\d{3,4}") print(df[["email", "phone"]])
email phone 0 [[email protected]] [555-1234] 1 [] [555-5678] 2 [[email protected]] [555-9012]
Why this works:- Email regex: [\w\.-]+@[\w\.-]+\.\w+ matches [email protected].- Phone regex: \d{3}-\d{3,4} matches 555-1234 or 555-123-4567.- str.findall() returns a list of all matches (empty if none found).
[\w\.-]+@[\w\.-]+\.\w+
[email protected]
\d{3}-\d{3,4}
555-1234
555-123-4567
str.findall()
df.to_csv("cleaned_data.csv", index=False)
df["column"] = df["column"].str.strip()
f"{var1}{var2}"
.join()
.upper()
"USA"
"usa"
df["date"] = df["date"].dt.tz_localize("UTC")
timedelta(days=7)
pattern = re.compile(r"\d{3}-\d{3}")
.*?
.*
r"<.*?>"
<tag>
<tag>content</tag>
r"<.*>"
dt.tz_localize("UTC")
"How do you extract the domain from an email ([email protected] → example.com)?" Answer: email.split("@")[-1] or re.search(r"@(\w+\.\w+)", email).group(1).
example.com
email.split("@")[-1]
re.search(r"@(\w+\.\w+)", email).group(1)
Date parsing:
"Convert "01/02/2023" to a datetime object." Answer: pd.to_datetime("01/02/2023", format="%m/%d/%Y") (note: %d/%m/%Y is a common trap).
"01/02/2023"
pd.to_datetime("01/02/2023", format="%m/%d/%Y")
%d/%m/%Y
Regex:
r"\d{3}-\d{3}-\d{4}"
datetime.now()
datetime.now(timezone.utc)
Challenge:Extract all valid email addresses from the following string:
text = "Contact us at [email protected] or [email protected]. Invalid: [email protected], @domain.com"
Solution:
import re emails = re.findall(r"[\w\.-]+@[\w\.-]+\.\w+", text) print(emails) # Output: ['[email protected]', '[email protected]']
Why it works:- [\w\.-]+ matches the username (letters, numbers, ., -).- @[\w\.-]+ matches the domain (e.g., @example).- \.\w+ matches the TLD (e.g., .com).
[\w\.-]+
.
-
@[\w\.-]+
@example
\.\w+
.com
text.strip()
text.split(",")
f"Value: {var}"
text.lower()
pd.to_datetime("2023-01-01")
df["date"].dt.strftime("%Y-%m-%d")
re.compile(r"\d{3}")
re.findall(r"\w+", text)
re.sub(r"\s+", " ", text)
⚠️ Exam Traps:- re.match() only checks the start of the string.- pd.to_datetime() defaults to errors="raise" (use errors="coerce" to avoid crashes).- Timezone-naive datetime objects cause silent bugs (always use UTC).
errors="raise"
to_datetime()
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.