You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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场景)

你提到的ApplicantIncomeCoapplicantIncomeLoanAmountLoan_Amount_Term这些列大多是右偏分布,IQR法依然适用。而且按GenderEducationSelf_EmployedProperty_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)

第二步:预处理分组键的缺失值

你的样本里MarriedDependents有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

火山引擎 最新活动