本文目录导读:

数据清洗是数据分析中最耗时但最重要的环节,Python 的 Pandas 库是处理这类问题的首选工具。
以下我为你整理了 10 个高频且适合作为练习案例的数据清洗场景,每个都附带核心代码和解决思路。
处理缺失值
场景:数据集中有 NaN 或空字符串。
常见操作:
- 删除含有缺失值的行/列
- 填充缺失值(均值、中位数、众数、前向/后向填充)
核心案例:
import pandas as pd
import numpy as np
df = pd.DataFrame({
'A': [1, 2, np.nan, 4],
'B': [5, np.nan, np.nan, 8],
'C': ['x', 'y', None, 'z']
})
# 1. 检查缺失值
print(df.isnull().sum())
# 2. 删除任何含有缺失值的行
df_drop = df.dropna()
# 3. 填充数值列用均值,类别列用众数
df['A'].fillna(df['A'].mean(), inplace=True)
df['C'].fillna(df['C'].mode()[0], inplace=True) # 众数可能有多个,取第一个
# 4. 前向填充(时间序列常用)
df['B'].fillna(method='ffill', inplace=True)
去除重复数据
场景:数据中存在完全重复或部分列重复的行。
核心案例:
df = pd.DataFrame({
'ID': [1, 2, 2, 3, 3],
'Name': ['Alice', 'Bob', 'Bob', 'Charlie', 'Charlie'],
'Age': [25, 30, 30, 35, 35]
})
# 1. 检查重复行
print(df.duplicated().sum())
# 2. 删除完全重复的行
df_unique = df.drop_duplicates()
# 3. 基于特定列去重(保留第一次出现的)
df_unique_id = df.drop_duplicates(subset=['ID'], keep='first')
# 4. 标记重复(用于审计)
df['is_duplicate'] = df.duplicated(subset=['ID'], keep=False)
数据类型转换
场景:日期列是字符串、数字列是对象类型、单位混用。
核心案例:
df = pd.DataFrame({
'date': ['2023-01-01', '2023/02/15', '20230320'],
'price': ['$100', '¥200', '300元'],
'age': ['25', '30', '三十五']
})
# 1. 统一日期格式(自动推断)
df['date'] = pd.to_datetime(df['date'], errors='coerce') # 无法解析的置为NaT
# 2. 去除货币符号并转为浮点数
df['price'] = df['price'].str.replace(r'[^\d.]', '', regex=True).astype(float)
# 3. 处理中文数字(需要映射字典)
age_map = {'二十五': 25, '三十': 30, '三十五': 35}
# 先用正则提取数字,若失败则用映射
# 简化方案:仅保留数字(会丢失'三十五')
df['age'] = pd.to_numeric(df['age'], errors='coerce') # '三十五' -> NaN
处理异常值
场景:年龄出现200岁、销售额为负数、超出3个标准差的数据。
核心案例:
import numpy as np
np.random.seed(42)
df = pd.DataFrame({'value': np.random.randn(100) * 50 + 100})
# 人为加入异常值
df.loc[0, 'value'] = 1000
df.loc[1, 'value'] = -500
# 1. 基于标准差(3σ原则)
mean = df['value'].mean()
std = df['value'].std()
df_no_outlier = df[(df['value'] >= mean - 3*std) & (df['value'] <= mean + 3*std)]
# 2. 基于四分位数(IQR)
Q1 = df['value'].quantile(0.25)
Q3 = df['value'].quantile(0.75)
IQR = Q3 - Q1
df_iqr = df[(df['value'] >= Q1 - 1.5*IQR) & (df['value'] <= Q3 + 1.5*IQR)]
# 3. 替换异常值为上下限(缩尾处理)
lower = Q1 - 1.5*IQR
upper = Q3 + 1.5*IQR
df['value'] = df['value'].clip(lower, upper)
字符串清洗(正则表达式)
场景:姓名首字母大写、去除多余空格、提取邮箱域名、手机号脱敏。
核心案例:
df = pd.DataFrame({
'name': [' alice ', 'BOB', 'Charlie '],
'email': ['alice@example.com', 'bob@test.org', 'charlie@company.com'],
'phone': ['138-1234-5678', '139 8765 4321', '021-12345678']
})
# 1. 去除空格 & 标题化
df['name'] = df['name'].str.strip().str.title()
# 2. 提取邮箱域名
df['domain'] = df['email'].str.split('@').str[1]
# 3. 手机号脱敏(保留前3后4)
df['phone_clean'] = df['phone'].str.replace(r'[^0-9]', '', regex=True)
df['phone_masked'] = df['phone_clean'].str.replace(r'(\d{3})\d{4}(\d{4})', r'\1****\2')
拆分及合并列
场景:全名列拆分为姓和名、地址拆分为省市、日期拆分为年月日。
核心案例:
df = pd.DataFrame({
'full_name': ['张三', '李四'],
'addr': ['上海市浦东新区', '北京市海淀区'],
'date': pd.to_datetime(['2023-01-15', '2024-06-20'])
})
# 1. 拆分列(中文无分隔符,需按字符长度,或使用正则)
df['last_name'] = df['full_name'].str[0]
df['first_name'] = df['full_name'].str[1:]
# 2. 拆分地址(提取省份)
df['province'] = df['addr'].str.extract(r'(.+?(?:省|市|自治区))')
# 3. 提取日期部件
df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month
df['day'] = df['date'].dt.day
统一文本格式(映射)
场景:性别包括“男/女/M/F/男性/女性”。
核心案例:
df = pd.DataFrame({
'gender': ['男', 'M', 'female', '女性', 'F', 'male']
})
# 1. 建立映射字典
gender_map = {
'男': 'M', 'M': 'M', 'male': 'M', 'Male': 'M',
'女': 'F', 'F': 'F', 'female': 'F', 'Female': 'F',
'男性': 'M', '女性': 'F'
}
df['gender_std'] = df['gender'].map(gender_map)
# 2. 检查映射是否完全(未映射的为NaN)
print(df['gender_std'].isnull().sum())
索引重置与排序
场景:删除行后索引不连续、需要按某列排序。
核心案例:
df = pd.DataFrame({'A': [3, 1, 2]}, index=[5, 2, 8])
# 1. 重置索引(从0开始,原索引变为列)
df_reset = df.reset_index()
# 2. 排序后重置索引
df_sorted = df.sort_values('A').reset_index(drop=True)
# 3. 按索引排序
df_indexed = df.sort_index()
合并与连接时去重
场景:两个数据表通过ID关联,但存在一对多关系导致笛卡尔积。
核心案例:
df1 = pd.DataFrame({'ID': [1, 2, 3], 'Name': ['A', 'B', 'C']})
df2 = pd.DataFrame({'ID': [1, 1, 2, 3], 'Score': [90, 95, 80, 70]})
# 1. 左连接(会重复行,因为ID=1在df2中有两条)
df_merged = df1.merge(df2, on='ID', how='left')
# 2. 若只需第一个匹配,先对df2去重
df2_first = df2.drop_duplicates(subset='ID', keep='first')
df_clean = df1.merge(df2_first, on='ID')
# 3. 或对结果进行聚合
df_agg = df1.merge(df2.groupby('ID', as_index=False)['Score'].mean(), on='ID')
数据透视与重塑
场景:宽表变长表(pivot→melt)、学生多门成绩转置。
核心案例:
# 宽表:每个学生一行,数学/语文/英语是列
df_wide = pd.DataFrame({
'name': ['Alice', 'Bob'],
'math': [90, 85],
'eng': [88, 92],
'chi': [95, 78]
})
# 1. 宽表转长表(melt)
df_long = df_wide.melt(id_vars=['name'],
value_vars=['math', 'eng', 'chi'],
var_name='subject',
value_name='score')
# 2. 长表转宽表(pivot)
df_back_wide = df_long.pivot(index='name', columns='subject', values='score').reset_index()
推荐练习的公开数据集
- Titanic(泰坦尼克号):经典的缺失值、属性编码、异常年龄清洗。
- 2015 Flight Delays(航班延误):时间格式、大面积缺失、分类编码。
- Instacart Market Basket(购物篮分析):重复订单、属性一致化、重采样。
- 自己收集的 CSV 数据(如商品详情页爬取):命名规范不一致、价格带符号、缺字段。
学习路径建议:先熟练掌握以上 10 个案例,然后找一个真实的脏数据集(如 Kaggle 上标记为 Data Cleaning 的比赛),完整跑一遍 EDA(探索性分析)前的工作。