数据清洗 #
数据清洗概述 #
数据清洗是数据分析的重要步骤,高质量的数据是分析结果可靠性的基础。
text
┌─────────────────────────────────────────────────────────────┐
│ 数据清洗任务 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 缺失值处理 │
│ ├── 检测缺失值 │
│ ├── 删除缺失值 │
│ └── 填充缺失值 │
│ │
│ 重复值处理 │
│ ├── 检测重复值 │
│ └── 删除重复值 │
│ │
│ 异常值处理 │
│ ├── 检测异常值 │
│ ├── 删除异常值 │
│ └── 替换异常值 │
│ │
│ 数据规范化 │
│ ├── 数据类型转换 │
│ ├── 字符串处理 │
│ └── 数据标准化 │
│ │
└─────────────────────────────────────────────────────────────┘
缺失值处理 #
检测缺失值 #
python
import pandas as pd
import numpy as np
df = pd.DataFrame({
'A': [1, 2, np.nan, 4, 5],
'B': [np.nan, 2, 3, np.nan, 5],
'C': ['a', 'b', np.nan, 'd', 'e'],
'D': [1, 2, 3, 4, np.nan]
})
# 检测缺失值
print(df.isna())
print(df.isnull()) # 等价
# 检测非缺失值
print(df.notna())
# 统计缺失值数量
print(df.isna().sum())
# 每行缺失值数量
print(df.isna().sum(axis=1))
# 缺失值比例
print(df.isna().mean())
# 查看缺失值信息
print(df.info())
删除缺失值 #
python
# 删除包含缺失值的行
print(df.dropna())
# 删除包含缺失值的列
print(df.dropna(axis=1))
# 只删除全为缺失值的行
print(df.dropna(how='all'))
# 只删除全为非缺失值的行
print(df.dropna(how='any')) # 默认
# 保留至少有 n 个非缺失值的行
print(df.dropna(thresh=3))
# 只考虑特定列
print(df.dropna(subset=['A', 'B']))
# 原地修改
df.dropna(inplace=True)
填充缺失值 #
python
df = pd.DataFrame({
'A': [1, 2, np.nan, 4, 5],
'B': [np.nan, 2, 3, np.nan, 5],
'C': ['a', 'b', np.nan, 'd', 'e']
})
# 用常数填充
print(df.fillna(0))
# 不同列用不同值
print(df.fillna({'A': 0, 'B': 1, 'C': 'unknown'}))
# 用统计值填充
print(df.fillna(df.mean(numeric_only=True))) # 均值
print(df.fillna(df.median(numeric_only=True))) # 中位数
print(df.fillna(df.mode().iloc[0])) # 众数
# 前向填充
print(df.fillna(method='ffill')) # 或 df.ffill()
# 后向填充
print(df.fillna(method='bfill')) # 或 df.bfill()
# 限制填充次数
print(df.fillna(method='ffill', limit=1))
# 插值填充
print(df.interpolate()) # 线性插值
print(df.interpolate(method='linear'))
print(df.interpolate(method='polynomial', order=2)) # 多项式插值
高级缺失值处理 #
python
# 分组填充
df = pd.DataFrame({
'group': ['A', 'A', 'B', 'B', 'A'],
'value': [1, np.nan, 3, np.nan, 5]
})
df['value'] = df.groupby('group')['value'].transform(
lambda x: x.fillna(x.mean())
)
print(df)
# 条件填充
df = pd.DataFrame({
'A': [1, np.nan, 3, np.nan, 5],
'B': [10, 20, 30, 40, 50]
})
df['A'] = df['A'].fillna(df['B'] / 10)
print(df)
重复值处理 #
检测重复值 #
python
df = pd.DataFrame({
'A': [1, 1, 2, 2, 3],
'B': ['a', 'a', 'b', 'b', 'c'],
'C': [10, 10, 20, 20, 30]
})
# 检测重复行
print(df.duplicated())
# 查看重复行
print(df[df.duplicated()])
# 查看非重复行
print(df[~df.duplicated()])
# 指定列判断重复
print(df.duplicated(subset=['A']))
# 保留最后一个
print(df.duplicated(keep='last'))
# 标记所有重复
print(df.duplicated(keep=False))
# 统计重复数量
print(df.duplicated().sum())
删除重复值 #
python
# 删除重复行
print(df.drop_duplicates())
# 指定列判断
print(df.drop_duplicates(subset=['A']))
# 保留最后一个
print(df.drop_duplicates(keep='last'))
# 删除所有重复
print(df.drop_duplicates(keep=False))
# 原地修改
df.drop_duplicates(inplace=True)
异常值处理 #
检测异常值 #
python
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'value': np.concatenate([np.random.normal(0, 1, 100), [10, -10, 15]])
})
# 统计方法
print(df.describe())
# 使用 Z-score
from scipy import stats
df['z_score'] = np.abs(stats.zscore(df['value']))
outliers = df[df['z_score'] > 3]
print(outliers)
# 使用 IQR(四分位距)
Q1 = df['value'].quantile(0.25)
Q3 = df['value'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
print(f'下界: {lower_bound}, 上界: {upper_bound}')
outliers = df[(df['value'] < lower_bound) | (df['value'] > upper_bound)]
print(outliers)
# 使用百分位数
lower = df['value'].quantile(0.01)
upper = df['value'].quantile(0.99)
outliers = df[(df['value'] < lower) | (df['value'] > upper)]
处理异常值 #
python
# 删除异常值
df_clean = df[(df['value'] >= lower_bound) & (df['value'] <= upper_bound)]
# 替换为边界值
df['value_clipped'] = df['value'].clip(lower_bound, upper_bound)
# 替换为缺失值
df.loc[(df['value'] < lower_bound) | (df['value'] > upper_bound), 'value'] = np.nan
# 替换为中位数
median = df['value'].median()
df.loc[(df['value'] < lower_bound) | (df['value'] > upper_bound), 'value'] = median
可视化检测异常值 #
python
import matplotlib.pyplot as plt
# 箱线图
plt.figure(figsize=(8, 4))
plt.boxplot(df['value'])
plt.title('Box Plot')
plt.show()
# 散点图
plt.figure(figsize=(8, 4))
plt.scatter(range(len(df)), df['value'])
plt.title('Scatter Plot')
plt.show()
数据类型转换 #
类型检测与转换 #
python
df = pd.DataFrame({
'A': ['1', '2', '3'],
'B': ['1.1', '2.2', '3.3'],
'C': ['2024-01-01', '2024-01-02', '2024-01-03'],
'D': ['True', 'False', 'True']
})
# 查看数据类型
print(df.dtypes)
# 转换为数值
df['A'] = pd.to_numeric(df['A'])
df['B'] = pd.to_numeric(df['B'])
# 转换为整数
df['A'] = df['A'].astype(int)
# 转换为日期
df['C'] = pd.to_datetime(df['C'])
# 转换为布尔值
df['D'] = df['D'].map({'True': True, 'False': False})
# 批量转换
df = df.astype({'A': 'int32', 'B': 'float32'})
智能类型转换 #
python
# 自动推断最佳类型
df = df.convert_dtypes()
print(df.dtypes)
# 推断对象类型
df = df.infer_objects()
字符串处理 #
清理字符串 #
python
df = pd.DataFrame({
'name': [' Alice ', 'BOB', 'Charlie ', ' diana']
})
# 去除空格
df['name'] = df['name'].str.strip() # 两端
df['name'] = df['name'].str.lstrip() # 左端
df['name'] = df['name'].str.rstrip() # 右端
# 大小写转换
df['name_lower'] = df['name'].str.lower()
df['name_upper'] = df['name'].str.upper()
df['name_title'] = df['name'].str.title()
df['name_capitalize'] = df['name'].str.capitalize()
# 替换
df['name'] = df['name'].str.replace('a', 'A')
# 正则替换
df['name'] = df['name'].str.replace(r'\s+', ' ', regex=True)
字符串分割与提取 #
python
df = pd.DataFrame({
'full_name': ['Alice Smith', 'Bob Johnson', 'Charlie Brown']
})
# 分割
df[['first', 'last']] = df['full_name'].str.split(' ', expand=True)
# 提取
df['initial'] = df['full_name'].str.extract(r'^(\w)')
# 提取多个组
df[['first_initial', 'last_initial']] = df['full_name'].str.extract(r'^(\w)\w+\s+(\w)')
数据规范化 #
标准化 #
python
from sklearn.preprocessing import StandardScaler, MinMaxScaler
df = pd.DataFrame({
'A': [1, 2, 3, 4, 5],
'B': [10, 20, 30, 40, 50]
})
# Z-score 标准化
scaler = StandardScaler()
df_std = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)
# Min-Max 归一化
scaler = MinMaxScaler()
df_norm = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)
# 手动标准化
df_std = (df - df.mean()) / df.std()
# 手动归一化
df_norm = (df - df.min()) / (df.max() - df.min())
数值离散化 #
python
df = pd.DataFrame({'age': [22, 25, 30, 35, 40, 45, 50, 55, 60]})
# 等宽分箱
df['age_group'] = pd.cut(df['age'], bins=3)
# 自定义分箱
df['age_group'] = pd.cut(df['age'],
bins=[0, 30, 50, 100],
labels=['Young', 'Middle', 'Senior'])
# 等频分箱
df['age_quartile'] = pd.qcut(df['age'], q=4, labels=['Q1', 'Q2', 'Q3', 'Q4'])
数据清洗流程示例 #
python
def clean_data(df):
# 1. 处理缺失值
df = df.dropna(subset=['important_column'])
df = df.fillna({'numeric_col': df['numeric_col'].median(),
'text_col': 'unknown'})
# 2. 处理重复值
df = df.drop_duplicates()
# 3. 处理异常值
Q1 = df['value'].quantile(0.25)
Q3 = df['value'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
df['value'] = df['value'].clip(lower, upper)
# 4. 数据类型转换
df['date'] = pd.to_datetime(df['date'])
df['amount'] = pd.to_numeric(df['amount'], errors='coerce')
# 5. 字符串处理
df['name'] = df['name'].str.strip().str.title()
# 6. 重置索引
df = df.reset_index(drop=True)
return df
数据质量检查 #
python
def data_quality_report(df):
print("=" * 50)
print("数据质量报告")
print("=" * 50)
print(f"\n数据形状: {df.shape}")
print("\n缺失值统计:")
missing = df.isna().sum()
print(missing[missing > 0])
print("\n重复行数量:", df.duplicated().sum())
print("\n数据类型:")
print(df.dtypes)
print("\n数值列统计:")
print(df.describe())
print("\n唯一值数量:")
for col in df.columns:
print(f" {col}: {df[col].nunique()}")
data_quality_report(df)
下一步 #
掌握了数据清洗后,接下来学习 统计分析,深入了解如何进行描述性统计和相关性分析!
最后更新:2026-04-04