Data rarely comes in perfect format. Dates might be stored as text, numbers might have currency symbols, or ages might be stored as strings. This tutorial shows you how to fix wrong data formats in Pandas and convert everything to the correct type for analysis.
We'll cover dates, numbers, currency, percentages, and mixed formats with plenty of practical examples!
Sample Dataset for Practice
Let's create a messy dataset with various format issues. This is what real-world data often looks like!
import pandas as pd
import numpy as np
# Create dataset with format issues
data = {
'Employee_ID': ['E001', 'E002', 'E003', 'E004', 'E005', 'E006', 'E007', 'E008', 'E009', 'E010'],
'Name': ['Alice Smith', 'Bob Jones', 'Charlie Brown', 'Diana Prince', 'Eve Wilson', 'Frank Miller', 'Grace Lee', 'Henry Ford', 'Ivy Chen', 'Jack Ryan'],
'Age': ['28', '32', 'thirty-five', '29', '45', '33', '27', 'invalid', '38', '41'],
'Salary': ['$50,000', '52000', '$55,500.50', '48000', '$60,000', 'USD 53000', '49,500', '$70000.00', '51000', '62,500'],
'Bonus': ['10%', '15%', '12.5%', '8%', '20%', '10.5%', '15%', '18%', '12%', '14%'],
'Join_Date': ['2023-01-15', '15/03/2023', 'Jan 20, 2023', '2023.05.10', '2022-12-01', '2023/07/22', '20-09-2023', 'March 15, 2023', '2023-11-08', '01-Feb-2023'],
'Last_Login': ['2024-01-15 14:30:00', '2024-01-16 09:15', '2024/01/17 16:45:30', 'Jan 18, 2024 10:00', '2024-01-19', '2024-01-20T08:30:00', '21/01/2024 11:00', '2024.01.22 15:20', 'invalid', '2024-01-23'],
'Hours_Worked': ['40.5', '38', '42.75', '40', 'thirty-seven', '39.5', '41', '40.25', '38.5', '40'],
'Rating': ['4.5', '3.8', '4.2', '4.7', '3.9', 'four point five', '4.1', '4.3', '3.7', '4.6'],
'Amount': ['1,234.56', '2345.67', '3,456.78', '4567.89', '5,678.90', '6789.01', '7,890.12', '8901.23', '9,012.34', '10,123.45']
}
df = pd.DataFrame(data)
print(df)
Dataset Preview:
| Employee_ID | Age | Salary | Bonus | Join_Date |
|---|---|---|---|---|
| E001 | '28' ⚠️ | '$50,000' ⚠️ | '10%' ⚠️ | '2023-01-15' |
| E002 | '32' ⚠️ | '52000' ⚠️ | '15%' ⚠️ | '15/03/2023' ⚠️ |
| E003 | 'thirty-five' ⚠️ | '$55,500.50' ⚠️ | '12.5%' ⚠️ | 'Jan 20, 2023' ⚠️ |
💡 Notice: Everything looks like text (strings) when it should be numbers or dates!
1. Fix Numeric Format - Remove Currency Symbols
Salary columns often have $, commas, or currency codes. We need to strip these out and convert to numbers.
# Remove $ sign, commas, and 'USD' prefix
df['Salary_Clean'] = df['Salary'].str.replace('$', '', regex=False)
df['Salary_Clean'] = df['Salary_Clean'].str.replace(',', '', regex=False)
df['Salary_Clean'] = df['Salary_Clean'].str.replace('USD', '', regex=False)
df['Salary_Clean'] = df['Salary_Clean'].str.strip() # Remove extra spaces
# Convert to numeric
df['Salary_Clean'] = pd.to_numeric(df['Salary_Clean'], errors='coerce')
print("Before and After:")
print(df[['Salary', 'Salary_Clean']].head())
print(f"\nData type changed: {df['Salary'].dtype} → {df['Salary_Clean'].dtype}")
Example Output:
Before and After:
Salary Salary_Clean
0 $50,000 50000.00
1 52000 52000.00
2 $55,500.50 55500.50
3 48000 48000.00
4 $60,000 60000.00
Data type changed: object → float64
💡 Pro Tip: Use errors='coerce' to convert invalid values to NaN instead of throwing errors!
2. Clean All Currency in One Step
Here's a faster way to clean currency using regex (regular expressions):
# Remove all non-numeric characters except decimal point
df['Salary_Clean'] = df['Salary'].str.replace(r'[^\d.]', '', regex=True)
df['Salary_Clean'] = pd.to_numeric(df['Salary_Clean'], errors='coerce')
print("Cleaned Salaries:")
print(df[['Name', 'Salary', 'Salary_Clean']])
Real-World Use Case: Financial reports, e-commerce data, and accounting exports often have currency symbols that need cleaning!
3. Convert Percentages to Decimals
Percentage values like "10%" need to be converted to 0.10 for calculations.
# Remove % sign and convert to decimal
df['Bonus_Decimal'] = df['Bonus'].str.replace('%', '', regex=False)
df['Bonus_Decimal'] = pd.to_numeric(df['Bonus_Decimal'], errors='coerce') / 100
print("Percentage to Decimal:")
print(df[['Name', 'Bonus', 'Bonus_Decimal']].head())
# Calculate actual bonus amount
df['Bonus_Amount'] = df['Salary_Clean'] * df['Bonus_Decimal']
print("\nBonus Calculations:")
print(df[['Name', 'Salary_Clean', 'Bonus_Decimal', 'Bonus_Amount']].head())
Example Output:
Percentage to Decimal:
Name Bonus Bonus_Decimal
0 Alice Smith 10% 0.10
1 Bob Jones 15% 0.15
2 Charlie... 12.5% 0.125
Bonus Calculations:
Name Salary_Clean Bonus_Decimal Bonus_Amount
0 Alice Smith 50000.0 0.10 5000.0
1 Bob Jones 52000.0 0.15 7800.0
4. Fix Age Format - Handle Text Numbers
Age might be stored as text or have invalid entries. Let's clean it properly.
# Convert age to numeric (invalid values become NaN)
df['Age_Clean'] = pd.to_numeric(df['Age'], errors='coerce')
# Show which ages failed to convert
invalid_ages = df[df['Age_Clean'].isna()]
print("Invalid Age Values:")
print(invalid_ages[['Name', 'Age', 'Age_Clean']])
# Fill invalid ages with median age
median_age = df['Age_Clean'].median()
df['Age_Clean'].fillna(median_age, inplace=True)
print(f"\nInvalid ages replaced with median: {median_age}")
print("\nCleaned Ages:")
print(df[['Name', 'Age', 'Age_Clean']].head(10))
Example Output:
Invalid Age Values:
Name Age Age_Clean
2 Charlie Brown thirty-five NaN
7 Henry Ford invalid NaN
Invalid ages replaced with median: 33.0
Cleaned Ages:
Name Age Age_Clean
0 Alice Smith 28 28.0
2 Charlie Brown thirty-five 33.0 ← Fixed!
7 Henry Ford invalid 33.0 ← Fixed!
5. Convert Date Formats - The Right Way
Dates are tricky because they come in many formats. Pandas to_datetime() is smart enough to handle most formats automatically!
# Convert various date formats to standard datetime
df['Join_Date_Clean'] = pd.to_datetime(df['Join_Date'], errors='coerce')
print("Date Format Conversion:")
print(df[['Name', 'Join_Date', 'Join_Date_Clean']].head(8))
# Check data type
print(f"\nData type: {df['Join_Date_Clean'].dtype}")
# Now you can extract useful information!
df['Join_Year'] = df['Join_Date_Clean'].dt.year
df['Join_Month'] = df['Join_Date_Clean'].dt.month
df['Join_Day_Name'] = df['Join_Date_Clean'].dt.day_name()
print("\nExtracted Date Components:")
print(df[['Name', 'Join_Date_Clean', 'Join_Year', 'Join_Month', 'Join_Day_Name']].head())
Example Output:
Date Format Conversion:
Name Join_Date Join_Date_Clean
0 Alice Smith 2023-01-15 2023-01-15
1 Bob Jones 15/03/2023 2023-03-15 ← Converted!
2 Charlie... Jan 20, 2023 2023-01-20 ← Converted!
3 Diana Prince 2023.05.10 2023-05-10 ← Converted!
Data type: datetime64[ns]
Extracted Date Components:
Name Join_Date_Clean Join_Year Join_Month Join_Day_Name
0 Alice Smith 2023-01-15 2023 1 Sunday
1 Bob Jones 2023-03-15 2023 3 Wednesday
Real-World Use Case: Calculate employee tenure, analyze trends by month, filter by date ranges!
6. Handle DateTime with Time Components
When you have both date and time, clean them together!
# Convert datetime with time component
df['Last_Login_Clean'] = pd.to_datetime(df['Last_Login'], errors='coerce')
print("DateTime Conversion:")
print(df[['Name', 'Last_Login', 'Last_Login_Clean']].head())
# Extract time components
df['Login_Hour'] = df['Last_Login_Clean'].dt.hour
df['Login_Minute'] = df['Last_Login_Clean'].dt.minute
df['Login_Date_Only'] = df['Last_Login_Clean'].dt.date
df['Login_Time_Only'] = df['Last_Login_Clean'].dt.time
print("\nExtracted Time Components:")
print(df[['Name', 'Last_Login_Clean', 'Login_Hour', 'Login_Date_Only', 'Login_Time_Only']].head())
Example Output:
Extracted Time Components:
Name Last_Login_Clean Login_Hour Login_Date_Only Login_Time_Only
0 Alice Smith 2024-01-15 14:30 14 2024-01-15 14:30:00
1 Bob Jones 2024-01-16 09:15 9 2024-01-16 09:15:00
Pro Tip: Extract hour to analyze peak usage times, or day of week to find patterns!
7. Remove Commas from Large Numbers
Numbers with thousand separators (1,234.56) need cleaning before calculations.
# Remove commas from numbers
df['Amount_Clean'] = df['Amount'].str.replace(',', '', regex=False)
df['Amount_Clean'] = pd.to_numeric(df['Amount_Clean'], errors='coerce')
print("Before and After:")
print(df[['Name', 'Amount', 'Amount_Clean']].head())
# Now you can do calculations!
total_amount = df['Amount_Clean'].sum()
average_amount = df['Amount_Clean'].mean()
print(f"\nTotal Amount: ${total_amount:,.2f}")
print(f"Average Amount: ${average_amount:,.2f}")
Example Output:
Before and After:
Name Amount Amount_Clean
0 Alice Smith 1,234.56 1234.56
1 Bob Jones 2345.67 2345.67
2 Charlie... 3,456.78 3456.78
Total Amount: $54,999.95
Average Amount: $5,499.99
8. Convert Text to Proper Data Types
Sometimes you need to explicitly convert data types for better performance and accuracy.
# Convert Hours_Worked to float
df['Hours_Worked_Clean'] = pd.to_numeric(df['Hours_Worked'], errors='coerce')
# Convert Rating to float
df['Rating_Clean'] = pd.to_numeric(df['Rating'], errors='coerce')
# Convert Employee_ID to category (saves memory!)
df['Employee_ID'] = df['Employee_ID'].astype('category')
print("Data Types After Conversion:")
print(df[['Hours_Worked_Clean', 'Rating_Clean', 'Employee_ID']].dtypes)
print("\nMemory Usage Comparison:")
print(f"Before: object type uses more memory")
print(f"After: float64, category types use less memory")
Real-World Use Case: Converting to proper types speeds up calculations and reduces memory usage on large datasets!
9. Create a Complete Format Cleaning Function
Here's a reusable function that cleans all common format issues at once!
def clean_data_formats(df):
"""Clean common data format issues"""
print("=== CLEANING DATA FORMATS ===\n")
df_clean = df.copy()
# 1. Clean currency columns
currency_columns = ['Salary', 'Amount']
for col in currency_columns:
if col in df_clean.columns:
df_clean[f'{col}_Clean'] = df_clean[col].str.replace(r'[^\d.]', '', regex=True)
df_clean[f'{col}_Clean'] = pd.to_numeric(df_clean[f'{col}_Clean'], errors='coerce')
print(f" Cleaned {col}: Removed currency symbols and commas")
# 2. Clean percentage columns
percentage_columns = ['Bonus']
for col in percentage_columns:
if col in df_clean.columns:
df_clean[f'{col}_Decimal'] = df_clean[col].str.replace('%', '', regex=False)
df_clean[f'{col}_Decimal'] = pd.to_numeric(df_clean[f'{col}_Decimal'], errors='coerce') / 100
print(f" Cleaned {col}: Converted to decimal")
# 3. Clean numeric columns
numeric_columns = ['Age', 'Hours_Worked', 'Rating']
for col in numeric_columns:
if col in df_clean.columns:
df_clean[f'{col}_Clean'] = pd.to_numeric(df_clean[col], errors='coerce')
# Fill NaN with median
median_val = df_clean[f'{col}_Clean'].median()
df_clean[f'{col}_Clean'].fillna(median_val, inplace=True)
print(f" Cleaned {col}: Converted to numeric, filled missing with median")
# 4. Clean date columns
date_columns = ['Join_Date', 'Last_Login']
for col in date_columns:
if col in df_clean.columns:
df_clean[f'{col}_Clean'] = pd.to_datetime(df_clean[col], errors='coerce')
print(f" Cleaned {col}: Converted to datetime")
print("\n=== SUMMARY ===")
print(f"Original columns: {len(df.columns)}")
print(f"After cleaning: {len(df_clean.columns)}")
print(f"New clean columns created: {len(df_clean.columns) - len(df.columns)}")
return df_clean
# Use the function
df_final = clean_data_formats(df)
print("\n" + "="*50)
print(df_final.dtypes)
Example Output:
=== CLEANING DATA FORMATS ===
Cleaned Salary: Removed currency symbols and commas
Cleaned Amount: Removed currency symbols and commas
Cleaned Bonus: Converted to decimal
Cleaned Age: Converted to numeric, filled missing with median
Cleaned Hours_Worked: Converted to numeric, filled missing with median
Cleaned Rating: Converted to numeric, filled missing with median
Cleaned Join_Date: Converted to datetime
Cleaned Last_Login: Converted to datetime
=== SUMMARY ===
Original columns: 10
After cleaning: 18
New clean columns created: 8
10. Handle Special Cases - Mixed Formats
Sometimes one column has multiple format issues. Here's how to handle complex scenarios:
# Example: Phone numbers in different formats
phone_data = {
'Name': ['Alice', 'Bob', 'Charlie', 'Diana'],
'Phone': ['(123) 456-7890', '123-456-7890', '1234567890', '+1-123-456-7890']
}
df_phone = pd.DataFrame(phone_data)
# Clean phone numbers - keep only digits
df_phone['Phone_Clean'] = df_phone['Phone'].str.replace(r'[^\d]', '', regex=True)
# Format consistently as XXX-XXX-XXXX
df_phone['Phone_Formatted'] = df_phone['Phone_Clean'].str[:3] + '-' + \
df_phone['Phone_Clean'].str[3:6] + '-' + \
df_phone['Phone_Clean'].str[6:10]
print("Phone Number Cleaning:")
print(df_phone)
Example Output:
Phone Number Cleaning:
Name Phone Phone_Clean Phone_Formatted
0 Alice (123) 456-7890 1234567890 123-456-7890
1 Bob 123-456-7890 1234567890 123-456-7890
2 Charlie 1234567890 1234567890 123-456-7890
3 Diana +1-123-456-7890 11234567890 112-345-67890 ← Note: +1 issue!
Common Format Cleaning Patterns - Quick Reference
# Remove currency symbols and commas
df['price_clean'] = df['price'].str.replace(r'[^\d.]', '', regex=True)
df['price_clean'] = pd.to_numeric(df['price_clean'], errors='coerce')
# Convert percentage to decimal
df['percent_decimal'] = df['percent'].str.replace('%', '').astype(float) / 100
# Convert text numbers to numeric
df['age_clean'] = pd.to_numeric(df['age'], errors='coerce')
# Convert dates (handles multiple formats)
df['date_clean'] = pd.to_datetime(df['date'], errors='coerce')
# Remove commas from large numbers
df['number_clean'] = df['number'].str.replace(',', '').astype(float)
# Extract only digits
df['digits_only'] = df['text'].str.replace(r'[^\d]', '', regex=True)
# Convert to specific date format
df['date_formatted'] = df['date_clean'].dt.strftime('%Y-%m-%d')
# Fill invalid conversions with median/mean
df['column_clean'].fillna(df['column_clean'].median(), inplace=True)
Best Practices for Format Cleaning
- Always check data types first: Use
df.dtypesto see what needs fixing - Use errors='coerce': Converts invalid values to NaN instead of crashing
- Keep original columns: Create new cleaned columns so you can compare
- Validate after cleaning: Check min/max, unique values to ensure correctness
- Document your cleaning: Write comments explaining what you cleaned and why
- Handle missing values: Decide whether to fill, drop, or flag them
- Test on sample data: Try cleaning on a few rows first before the whole dataset
Remember:
- Use
pd.to_numeric()for numbers - Use
pd.to_datetime()for dates - Use
str.replace()for text cleaning - Use
errors='coerce'to handle invalid values gracefully - Always validate your cleaned data before analysis
Comments
Post a Comment