🚀 AI 一键生成 joinquant 策略代码

如何使用 JoinQuant 的 winsorize 函数对因子数据进行去极值处理?

joinquant | 发布时间: 2025/12/12
以下内容由 EasyQuant 生成。

问题描述

如何对我的因子数据进行去极值处理?

解决方案

在聚宽(JoinQuant)平台中,对因子数据进行去极值(Winsorization)处理主要使用 jqfactor 库提供的两个函数:winsorizewinsorize_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% 分位数的那个值,而不是被删除或置为空值。这保证了数据形状的完整性。