🚀 AI 一键生成 joinquant 策略代码

JoinQuant 因子处理教程:winsorize, standardlize, neutralize 函数应用

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

问题描述

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

解决方案

在JoinQuant聚宽平台中,因子数据的预处理(去极值、中性化、标准化)主要通过 jqfactor 库中的函数来实现。

以下是关于如何进行去极值中性化处理的详细说明及代码示例。

1. 去极值 (Winsorization)

去极值的目的是剔除或平滑数据中的异常值,防止极端值对后续的统计分析(如回归)产生过大影响。聚宽提供了两种主要方法:

  • winsorize_med (推荐):基于中位数和绝对中位差(MAD)去极值。这种方法对异常值更稳健。
  • winsorize:基于标准差(如3倍标准差)或分位数(如1%和99%分位)去极值。

代码示例

from jqfactor import winsorize_med, winsorize

# 假设 factor_data 是一个 Series,索引为股票代码,值为因子值

# 方法一:中位数去极值 (推荐)
# scale=3 表示将大于 中位数 + 3 * MAD 的值替换为边界值
factor_win_med = winsorize_med(factor_data, scale=3, inclusive=True, inf2nan=True)

# 方法二:分位数去极值
# 将数据限制在 2.5% 到 97.5% 的分位数之间
factor_win_quant = winsorize(factor_data, qrange=[0.025, 0.975], inclusive=True, inf2nan=True)

# 方法三:标准差去极值
# 将数据限制在 3倍标准差范围内
factor_win_std = winsorize(factor_data, scale=3, inclusive=True, inf2nan=True)

2. 中性化 (Neutralization)

中性化的目的是剔除因子中包含的行业风险市值风险,使因子纯粹反映其本身的选股能力。

  • neutralize:通过线性回归取残差的方式,剔除指定的风险暴露。
  • how 参数:指定要剔除的风险因子。常用 ['jq_l1', 'market_cap'](剔除聚宽一级行业和市值影响)。

代码示例

from jqfactor import neutralize

# date 参数非常重要,用于获取当天的行业分类和市值数据
target_date = '2023-01-05'

# 对去极值后的数据进行中性化
# how=['jq_l1', 'market_cap'] 表示同时进行行业中性化和市值中性化
factor_neu = neutralize(factor_win_med, how=['jq_l1', 'market_cap'], date=target_date)

3. 完整综合示例

通常的数据处理流程是:获取数据 -> 去极值 -> 中性化 -> 标准化。以下是一个完整的可运行代码示例:

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
from jqdata import get_index_stocks, get_factor_values
from jqfactor import winsorize_med, neutralize, standardlize

def process_factor_example():
    # 1. 设定日期和股票池 (以沪深300为例)
    date = '2023-05-10'
    securities = get_index_stocks('000300.XSHG', date)
    
    # 2. 获取因子数据 (以市盈率 pe_ratio 为例)
    # get_factor_values 返回的是字典,我们需要提取出 Series
    factor_dict = get_factor_values(securities, ['pe_ratio'], end_date=date, count=1)
    # 获取当天的因子值 Series,索引为股票代码
    raw_factor = factor_dict['pe_ratio'].iloc[0]
    
    # 打印原始数据统计
    print("原始数据均值: %.4f, 标准差: %.4f" % (raw_factor.mean(), raw_factor.std()))
    
    # 3. 去极值 (使用中位数法,3倍MAD)
    # inf2nan=True 会将无穷大值视为NaN处理
    factor_win = winsorize_med(raw_factor, scale=3, inclusive=True, inf2nan=True)
    
    print("去极值后均值: %.4f, 标准差: %.4f" % (factor_win.mean(), factor_win.std()))
    
    # 4. 中性化 (剔除行业和市值影响)
    # 注意:必须传入 date 参数,以便函数内部获取当天的行业和市值数据
    # how 参数支持 'jq_l1'(聚宽一级行业), 'sw_l1'(申万一级), 'market_cap'(市值) 等
    factor_neu = neutralize(factor_win, how=['jq_l1', 'market_cap'], date=date)
    
    print("中性化后均值: %.4f, 标准差: %.4f" % (factor_neu.mean(), factor_neu.std()))
    
    # 5. 标准化 (Z-Score标准化)
    # 将数据转化为均值为0,标准差为1的分布,便于不同因子间比较
    factor_std = standardlize(factor_neu)
    
    print("标准化后均值: %.4f, 标准差: %.4f" % (factor_std.mean(), factor_std.std()))
    
    return factor_std

# 运行示例
final_factor = process_factor_example()
print("\n处理后的前5个数据:")
print(final_factor.head())

关键参数说明

  1. winsorize_med:
    • scale: 默认为1。通常设置为 3 或 5。
    • inclusive: 是否将越界值替换为边界值(True)还是设置为 NaN(False)。通常设为 True
  2. neutralize:
    • how: 列表。
      • 行业:'jq_l1' (聚宽一级), 'sw_l1' (申万一级)。
      • 市值:'market_cap' (线性市值), 'ln_market_cap' (对数市值)。
    • date: 必填。用于对齐行业和市值数据的时间。请避免使用 datetime.date.today(),应使用回测或研究中的具体历史日期。

Q&A

Q: 为什么中性化需要传入 date 参数?
A: 中性化通常需要剔除行业和市值的影响。股票的行业分类可能会变更,市值每天都在变化。函数需要 date 参数来查询该日期当天对应的行业分类和市值数据,以构建回归模型计算残差。

Q: 去极值应该在中性化之前还是之后?
A: 通常建议在中性化之前进行去极值。因为极值(异常值)会严重影响中性化回归模型的参数估计(斜率),导致得到的残差(即中性化后的因子)失真。

Q: winsorizewinsorize_med 哪个更好?
A: 对于金融数据,通常推荐使用 winsorize_med。因为均值和标准差本身容易受极值影响,而中位数(Median)和绝对中位差(MAD)对异常值更不敏感(Robust),能更有效地识别真正的离群点。