Python Pandas中薪资与贷款相关列异常值识别替换最优方法
Python中数值列异常值识别与替换的最优方案
嘿,针对你提出的两个异常值处理问题,我来分享下实际项目里常用的最优方案,结合你提到的场景展开:
1. 单Salary列的异常值处理
薪资数据通常是右偏分布(高收入人群占比少),所以优先推荐和你用Seaborn箱线图匹配的IQR四分位距法;如果数据接近正态分布,也可以用Z-score法。以下是具体实现:
方案一:IQR法(偏态数据首选)
IQR基于四分位数,能有效过滤极端值,和箱线图的异常值判定逻辑完全一致:
import pandas as pd # 假设df是你的DataFrame q1 = df['Salary'].quantile(0.25) q3 = df['Salary'].quantile(0.75) iqr = q3 - q1 lower_bound = q1 - 1.5 * iqr upper_bound = q3 + 1.5 * iqr # 标记异常值为NaN df['Salary'] = df['Salary'].where((df['Salary'] >= lower_bound) & (df['Salary'] <= upper_bound), pd.NA) # 替换异常值:偏态数据建议用中位数(比均值更稳健,不受剩余极端值影响) df['Salary'] = df['Salary'].fillna(df['Salary'].median()) # 如果你坚持要用均值: # df['Salary'] = df['Salary'].fillna(df['Salary'].mean())
方案二:Z-score法(正态分布数据适用)
如果你的Salary列接近正态分布,用Z-score(绝对值>3视为异常)更合适:
from scipy.stats import zscore # 计算Z值(先剔除已有的NaN) df['z_score'] = zscore(df['Salary'].dropna()) # 标记Z绝对值>3的为异常值 df['Salary'] = df['Salary'].where(df['z_score'].abs() <= 3, pd.NA) # 填充异常值(同样建议中位数) df['Salary'] = df['Salary'].fillna(df['Salary'].median()) # 清理临时列 df.drop('z_score', axis=1, inplace=True)
2. 多列异常值处理+分组填充(针对你的Pandas场景)
你提到的ApplicantIncome、CoapplicantIncome、LoanAmount、Loan_Amount_Term这些列大多是右偏分布,IQR法依然适用。而且按Gender、Education、Self_Employed、Property_Area分组填充的思路非常合理——不同分组的收入/贷款水平差异很大,分组统计值比全局值更贴合实际。
完整实现步骤
第一步:批量标记多列异常值
先写一个通用函数,批量处理目标列的异常值:
def mark_outliers_with_iqr(series): q1 = series.quantile(0.25) q3 = series.quantile(0.75) iqr = q3 - q1 lower = q1 - 1.5 * iqr upper = q3 + 1.5 * iqr return series.where((series >= lower) & (series <= upper), pd.NA) # 批量处理目标列 target_cols = ['ApplicantIncome', 'CoapplicantIncome', 'LoanAmount', 'Loan_Amount_Term'] df[target_cols] = df[target_cols].apply(mark_outliers_with_iqr)
第二步:预处理分组键的缺失值
你的样本里Married、Dependents有NaN,分组时会把NaN当作单独组,影响统计结果,建议先填充:
# 用众数填充分组相关列的缺失值(众数适合分类变量) df['Gender'] = df['Gender'].fillna(df['Gender'].mode()[0]) df['Married'] = df['Married'].fillna(df['Married'].mode()[0]) df['Dependents'] = df['Dependents'].fillna(df['Dependents'].mode()[0]) df['Self_Employed'] = df['Self_Employed'].fillna(df['Self_Employed'].mode()[0])
第三步:分组填充异常值(按你的需求用均值)
优先用分组均值填充,若分组均值为空(比如分组全是NaN)则 fallback到全局均值:
import numpy as np # 定义分组键 group_keys = ['Gender', 'Education', 'Self_Employed', 'Property_Area'] # 计算全局均值(作为 fallback) global_means = df[target_cols].mean() # 遍历每一列进行分组填充 for col in target_cols: # 计算分组均值 group_mean = df.groupby(group_keys)[col].transform('mean') # 先填充分组均值,再填充全局均值 df[col] = df[col].fillna(group_mean).fillna(global_means[col])
额外优化:过滤小样本分组
如果某些分组样本量太少(比如<5),统计值没有参考意义,可以直接用全局值:
# 计算每个分组的样本量 group_sizes = df.groupby(group_keys)[target_cols[0]].transform('count') for col in target_cols: group_mean = df.groupby(group_keys)[col].transform('mean') # 样本量>=5用分组均值,否则用全局均值 df[col] = df[col].fillna( np.where(group_sizes >= 5, group_mean, global_means[col]) )
内容的提问来源于stack exchange,提问作者Anandaraj Pandian




