🚀 AI 一键生成 joinquant 策略代码

JoinQuant 因子预处理:详解 winsorize_med 函数的使用

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

问题描述

如何使用中位数方法去除因子数据中的极值?

解决方案

在 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)

参数详解

  1. data (series):

    • 输入数据,支持 pd.Seriespd.DataFramenp.array。通常是某一截面上所有股票的因子值。
  2. scale (float):

    • 默认值: 1.0
    • 含义: 缩尾范围的倍数。边界计算公式为 [median - scale * distance, median + scale * distance]
    • 建议: 在实际量化研究中,通常设置为 3.05.0,以保留绝大部分正常数据,仅剔除极端离群点。
  3. inclusive (bool):

    • 默认值: True
    • True: 将超出边界的值替换为边界值(即 Winsorization,缩尾)。
    • False: 将超出边界的值替换为 np.nan(即 Trimming,截尾)。
  4. inf2nan (bool):

    • 默认值: True
    • 含义: 是否在处理前将 np.inf-np.inf 替换为 np.nan。建议保持为 True,因为无穷大值会影响中位数的计算逻辑(尽管中位数本身对极值不敏感,但处理 NaN 比处理 Inf 更安全)。
  5. 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 设置为 33 * 1.4826 (约 4.5) 左右。通常直接使用 35 也是常见的经验值。

Q: 处理后的数据还需要做标准化吗?
A: 是的。去极值只是第一步,目的是防止异常值扭曲后续的统计特性。去极值后,通常还需要进行标准化(Standardization,如 Z-Score),将因子值缩放到统一的量纲(例如均值为0,标准差为1),以便于不同因子之间的比较和合成。可以使用 jqfactor.standardlize 函数。