第二章 数据获取与清洗:ESG数据的“脏活累活”

各位同学,欢迎来到第二章。上一章我们聊了ESG因子的理论基础,今天咱们来点实在的——数据。

做量化的人都知道一句话:“Garbage in, garbage out。” 你模型再漂亮,数据是垃圾,结果就是废纸。ESG数据尤其如此。它不像股价数据那样标准化、高频、干净。ESG数据来源杂、口径乱、缺失多、更新慢。我刚开始做ESG策略时,就被数据坑过好几次。

所以这一章,我带你走一遍ESG数据获取与清洗的全流程。说白了,就是教你怎么把“脏数据”变成“干净因子”。

核心知识点:

  • 三大主流ESG数据源:MSCI、Sustainalytics、商道融绿
  • 数据缺失值处理:别只会删行
  • 异常值检测与修正:识别“假信号”
ESG数据清洗流程 数据获取 MSCI / Sustainalytics / 商道融绿 缺失值处理 删除 / 均值填充 / 行业均值 异常值检测 IQR / Z-score / 业务规则 因子 缺失值处理策略 • 直接删除(慎用) • 均值/中位数填充 • 行业均值填充(推荐) 异常值检测方法 • IQR法(常用) • Z-score法 • 业务规则校验 数据清洗是ESG因子构建中最耗时、最关键的环节

2.1 常见ESG数据源:三足鼎立

ESG数据源很多,但主流就三家:MSCI、Sustainalytics、商道融绿。我分别说说它们的特点和坑。

2.1.1 MSCI ESG评级

MSCI是行业老大哥。它的ESG评级覆盖全球8000多家公司,数据历史长,方法论成熟。我个人习惯用MSCI的数据做回测,因为它的因子构建逻辑比较透明。

MSCI的评级从AAA到CCC,共7档。它把ESG拆成10个主题、35个关键指标。比如环境主题下,有碳排放、水资源管理、生物多样性等。

注意: MSCI的数据更新频率是季度,不是实时。你拿到的数据可能滞后1-2个月。做高频策略的同学,这点要心里有数。

2.1.2 Sustainalytics

Sustainalytics被Morningstar收购后,影响力越来越大。它的特色是“ESG风险评级”,不是打分,而是评估公司面临的ESG风险有多大。风险等级从可忽略到严重,共5档。

我更喜欢Sustainalytics的一点是,它提供了“争议性事件”的实时监控。比如某公司爆出环境污染丑闻,Sustainalytics会很快更新风险评分。这对事件驱动策略很有用。

我的经验: 如果你做的是长期配置策略,用MSCI更合适。如果你做的是事件驱动或短期交易,Sustainalytics的争议事件数据更有价值。

2.1.3 商道融绿

商道融绿是国内ESG评级的头部机构。它覆盖A股上市公司,评级从A+到D,共10档。它的方法论更贴合中国市场的特点,比如把“乡村振兴”、“共同富裕”等政策因素纳入考量。

我记得2021年做A股ESG策略时,发现MSCI对很多A股公司的覆盖不全,尤其是中小盘股。后来换成商道融绿的数据,覆盖率从60%提升到90%以上。嗯,这就是本土数据源的优势。

数据源 覆盖范围 更新频率 核心指标 适用场景
MSCI 全球8000+公司 季度 AAA-CCC评级 长期配置、因子回测
Sustainalytics 全球12000+公司 月度+事件驱动 风险评分+争议事件 事件驱动、风险管理
商道融绿 A股4000+公司 季度 A+到D评级 A股策略、本土化研究

2.2 数据缺失值处理:别只会删行

拿到ESG数据后,第一件事就是检查缺失值。你会发现,缺失率可能高达30%-50%。尤其是中小盘股,很多公司根本不披露ESG数据。

新手最容易犯的错误是:直接删除有缺失的行。这会导致样本偏差——你留下的都是大公司、好公司,回测结果当然漂亮,但实盘就露馅了。

我处理缺失值的思路是分层处理:

  1. 缺失率 < 5%: 直接删除。影响不大。
  2. 缺失率 5%-30%: 用行业均值填充。比如某家化工公司缺失碳排放数据,就用化工行业的均值来填。
  3. 缺失率 > 30%: 考虑用模型预测填充,或者干脆把这个因子拆成“有数据”和“无数据”两个子因子。

避坑指南: 我曾经用全局均值填充缺失值,结果回测效果奇好。后来发现,因为金融行业ESG评分普遍高,全局均值被拉高了,导致非金融公司的评分虚高。从那以后,我坚持用行业均值填充。

下面是一个简单的Python代码示例,演示如何用行业均值填充缺失值:

import pandas as pd
import numpy as np

# 假设df包含三列:'stock_id', 'industry', 'esg_score'
df = pd.read_csv('esg_data.csv')

# 计算每个行业的ESG均值
industry_mean = df.groupby('industry')['esg_score'].mean()

# 用行业均值填充缺失值
df['esg_score_filled'] = df.apply(
    lambda row: industry_mean[row['industry']] 
    if pd.isna(row['esg_score']) 
    else row['esg_score'],
    axis=1
)

print(f"填充前缺失率: {df['esg_score'].isna().mean():.2%}")
print(f"填充后缺失率: {df['esg_score_filled'].isna().mean():.2%}")

2.3 异常值检测与修正:识别“假信号”

ESG数据里经常出现异常值。比如某公司突然从B级跳到AAA级,或者碳排放数据突然暴增10倍。这些异常值可能是数据录入错误,也可能是公司发生了重大事件。

你想想看,如果不处理这些异常值,你的因子信号就会失真。可能一个假信号就让你开仓了,结果亏得底朝天。

我常用的异常值检测方法有三种:

2.3.1 IQR法(四分位距法)

这是最常用的方法。计算数据的Q1(25%分位数)和Q3(75%分位数),IQR = Q3 - Q1。一般认为,小于Q1 - 1.5*IQR或大于Q3 + 1.5*IQR的值是异常值。

我个人习惯用3倍IQR作为阈值,因为ESG数据的分布比较宽,1.5倍会误杀太多正常值。

2.3.2 Z-score法

Z-score = (值 - 均值) / 标准差。通常认为|Z| > 3是异常值。这个方法对正态分布的数据效果好,但ESG数据往往偏态分布,所以我会先做对数变换。

2.3.3 业务规则校验

这是最容易被忽略的。比如ESG评级不可能超过AAA,碳排放数据不可能为负数。这些业务规则可以直接过滤掉明显错误的数据。

警告: 不要机械地使用统计方法。我曾经用Z-score检测出某公司碳排放数据异常,准备修正。后来一查,原来是该公司收购了一家高污染工厂,数据是真实的。所以,异常值不一定是错误值,要结合业务背景判断。

下面是一个综合异常值检测的代码示例:

import numpy as np
import pandas as pd

def detect_outliers(df, column, method='iqr', threshold=3.0):
    """
    异常值检测函数
    method: 'iqr' 或 'zscore'
    """
    if method == 'iqr':
        Q1 = df[column].quantile(0.25)
        Q3 = df[column].quantile(0.75)
        IQR = Q3 - Q1
        lower = Q1 - threshold * IQR
        upper = Q3 + threshold * IQR
        outliers = (df[column] < lower) | (df[column] > upper)
    elif method == 'zscore':
        z = np.abs((df[column] - df[column].mean()) / df[column].std())
        outliers = z > threshold
    else:
        raise ValueError("method must be 'iqr' or 'zscore'")
    
    return outliers

# 使用示例
df = pd.read_csv('esg_data.csv')
outliers = detect_outliers(df, 'esg_score', method='iqr', threshold=3.0)
print(f"检测到 {outliers.sum()} 个异常值")

# 修正:用中位数替换异常值
median_val = df.loc[~outliers, 'esg_score'].median()
df.loc[outliers, 'esg_score'] = median_val

2.4 数据清洗的实战心得

说了这么多,最后分享几点实战心得:

  • 数据对齐: 不同数据源的日期格式、股票代码可能不同。我习惯统一转换成YYYY-MM-DD格式,股票代码用ISIN或SEDOL。
  • 版本控制: ESG数据经常有回溯调整。比如MSCI会修正历史评级。我建议每次更新数据后,都保留一份原始备份。
  • 自动化脚本: 数据清洗流程最好写成脚本,每周自动跑一次。手动处理太容易出错了。

我的习惯: 每次清洗完数据,我都会随机抽10只股票,手动核对原始数据和清洗后的数据。虽然费时间,但能发现很多自动化流程遗漏的问题。

好了,这一章的内容就到这里。数据清洗虽然枯燥,但它是整个ESG因子策略的基石。你花在数据上的时间,会在回测和实盘中加倍回报给你。


公众号:蓝海数据掘金营,微信deep3321