查看全集:💎Quantopia量化分析56讲
回归模型帮助我们理解变量之间的关系,但由于多种因素,其系数可能不稳定。本教程探讨三个主要原因:小样本偏差噪声、结构突变和多重共线性。
在小数据集中,随机噪声可能对回归系数产生不成比例的影响。随着样本量增大,系数会趋于稳定。
import numpy as np
import statsmodels.api as sm
import matplotlib.pyplot as plt
def linreg(X, Y):
X = sm.add_constant(X) # 添加截距项
model = sm.OLS(Y, X).fit()
return model.params[0], model.params[1] # 截距, 斜率
# 生成20个随机数据点
np.random.seed(107)
small_data = np.random.randn(20)
xs_small = np.arange(20)
a, b = linreg(xs_small, small_data)
# 可视化
plt.scatter(xs_small, small_data, alpha=0.7)
plt.plot(xs_small, xs_small*b + a, 'r')
plt.title("小样本回归演示");
生成1000个随机点观察系数变化:
large_data = np.random.randn(1000)
xs_large = np.arange(1000)
a_large, b_large = linreg(xs_large, large_data)
print(f"大样本斜率:{b_large:.4f}")
当变量间关系发生突变时,模型在突变前后会失效。
import yfinance as yf
# 获取数据
data = yf.download('SPY', start='2003-01-01', end='2009-02-01')
prices = data['Close']
# 设置断点(约2007年6月)
breakpoint = 1200
pre_break = prices.iloc[:breakpoint]
post_break = prices.iloc[breakpoint:]
# 分段回归
X_full = np.arange(len(prices))
a_full, b_full = linreg(X_full, prices)
X_pre = np.arange(len(pre_break))
a_pre, b_pre = linreg(X_pre, pre_break)
X_post = np.arange(len(post_break))
a_post, b_post = linreg(X_post, post_break)
# 可视化
plt.figure(figsize=(10,6))
prices.plot(label='SPY价格')
plt.plot(prices.index, X_full*b_full + a_full, 'y', label='全段回归')
plt.plot(pre_break.index, X_pre*b_pre + a_pre, 'r', label='断点前')
plt.plot(post_break.index, X_post*b_post + a_post, 'r', label='断点后')
plt.legend();
使用CUSUM检验结构突变:
from statsmodels.stats.diagnostic import breaks_cusumolsresid
full_model = sm.OLS(prices, sm.add_constant(X_full)).fit()
print(f"CUSUM检验p值:{breaks_cusumolsresid(full_model.resid)[1]:.2e}")
当预测变量高度相关时,微小数据变化会导致系数大幅波动。
# 获取三组数据
data = yf.download(['SPY', 'MDY', 'V'], start='2013-01-01', end='2015-06-01')['Close']
# 数据清洗
b1 = data['SPY'].dropna()
b2 = data['MDY'].dropna()
asset = data['V'].dropna()
# 计算相关系数
corr = b1.corr(b2)
print(f"SPY-MDY相关系数:{corr:.2f}")
# 多元回归函数
def multiple_linreg(Y, X1, X2):
X = sm.add_constant(np.column_stack((X1, X2)))
model = sm.OLS(Y, X).fit()
return model.params
# 分时段回归
params1 = multiple_linreg(asset['2013':'2015'], b1['2013':'2015'], b2['2013':'2015'])
params2 = multiple_linreg(asset, b1, b2)
print(f"2013-2015年系数:SPY={params1[1]:.2f}, MDY={params1[2]:.2f}")
print(f"延长时段系数:SPY={params2[1]:.2f}, MDY={params2[2]:.2f}")
计算方差膨胀因子(VIF):
from statsmodels.stats.outliers_influence import variance_inflation_factor
X = sm.add_constant(np.column_stack((b1, b2)))
vifs = [variance_inflation_factor(X, i) for i in range(1, X.shape[1])]
print(f"方差膨胀因子:{vifs}")
下一步建议:探索岭回归/Lasso回归等正则化方法,学习时间序列交叉验证技巧。