机器学习——数据清洗

数据科学项目中缺失值填充的全面指南

一、数据准备与原始值保留策略

1.1 创建数据副本的最佳实践

在数据清洗过程中,保持原始数据的完整性至关重要。我们建议采用以下两种复制策略:

  1. 完全独立副本:使用Python的copy模块创建深层副本,确保所有嵌套数据结构都被完全复制

    python 复制代码
    import copy
    original_data = copy.deepcopy(data)  # 完全独立的原始数据副本
  2. 工作副本:使用pandas的copy()方法创建工作副本

    python 复制代码
    working_data = data.copy(deep=True)  # 深度复制,确保不影响原始数据

应用场景:在金融数据分析项目中,原始交易记录必须保持不可变,而清洗操作在工作副本上进行。

1.2 详细的缺失值标记方法

为每个存在缺失值的列创建标记列,记录原始缺失情况:

python 复制代码
for col in working_data.columns:
    if working_data[col].isnull().any():  # 检查列是否有缺失值
        working_data[f'{col}_missing'] = working_data[col].isnull().astype(int)
        # 存储缺失位置信息,1表示缺失,0表示存在

实际应用:在医疗数据集中,标记哪些血压值是估算的,哪些是实际测量的,这对后续分析至关重要。

二、基础统计填充方法的深入解析

2.1 众数填充的完整实现(适合分类变量)

python 复制代码
from scipy.stats import mode

def fill_with_mode(df, column):
    # 计算众数(忽略缺失值)
    mode_result = mode(df[column].dropna())
    mode_value = mode_result.mode[0] if len(mode_result.mode) > 0 else None
    
    # 创建填充列并保留原始值
    filled_col = f'{column}_filled_mode'
    df[filled_col] = df[column].fillna(mode_value)
    
    return df

# 示例:填充产品类别中的缺失值
data = fill_with_mode(data, 'product_category')

注意事项:当所有值都相同时众数可能不存在,需添加异常处理。

2.2 平均数填充的增强版(适合正态分布数值变量)

python 复制代码
def fill_with_mean(df, column, groupby_col=None):
    if groupby_col:
        # 分组计算均值(如按地区计算平均收入)
        group_means = df.groupby(groupby_col)[column].transform('mean')
        df[f'{column}_filled_mean'] = df[column].fillna(group_means)
    else:
        # 整体均值填充
        global_mean = df[column].mean()
        df[f'{column}_filled_mean'] = df[column].fillna(global_mean)
    
    return df

# 示例1:整体均值填充年龄
data = fill_with_mean(data, 'age')

# 示例2:按部门填充工资均值
data = fill_with_mean(data, 'salary', 'department')

优化建议:对于偏态分布数据,考虑使用对数转换后的均值。

2.3 中位数填充的稳健实现(适合偏态分布数据)

python 复制代码
def fill_with_median(df, column, clip_outliers=True):
    if clip_outliers:
        # 移除极端值后计算中位数
        q1 = df[column].quantile(0.25)
        q3 = df[column].quantile(0.75)
        iqr = q3 - q1
        filtered = df[column][
            (df[column] >= q1-1.5*iqr) & 
            (df[column] <= q3+1.5*iqr)
        ]
        median_value = filtered.median()
    else:
        median_value = df[column].median()
    
    df[f'{column}_filled_median'] = df[column].fillna(median_value)
    return df

# 示例:填充房屋价格(考虑去除异常值)
data = fill_with_median(data, 'house_price', clip_outliers=True)

适用场景:在收入、房价等通常右偏的数据中,中位数比平均数更能代表典型值。

三、机器学习填充方法的实现

3.1 数据预处理的完整流程

python 复制代码
from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.impute import SimpleImputer

# 分类变量编码
def encode_categorical(df, cat_cols):
    le_dict = {}
    for col in cat_cols:
        le = LabelEncoder()
        df[col+'_encoded'] = le.fit_transform(df[col].fillna('Missing'))
        le_dict[col] = le  # 保存编码器供后续使用
    return df, le_dict

# 数值变量标准化
def scale_numeric(df, num_cols):
    scaler = StandardScaler()
    df[num_cols] = scaler.fit_transform(df[num_cols])
    return df, scaler

# 示例预处理流程
data, encoders = encode_categorical(data, ['department', 'job_title'])
data, scaler = scale_numeric(data, ['age', 'salary'])

关键点:始终保存预处理对象,以便对新数据应用相同转换。

3.2 线性回归填充的完整实现

python 复制代码
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split

def fill_with_regression(df, target_col, feature_cols):
    # 分离完整数据和缺失数据
    complete = df[df[target_col].notnull()]
    missing = df[df[target_col].isnull()]
    
    if len(complete) < 10:  # 样本量不足时退回均值填充
        return fill_with_mean(df, target_col)
    
    # 划分训练测试集
    X = complete[feature_cols]
    y = complete[target_col]
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
    
    # 训练模型
    model = LinearRegression()
    model.fit(X_train, y_train)
    
    # 预测缺失值
    if not missing.empty:
        X_missing = missing[feature_cols]
        predictions = model.predict(X_missing)
        df.loc[df[target_col].isnull(), f'{target_col}_filled_lr'] = predictions
    
    return df, model

# 示例:用学历和工作年限预测收入
data, lr_model = fill_with_regression(
    data, 
    target_col='income',
    feature_cols=['education_years', 'work_experience']
)

评估指标:建议检查模型在测试集上的R²值,确保预测质量。

3.3 随机森林填充的高级实现

python 复制代码
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error

def fill_with_rf(df, target_col, feature_cols, n_estimators=100):
    complete = df[df[target_col].notnull()]
    missing = df[df[target_col].isnull()]
    
    if len(complete) < 30:  # 随机森林需要更多样本
        return fill_with_median(df, target_col)
    
    X = complete[feature_cols]
    y = complete[target_col]
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
    
    # 使用交叉验证选择最佳参数
    model = RandomForestRegressor(
        n_estimators=n_estimators,
        random_state=42,
        n_jobs=-1  # 使用所有CPU核心
    )
    model.fit(X_train, y_train)
    
    # 评估模型
    y_pred = model.predict(X_test)
    mse = mean_squared_error(y_test, y_pred)
    print(f'Test MSE: {mse:.2f}')
    
    # 填充缺失值
    if not missing.empty:
        X_missing = missing[feature_cols]
        predictions = model.predict(X_missing)
        df.loc[df[target_col].isnull(), f'{target_col}_filled_rf'] = predictions
    
    return df, model

# 示例:用多种特征预测客户生命周期价值
data, rf_model = fill_with_rf(
    data,
    target_col='customer_lifetime_value',
    feature_cols=['age', 'income', 'purchase_frequency', 'region']
)

优化建议:使用GridSearchCV进行超参数调优,考虑特征重要性分析。

四、填充结果比较与分析

4.1 可视化

python 复制代码
import matplotlib.pyplot as plt
import seaborn as sns

def compare_distributions(df, original_col, filled_cols):
    plt.figure(figsize=(12, 6))
    
    # 绘制原始分布(仅非缺失值)
    sns.kdeplot(df[original_col].dropna(), label='Original', linewidth=3)
    
    # 绘制各填充方法分布
    for method in filled_cols:
        sns.kdeplot(df[method], label=method.replace('_filled_', ' '))
    
    plt.title('Distribution Comparison')
    plt.xlabel(original_col)
    plt.ylabel('Density')
    plt.legend()
    plt.show()

# 示例:比较年龄的不同填充结果
compare_distributions(
    data,
    original_col='age',
    filled_cols=['age_filled_mean', 'age_filled_median', 'age_filled_rf']
)

扩展功能:添加Q-Q图比较分位数分布,或箱线图比较统计量。

4.2 统计比较

python 复制代码
def comprehensive_stats_compare(df, original_col, filled_cols):
    stats = pd.DataFrame()
    
    # 原始数据统计(仅非缺失值)
    stats['Original'] = df[original_col].describe()
    
    # 各填充方法统计
    for col in filled_cols:
        stats[col.replace('_filled_', ' ')] = df[col].describe()
    
    # 添加额外指标
    additional_stats = pd.DataFrame({
        'Skewness': [df[original_col].skew()] + [df[col].skew() for col in filled_cols],
        'Kurtosis': [df[original_col].kurtosis()] + [df[col].kurtosis() for col in filled_cols],
        'Missing %': [df[original_col].isnull().mean()*100] + [0]*len(filled_cols)
    }, index=['Original'] + [col.replace('_filled_', ' ') for col in filled_cols])
    
    return stats.T.join(additional_stats)

# 示例:生成详细的统计比较报告
stats_report = comprehensive_stats_compare(
    data,
    original_col='income',
    filled_cols=['income_filled_mean', 'income_filled_median', 'income_filled_lr']
)

print(stats_report.round(2))
相关推荐
跟着珅聪学java23 分钟前
Apache OpenNLP简介
人工智能·知识图谱
AwhiteV1 小时前
利用图数据库高效解决 Text2sql 任务中表结构复杂时占用过多大模型上下文的问题
数据库·人工智能·自然语言处理·oracle·大模型·text2sql
Black_Rock_br1 小时前
AI on Mac, Your Way!全本地化智能代理,隐私与性能兼得
人工智能·macos
☺����2 小时前
实现自己的AI视频监控系统-第一章-视频拉流与解码2
开发语言·人工智能·python·音视频
一车小面包2 小时前
逻辑回归 从0到1
算法·机器学习·逻辑回归
小猿姐3 小时前
KubeBlocks AI:AI时代的云原生数据库运维探索
数据库·人工智能·云原生·kubeblocks
算法_小学生3 小时前
循环神经网络(RNN, Recurrent Neural Network)
人工智能·rnn·深度学习
吱吱企业安全通讯软件3 小时前
吱吱企业通讯软件保证内部通讯安全,搭建数字安全体系
大数据·网络·人工智能·安全·信息与通信·吱吱办公通讯
盲盒Q4 小时前
《频率之光:共振之战》
人工智能·硬件架构·量子计算