问题描述
解决方案
在 JoinQuant 聚宽平台中,使用中位数方法去除因子数据的极值(也称为绝对中位差法 MAD),可以使用 jqfactor 库中的 winsorize_med 函数。
这种方法通过计算数据的中位数(Median)和绝对中位差(MAD),将超出 [Median - n * MAD, Median + n * MAD] 范围的值替换为边界值或 NaN。相比于标准差法,中位数法对异常值更不敏感,因此更加稳健。
以下是具体的代码实现和参数说明。
代码实现
# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
from jqfactor import winsorize_med
# 1. 构造示例数据 (模拟一个因子在截面上的数据,包含极值)
# 假设 index 为股票代码,values 为因子值
data = pd.Series({
'000001.XSHE': 1.2,
'000002.XSHE': 1.3,
'000004.XSHE': 1.25,
'000005.XSHE': 100.0, # 明显的极大异常值
'000006.XSHE': -50.0, # 明显的极小异常值
'000007.XSHE': 1.1,
'000008.XSHE': 1.4
})
print("原始数据:")
print(data)
# 2. 使用 winsorize_med 进行中位数去极值
# scale=3 表示保留中位数上下 3 倍 MAD 范围内的数据(类似于 3倍标准差)
# inclusive=True 表示将超出的数值替换为边界值(Winsorization),False 则替换为 NaN (Trimming)
processed_data = winsorize_med(data, scale=3, inclusive=True, inf2nan=True)
print("\n去极值后的数据:")
print(processed_data)
# -------------------------------------------------------
# 如果处理的是 DataFrame (例如多只股票、多个因子的数据)
df = pd.DataFrame({
'factor_A': [1.2, 1.3, 100.0, -50.0, 1.1],
'factor_B': [0.5, 0.6, 0.55, 200.0, 0.52]
}, index=['000001.XSHE', '000002.XSHE', '000005.XSHE', '000006.XSHE', '000007.XSHE'])
# axis=0 表示对每一列(即每个因子)单独进行去极值处理
df_processed = winsorize_med(df, scale=3, inclusive=True, axis=0)
print("\nDataFrame 去极值结果 (axis=0):")
print(df_processed)
函数说明
winsorize_med(series, scale=1, inclusive=True, inf2nan=True, axis=1)
参数详解
-
data (series):
- 输入数据,支持
pd.Series、pd.DataFrame或np.array。通常是某一截面上所有股票的因子值。
- 输入数据,支持
-
scale (float):
- 默认值: 1.0
- 含义: 缩尾范围的倍数。边界计算公式为
[median - scale * distance, median + scale * distance]。 - 建议: 在实际量化研究中,通常设置为 3.0 或 5.0,以保留绝大部分正常数据,仅剔除极端离群点。
-
inclusive (bool):
- 默认值:
True - True: 将超出边界的值替换为边界值(即 Winsorization,缩尾)。
- False: 将超出边界的值替换为
np.nan(即 Trimming,截尾)。
- 默认值:
-
inf2nan (bool):
- 默认值:
True - 含义: 是否在处理前将
np.inf和-np.inf替换为np.nan。建议保持为 True,因为无穷大值会影响中位数的计算逻辑(尽管中位数本身对极值不敏感,但处理 NaN 比处理 Inf 更安全)。
- 默认值:
-
axis (int):
- 默认值: 1
- 仅当输入数据为
pd.DataFrame时生效。 - 0: 对每一列做去极值(通常用于 DataFrame 的列是不同因子,行是股票的情况,或者列是股票,行是时间的情况,具体取决于你的数据结构)。
- 1: 对每一行做去极值。
常见问题 Q&A
Q: 为什么要使用中位数去极值而不是标准差去极值?
A: 标准差去极值(3-sigma法)依赖于均值和标准差,而这两个统计量本身极易受到极端值的影响(例如一个巨大的异常值会拉高均值和标准差,导致原本的异常值被判定为正常)。中位数去极值利用中位数(Median)和绝对中位差(MAD),这两个统计量具有很高的崩溃点(Breakdown Point),对异常值非常稳健,因此在处理金融数据(通常包含肥尾分布)时效果更好。
Q: scale 参数设置多少比较合适?
A: scale 类似于正态分布中的标准差倍数。在正态分布下,1个标准差约等于 1.4826 倍的 MAD。因此,如果你习惯使用 3倍标准差(3-sigma)去极值,在使用 winsorize_med 时,可以将 scale 设置为 3 或 3 * 1.4826 (约 4.5) 左右。通常直接使用 3 或 5 也是常见的经验值。
Q: 处理后的数据还需要做标准化吗?
A: 是的。去极值只是第一步,目的是防止异常值扭曲后续的统计特性。去极值后,通常还需要进行标准化(Standardization,如 Z-Score),将因子值缩放到统一的量纲(例如均值为0,标准差为1),以便于不同因子之间的比较和合成。可以使用 jqfactor.standardlize 函数。