数据清洗基础:缺失值处理、异常值检测、重复值处理
数据清洗这活儿,说白了就是给原始数据「洗澡」。我做了这么多年绿色金融数据,发现一个残酷的事实:你花在数据清洗上的时间,往往比建模还多。别不信,今天咱们就聊聊最基础的三个动作——缺失值、异常值、重复值。
核心观点:数据清洗不是机械操作,而是基于业务逻辑的决策过程。绿色金融数据尤其特殊,因为很多指标(比如碳排放、绿色评级)本身就带有主观性和不完整性。
一、缺失值处理:别急着删,先问问为什么
绿色金融数据里,缺失值简直家常便饭。我记得有一次处理某上市公司的ESG评级数据,发现碳排放强度这一列有30%都是空的。当时团队里有人直接说「删掉算了」,我赶紧拦住——缺失本身可能就是信息。
为什么会缺失?我总结了几种常见情况:
- 数据未披露:很多中小企业根本不披露碳排放数据,这不是随机缺失,而是「选择性缺失」
- 采集失败:API接口断了、爬虫被反、人工录入漏了——这种可以补
- 业务上不存在:比如某公司没有绿色债券,那绿色债券收益率自然是空值
处理方式有三种,我按推荐程度排个序:
1. 删除法
简单粗暴,但代价大。我一般只在两种情况下用删除:一是缺失比例低于5%,二是缺失完全随机且不影响样本代表性。
# 删除缺失值比例超过30%的列
df.dropna(thresh=len(df)*0.7, axis=1, inplace=True)
# 删除缺失值所在的行(慎用)
df_clean = df.dropna(subset=['碳排放强度'])
⚠️ 我曾经踩过的坑:有一次做绿色债券收益率分析,我删掉了所有缺失行,结果样本从5000条骤降到800条,模型直接过拟合。后来发现,缺失主要集中在某些特定行业——删除操作引入了严重的样本偏差。
2. 填充法
这是最常用的方法。但填充什么值,得看数据分布:
| 填充方式 | 适用场景 | 代码示例 |
|---|---|---|
| 均值填充 | 数据近似正态分布,如公司市值 | df.fillna(df.mean()) |
| 中位数填充 | 数据有偏或存在异常值,如绿色债券收益率 | df.fillna(df.median()) |
| 众数填充 | 分类变量,如绿色评级等级 | df.fillna(df.mode().iloc[0]) |
| 前向/后向填充 | 时间序列数据,如季度碳排放数据 | df.fillna(method='ffill') |
💡 我的个人习惯:对于绿色金融中的财务指标,我更喜欢用中位数而不是均值。你想想看,一个极端值(比如某公司突然暴增的碳排放)就能把均值拉偏,但中位数稳得很。
3. 插值法
插值比简单填充更「聪明」一点。它利用已知数据点之间的趋势来估算缺失值。我常用的是线性插值和时间插值:
# 线性插值
df['碳排放强度'] = df['碳排放强度'].interpolate(method='linear')
# 时间序列插值(适用于日期索引)
df['绿色债券收益率'] = df['绿色债券收益率'].interpolate(method='time')
嗯,这里要注意:插值假设数据是平滑变化的。如果数据本身波动剧烈(比如某些高频绿色指数),插值反而会引入噪声。
二、异常值检测:别把「特殊」当「错误」
异常值检测是我最喜欢聊的话题。为什么?因为绿色金融数据里,异常值往往藏着最有价值的信息。比如某公司碳排放突然下降90%,可能是数据错误,但也可能是它真的完成了技术革新。
我常用的方法就两个,但够用了:
1. 3σ原则(拉依达准则)
假设数据服从正态分布,那么超出均值±3倍标准差的值就是异常。这个方法简单,但有个前提——数据得是正态的。
import numpy as np
mean = df['碳排放强度'].mean()
std = df['碳排放强度'].std()
threshold = 3
# 标记异常值
df['is_outlier_3sigma'] = (np.abs(df['碳排放强度'] - mean) > threshold * std)
⚠️ 我曾经犯过的错:有一次用3σ原则处理绿色债券的收益率数据,结果把很多高收益债券都标记成了异常。后来才发现,收益率数据是典型的厚尾分布,3σ原则根本不适用。从那以后,我每次用3σ之前都会先画个直方图看看分布形态。
2. IQR方法(四分位距法)
这个方法不依赖正态分布假设,更稳健。它的逻辑是:把低于Q1-1.5×IQR或高于Q3+1.5×IQR的值视为异常。
Q1 = df['碳排放强度'].quantile(0.25)
Q3 = df['碳排放强度'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
df['is_outlier_iqr'] = (df['碳排放强度'] < lower_bound) | (df['碳排放强度'] > upper_bound)
我个人习惯用IQR方法处理绿色金融数据。你想想看,碳排放、绿色评级这些数据,哪有什么正态分布可言?IQR方法对异常值不那么敏感,更适合真实世界的脏数据。
💡 避坑指南:我曾经处理过一组绿色基金收益率数据,用IQR方法检测出大量异常值。后来一查,原来是基金分红导致的收益率突变。所以记住:异常值检测只是第一步,业务验证才是关键。别一看到异常就删,先问问业务同事「这合理吗」。
三、重复值处理:看似简单,实则暗藏玄机
重复值处理听起来最简单——删掉重复行不就完了?但绿色金融数据里,重复值的定义往往没那么清晰。
我遇到过几种情况:
- 完全重复:所有字段都一样,这种直接删
- 部分重复:比如同一家公司、同一时间点,但数据来源不同导致数值略有差异
- 业务重复:比如同一笔绿色债券在多个数据库中被重复记录
# 检查完全重复
df.duplicated().sum()
# 删除完全重复
df.drop_duplicates(inplace=True)
# 基于关键字段去重(保留第一条)
df.drop_duplicates(subset=['公司代码', '报告年份'], keep='first', inplace=True)
# 基于关键字段去重(取均值)
df_grouped = df.groupby(['公司代码', '报告年份']).agg({'碳排放强度': 'mean'}).reset_index()
关键点:去重之前,一定要想清楚「重复」的定义是什么。对于绿色金融数据,我通常用「公司代码+报告年份」作为唯一标识。如果同一家公司同一年有多个碳排放数据,我会取均值——但前提是这些数据来自可靠来源。
嗯,最后说一句:数据清洗没有银弹。每个方法都有适用场景,关键是你得理解你的数据。我做了这么多年,最大的体会就是——别让工具替你思考。代码是死的,但业务逻辑是活的。
公众号:蓝海资料掘金营,微信deep3321