本文目录导读:

我来详细讲解如何使用Pandas进行数据插补,包含多种场景和方法。
基础环境准备
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 创建包含缺失值的示例数据
np.random.seed(42)
df = pd.DataFrame({
'date': pd.date_range('2024-01-01', periods=100, freq='D'),
'temperature': [20 + np.random.randn() * 5 if np.random.random() > 0.2 else np.nan
for _ in range(100)],
'humidity': [60 + np.random.randn() * 10 if np.random.random() > 0.15 else np.nan
for _ in range(100)],
'category': np.random.choice(['A', 'B', 'C'], 100)
})
print("原始数据概览:")
print(df.info())
print(f"\n缺失值统计:\n{df.isnull().sum()}")
多种插补方法实战
1 简单统计插补
def simple_imputation(df):
"""演示各种简单插补方法"""
df_imputed = df.copy()
# 方法1:使用列均值插补
df_imputed['temperature_mean'] = df_imputed['temperature'].fillna(df_imputed['temperature'].mean())
# 方法2:使用中位数插补(适合有异常值的数据)
df_imputed['temperature_median'] = df_imputed['temperature'].fillna(df_imputed['temperature'].median())
# 方法3:使用众数插补(适合分类数据)
df_imputed['category_mode'] = df_imputed['category'].fillna(df_imputed['category'].mode()[0])
# 方法4:使用常数插补
df_imputed['temperature_constant'] = df_imputed['temperature'].fillna(25) # 假设默认温度25度
print("简单插补结果对比:")
print(df_imputed[['temperature', 'temperature_mean', 'temperature_median', 'temperature_constant']].head())
return df_imputed
df_simple = simple_imputation(df)
2 时间序列插补
def time_series_imputation(df):
"""时间序列数据插补方法"""
df_ts = df.copy()
df_ts.set_index('date', inplace=True)
# 方法1:前向填充
df_ts['temp_ffill'] = df_ts['temperature'].fillna(method='ffill')
# 方法2:后向填充
df_ts['temp_bfill'] = df_ts['temperature'].fillna(method='bfill')
# 方法3:线性插值
df_ts['temp_linear'] = df_ts['temperature'].interpolate(method='linear')
# 方法4:时间加权插值
df_ts['temp_time'] = df_ts['temperature'].interpolate(method='time')
# 查看某段缺失数据的插补效果
na_indices = df_ts['temperature'].isna()
print(f"缺失值位置:{na_indices.sum()}个")
print("\n插补效果示例:")
print(df_ts[na_indices].head())
return df_ts
df_ts = time_series_imputation(df)
3 高级插补方法
from sklearn.impute import KNNImputer, SimpleImputer
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
def advanced_imputation(df):
"""高级插补方法"""
# 准备数据(排除非数值列)
numeric_cols = ['temperature', 'humidity']
df_numeric = df[numeric_cols].copy()
# 方法1:KNN插补
knn_imputer = KNNImputer(n_neighbors=3)
df_knn = pd.DataFrame(
knn_imputer.fit_transform(df_numeric),
columns=numeric_cols
)
# 方法2:多重插补(MICE)
mice_imputer = IterativeImputer(
max_iter=10,
random_state=42,
imputation_order='random'
)
df_mice = pd.DataFrame(
mice_imputer.fit_transform(df_numeric),
columns=numeric_cols
)
# 方法3:分组均值插补
df_grouped = df.copy()
df_grouped['temp_group_mean'] = df_grouped.groupby('category')['temperature'].transform(
lambda x: x.fillna(x.mean())
)
print("KNN插补结果:")
print(df_knn.head())
print(f"\nMICE插补结果:")
print(df_mice.head())
print(f"\n分组均值插补结果:")
print(df_grouped[['category', 'temperature', 'temp_group_mean']].head())
return df_knn, df_mice, df_grouped
df_knn, df_mice, df_grouped = advanced_imputation(df)
4 综合插补策略
def comprehensive_imputation_strategy(df):
"""综合插补策略"""
def custom_imputer(series, method='mixed'):
"""
自定义插补函数
method: 'mixed' - 混合策略, 'auto' - 自动选择
"""
if series.isnull().sum() == 0:
return series
missing_pct = series.isnull().mean()
if method == 'mixed':
if missing_pct < 0.1:
# 缺失<10%:使用均值或中位数
return series.fillna(series.median())
elif missing_pct < 0.3:
# 缺失10%-30%:使用插值
return series.interpolate(method='linear')
else:
# 缺失>30%:使用多重插补
imputer = IterativeImputer(max_iter=5)
return pd.Series(
imputer.fit_transform(series.values.reshape(-1, 1)).flatten(),
index=series.index
)
return series
# 应用自定义插补策略
df_result = df.copy()
for col in ['temperature', 'humidity']:
df_result[f'{col}_imputed'] = custom_imputer(df_result[col])
print("综合插补策略结果:")
print(f"原始数据缺失率: {df['temperature'].isnull().mean():.2%}")
print(f"插补后缺失率: {df_result['temperature_imputed'].isnull().sum()}")
return df_result
df_comprehensive = comprehensive_imputation_strategy(df)
插补效果评估
def evaluate_imputation(original_df, imputed_df, true_values=None):
"""评估插补效果"""
# 模拟真实值(假设我们知道某些缺失值的真实值)
if true_values is None:
# 创建模拟真实值
true_values = {
'temperature': 22.5,
'humidity': 65.0
}
# 计算误差
print("插补效果评估:")
for col in ['temperature', 'humidity']:
imputed_col = f'{col}_imputed' if f'{col}_imputed' in imputed_df.columns else col
if imputed_col in imputed_df.columns:
# 获取插补后的值
imputed_values = imputed_df[imputed_col]
# 计算基本统计
print(f"\n{col}插补结果统计:")
print(f" 均值: {imputed_values.mean():.2f}")
print(f" 标准差: {imputed_values.std():.2f}")
print(f" 最小值: {imputed_values.min():.2f}")
print(f" 最大值: {imputed_values.max():.2f}")
# 可视化比较
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
for i, col in enumerate(['temperature', 'humidity']):
axes[i].hist(original_df[col].dropna(), alpha=0.5, label='原始数据', bins=20)
if f'{col}_imputed' in imputed_df.columns:
axes[i].hist(imputed_df[f'{col}_imputed'], alpha=0.5, label='插补后数据', bins=20)
axes[i].set_title(f'{col} 分布对比')
axes[i].legend()
plt.tight_layout()
plt.show()
return imputed_values
evaluate_imputation(df, df_comprehensive)
实战案例:销售数据插补
def sales_data_imputation_example():
"""销售数据插补实战案例"""
# 创建销售数据
np.random.seed(42)
sales_data = pd.DataFrame({
'date': pd.date_range('2024-01-01', '2024-03-31', freq='D'),
'sales': [
100 + 50 * np.sin(2 * np.pi * i / 7) + np.random.randn() * 20
for i in range(91)
],
'weekday': [pd.Timestamp('2024-01-01') + pd.Timedelta(days=i) for i in range(91)]
})
# 随机引入缺失值(特别是在周末)
np.random.seed(42)
days_of_week = sales_data['date'].dt.dayofweek
missing_mask = (days_of_week >= 5) & (np.random.random(91) < 0.4) # 周末40%缺失
missing_mask |= (np.random.random(91) < 0.1) # 其他时间10%缺失
sales_data.loc[missing_mask, 'sales'] = np.nan
sales_data['weekday_name'] = sales_data['date'].dt.day_name()
print("销售数据概况:")
print(f"数据量: {len(sales_data)}")
print(f"缺失值: {sales_data['sales'].isnull().sum()}")
print(f"缺失率: {sales_data['sales'].isnull().mean():.2%}")
# 多种插补方法对比
sales_data['sales_weekday_mean'] = sales_data.groupby('weekday_name')['sales'].transform(
lambda x: x.fillna(x.mean())
)
sales_data['sales_ffill'] = sales_data['sales'].fillna(method='ffill')
sales_data['sales_linear'] = sales_data['sales'].interpolate(method='linear')
# 使用季节性插补
sales_data['sales_seasonal'] = sales_data['sales'].interpolate(method='cubic')
print(f"\n插补后数据概况:")
print(sales_data[sales_data['sales'].isna()].head())
# 可视化
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
methods = ['weekday_mean', 'ffill', 'linear', 'seasonal']s = ['周平均插补', '前向填充', '线性插值', '三次样条插值']
for i, (method, title) in enumerate(zip(methods, titles)):
row, col = i // 2, i % 2
# 近30天数据对比
recent = sales_data.tail(30)
axes[row, col].plot(recent['date'], recent['sales'], 'o-', label='原始数据', alpha=0.6)
axes[row, col].plot(recent['date'], recent[f'sales_{method}'], 's--', label=f'{title}插补', alpha=0.8)
axes[row, col].set_title(f'{title}插补效果')
axes[row, col].legend()
axes[row, col].tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.show()
return sales_data
sales_data = sales_data_imputation_example()
最佳实践建议
def imputation_best_practices():
"""数据插补最佳实践"""
print("数据插补最佳实践建议:")
print("="*50)
print("""
1. **缺失值分析**
- 了解缺失模式:随机缺失(MCAR/MAR)还是非随机缺失(MNAR)
- 评估缺失比例:<5%可忽略,5-20%需要插补,>20%需谨慎
2. **选择合适的插补方法**
- 连续变量:均值、中位数、线性插值
- 分类变量:众数、类别插补
- 时间序列:前向/后向填充、时间插值
- 多变量关系:KNN、MICE
3. **实用技巧**
- 先处理异常值,再进行插补
- 考虑业务逻辑,如销售额不能为负
- 对关键变量使用三种以上方法对比
4. **避免的陷阱**
- 不要盲目使用均值插补
- 不要忽略缺失值背后的业务原因
- 避免数据泄露(用训练集统计量插补)
5. **评估标准**
- 保持数据分布特征
- 维持变量间相关性
- 业务合理性检验
""")
imputation_best_practices()
- 基础方法:fillna(), interpolate() 是Pandas内置的常用函数
- 高级方法:Scikit-learn提供KNNImputer、IterativeImputer等
- 业务理解:插补策略应考虑数据特征和业务逻辑
- 效果评估:通过分布对比、统计指标验证插补质量
- 综合应用:根据缺失比例和数据类型选择合适策略
这些方法可以灵活组合使用,找到最适合你具体场景的插补方案。