Python案例如何用Pandas做数据插补

wen python案例 1

本文目录导读:

Python案例如何用Pandas做数据插补

  1. 基础环境准备
  2. 多种插补方法实战
  3. 插补效果评估
  4. 实战案例:销售数据插补
  5. 最佳实践建议

我来详细讲解如何使用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()
  1. 基础方法:fillna(), interpolate() 是Pandas内置的常用函数
  2. 高级方法:Scikit-learn提供KNNImputer、IterativeImputer等
  3. 业务理解:插补策略应考虑数据特征和业务逻辑
  4. 效果评估:通过分布对比、统计指标验证插补质量
  5. 综合应用:根据缺失比例和数据类型选择合适策略

这些方法可以灵活组合使用,找到最适合你具体场景的插补方案。

抱歉,评论功能暂时关闭!