问题描述
解决方案
在聚宽(JoinQuant)平台中,对因子数据进行去极值(Winsorization)处理主要使用 jqfactor 库提供的两个函数:winsorize 和 winsorize_med。
这两个函数分别对应分位数/标准差去极值和中位数去极值(MAD法)。
1. 使用 winsorize 函数
该函数支持通过标准差倍数、固定数值范围或分位数范围来处理极值。最常用的是通过分位数(如 1% 和 99%)进行缩尾处理。
函数签名:
winsorize(series, scale=None, range=None, qrange=None, inclusive=True, inf2nan=True, axis=1)
常用参数说明:
- data: 输入的 Series 或 DataFrame。
- qrange: 列表,例如
[0.05, 0.95],表示将小于 5% 分位数的值替换为 5% 分位数的值,将大于 95% 分位数的值替换为 95% 分位数的值。 - scale: 标准差倍数。例如
scale=3表示保留平均值正负 3 倍标准差内的数据。 - axis: 默认为 1。当输入为 DataFrame 时,
1表示对每行(通常是同一时间截面下的不同股票)做去极值,0表示对每列做去极值。
2. 使用 winsorize_med 函数
该函数使用中位数绝对偏差(MAD)法去极值。这种方法对异常值更稳健,因为它基于中位数而不是均值。
函数签名:
winsorize_med(series, scale=1, inclusive=True, inf2nan=True, axis=1)
常用参数说明:
- scale: 倍数,默认为 1。会将位于
[中位数 - scale * 距离, 中位数 + scale * 距离]边界之外的值替换为边界值。 - inf2nan: 是否将无穷大值替换为 NaN,默认为 True。
代码示例
以下代码展示了如何创建一个模拟的因子数据 DataFrame,并分别使用上述两种方法进行去极值处理。
# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
from jqfactor import winsorize, winsorize_med
# 1. 模拟生成因子数据
# 假设有3个时间点,5只股票,数据中包含一些极端值
data = pd.DataFrame({
'stock_1': [1.0, 100.0, 1.2], # 100.0 是极端值
'stock_2': [0.9, 0.8, -50.0], # -50.0 是极端值
'stock_3': [1.1, 1.0, 0.9],
'stock_4': [1.05, 0.95, 1.1],
'stock_5': [0.98, 1.02, 1.0]
}, index=['2023-01-01', '2023-01-02', '2023-01-03'])
print("原始数据:")
print(data)
print("-" * 30)
# 2. 使用 winsorize 进行分位数去极值 (保留 5% - 95% 之间的数据)
# axis=1 表示在横截面上(即每一行,同一时间的不同股票间)进行处理
data_quantile = winsorize(data, qrange=[0.05, 0.95], axis=1)
print("分位数去极值 (5%-95%):")
print(data_quantile)
print("-" * 30)
# 3. 使用 winsorize_med 进行中位数去极值 (3倍中位数绝对偏差)
# 这种方法通常能更好地保留数据的分布形态,同时去除离群点
data_med = winsorize_med(data, scale=3, axis=1)
print("中位数去极值 (3倍MAD):")
print(data_med)
总结
- 如果你希望严格限制数据在某个百分比范围内(如去除头部和尾部各 2.5%),请使用
winsorize配合qrange参数。 - 如果你希望基于统计分布的稳健性来识别离群点(不强制去除固定比例的数据),请使用
winsorize_med。
Q&A
Q: winsorize 函数中的 axis 参数应该设为 0 还是 1?
A: 这取决于你的数据结构和处理逻辑。如果你的 DataFrame 行是日期、列是股票代码,且你希望在同一时间截面下对所有股票的因子值去极值,应设置 axis=1(默认值)。如果你希望对单只股票的历史时间序列进行去极值,应设置 axis=0。
Q: 数据中包含 np.inf (无穷大) 时会报错吗?
A: 默认情况下不会报错。这两个函数都有一个参数 inf2nan=True,它会在处理前自动将正负无穷大 (inf, -inf) 替换为 NaN(空值),从而避免计算错误。
Q: 去极值处理后,边界之外的数据是被删除还是被替换?
A: 是被替换(Clipping)。例如使用 qrange=[0.05, 0.95] 时,小于 5% 分位数的数值会被强制修改为 5% 分位数的那个值,而不是被删除或置为空值。这保证了数据形状的完整性。