4. 数据清洗与预处理:ESG数据缺失值处理、标准化、行业调整
各位同学,咱们今天聊聊ESG数据预处理。说实话,这步活儿看着不起眼,但我在项目里吃过不少亏。你想想看,ESG数据有多乱?有的公司披露了碳排放,有的只给了个评级,还有的干脆啥都没写。直接拿这种数据跑模型,结果肯定一塌糊涂。
我个人习惯,拿到ESG数据后先做三件事:补缺失、做标准化、调行业差异。这三步走完,数据才算能用。下面我一个个说。
4.1 缺失值处理:别让空值毁了你的模型
ESG数据的缺失率有多高?我见过最夸张的,某个指标缺失率超过70%。这时候你要是直接删掉,样本量就没了。要是用均值填充,又会引入偏差。怎么办?
我常用的方法分三种情况:
- 缺失率 < 5%:直接删除。影响不大,省事。
- 缺失率 5% - 30%:用行业均值或中位数填充。注意,是行业均值,不是全局均值。不同行业的ESG表现差异很大,用全局均值会抹平行业特征。
- 缺失率 > 30%:考虑用模型预测填充,或者干脆把这个指标拆成哑变量。我曾经在做一个MSCI ESG评级的项目时,某个子指标缺失了40%,我最后把它拆成了“是否披露”和“披露值”两个变量,效果反而更好。
核心原则:缺失值处理不是技术问题,是业务判断。你得先搞清楚数据为什么缺失——是公司没披露,还是数据收集遗漏?前者可能暗示公司治理问题,后者只是技术问题。处理方式完全不同。
代码实现上,我习惯用pandas这样处理:
import pandas as pd
import numpy as np
# 假设df是ESG数据,'esg_score'列有缺失值
# 按行业分组填充中位数
df['esg_score'] = df.groupby('industry')['esg_score'].transform(
lambda x: x.fillna(x.median())
)
# 对于缺失率高的指标,创建哑变量
df['esg_disclosed'] = df['esg_score'].notna().astype(int)
我的小技巧:填充后一定要检查分布。我曾经用均值填充后,发现数据分布从双峰变成了单峰,明显不合理。后来改用中位数,分布才正常。所以,填充完记得画个直方图看看。
4.2 标准化:让不同量纲的数据能放在一起比
ESG数据里,有的指标是百分比(比如女性员工占比),有的是绝对值(比如碳排放吨数),还有的是评分(1-10分)。这些数据量纲不同,直接放一起做回归,系数没法解释。
我常用的标准化方法有两种:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Z-score标准化 | (x - μ) / σ | 数据近似正态分布,适合回归模型 |
| Min-Max归一化 | (x - min) / (max - min) | 数据有明确边界,适合评分类指标 |
我个人更倾向Z-score。为什么?因为ESG数据经常有极端值,Min-Max会被极端值压缩到很小的区间。而Z-score对极端值没那么敏感。当然,如果你做的是聚类分析,Min-Max可能更合适,因为它保留了原始数据的相对距离。
代码示例:
from sklearn.preprocessing import StandardScaler
# Z-score标准化
scaler = StandardScaler()
df['esg_score_std'] = scaler.fit_transform(df[['esg_score']])
# 注意:标准化要在训练集上fit,再transform测试集
# 避免数据泄露
避坑指南:我曾经在时间序列数据上犯过错误——用全量数据做标准化,结果把未来信息泄露到了过去。正确的做法是:用滚动窗口的方式,只基于历史数据做标准化。这一点在量化策略里尤其重要。
4.3 行业调整:消除行业间的系统性差异
这是ESG数据处理里最容易被忽略的一步。你想想看,银行和钢铁厂的碳排放能一样吗?银行几乎没直接排放,钢铁厂是排放大户。如果你不做行业调整,模型会认为钢铁厂的ESG表现差,但这其实是行业特性决定的。
行业调整的核心思路是:把每个公司的ESG指标减去行业均值,再除以行业标准差。这样得到的是“相对于同行业的表现”,而不是绝对表现。
我常用的调整方法:
- 行业均值调整:每个指标减去行业均值。适合比较同一行业内的公司。
- 行业分位数调整:计算每个公司在行业内的分位数排名。适合评级类数据。
- 行业回归调整:用行业哑变量做回归,取残差作为调整后的值。这是最严谨的方法,但计算量也最大。
代码实现:
# 行业均值调整
df['esg_adj'] = df.groupby('industry')['esg_score'].transform(
lambda x: x - x.mean()
)
# 行业分位数调整
df['esg_rank'] = df.groupby('industry')['esg_score'].rank(pct=True)
关键点:行业调整后,不同行业的公司就可以直接比较了。比如,调整后的ESG得分,银行A的0.5和钢铁厂B的0.5,代表它们在各自行业内的相对表现是相同的。这一点在做跨行业投资组合时特别有用。
4.4 整体流程:一张图说清楚
下面我用一张流程图,把整个数据预处理的流程串起来。你跟着走一遍,基本不会出错。
这张图看着简单,但每一步都有坑。我建议你每次处理数据时,都按这个流程走一遍,然后检查每一步的输出。尤其是行业调整那步,很多人会忘,结果模型跑出来全是行业哑变量在起作用,真正的ESG信号反而被淹没了。
我的经验:做完预处理后,一定要做一次数据质量检查。我通常会看三样东西:缺失率是否降到了可接受范围、标准化后的数据分布是否合理、行业调整后各行业的均值是否接近0。这三样都过了,数据才算合格。
好了,数据预处理这块就讲这么多。记住,ESG分析里,数据质量决定了模型的天花板。你模型再花哨,数据是垃圾,结果就是垃圾。所以,别嫌预处理麻烦,这步做好了,后面事半功倍。