import numpy as np # 计算有限值筛选、对数变换与绘图坐标
import pandas as pd # 构造2023Q4公司横截面并保留公司键
import matplotlib.pyplot as plt # 展示完整样本拟合与固定绘图子样本
import statsmodels.api as sm # 估计OLS系数并生成HC3稳健推断
import os # 从环境变量取得可移植的数据根
from pathlib import Path # 用路径对象解析数据根目录
# 统一中文标签与负号的出版显示
plt.rcParams['font.family'] = ['Source Han Serif SC'] # 统一图形中的中文出版字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示为方块的问题3 线性回归 (Linear Regression)
3.1 导读
线性回归把一个响应变量的条件均值写成若干预测变量的线性组合。它既是可解释的预测基线,也是理解估计、标准误、假设检验和模型诊断的入口。本章以中国上市公司费用与营收的真实财务数据为主线,区分样本中的条件关联、总体参数和因果效应,并说明何时应采用稳健标准误、非线性项或正则化替代方案。
3.2 学习目标
完成本章后,读者应能够:
- 从残差平方和推导简单 OLS 估计量与矩阵正规方程。
- 正确解释多元回归系数、标准误、置信区间与 \(p\) 值的成立条件。
- 区分 \(R^2\)、残差标准误和样本外预测损失的用途。
- 识别异方差、非线性、异常点和共线性,并选择有条件的补救方法。
- 写出含虚拟变量、非线性项和交互项的模型,并限定其商业解释边界。
3.3 引言 (Introduction)
线性回归(linear regression)是统计学习中常用的监督学习方法。它计算高效,系数在模型设定下可解释,并为更复杂方法提供透明基线;这些优点不意味着线性形式总是正确,也不意味着回归系数自动具有因果含义。
3.3.1 线性回归在经济金融领域的典型应用
线性回归在经济金融领域有多种典型用途,以下场景说明不同回归系数所对应的估计对象和解释边界。
应用一:资产定价与因子模型。 CAPM 时间序列回归将单个资产的超额收益对市场超额收益回归,斜率 \(\beta\) 衡量市场因子暴露 (Sharpe 1964年)。Fama–French 三因子的经验实现通常以资产超额收益为因变量,以市场、SMB 和 HML 因子收益为自变量做时间序列回归 (Fama 和 French 1993年);截面定价是另一层检验,不应与时间序列因子暴露混为一谈。
应用二:宏观经济预测与政策评估。 泰勒规则是以通胀偏离目标和产出缺口描述政策利率的反应函数 (Taylor 1993年),不是“GDP增长、通胀和货币供应量预测利率”的泛称。政策效果若要做因果解释,还需要额外的识别设计。
应用三:公司财务分析与信用评级。 Altman Z-score 原型是线性判别分数,用若干财务比率的线性组合做破产分类 (Altman 1968年);它不是用 OLS 直接估计违约概率的线性回归模型。
应用四:房地产估值与特征价格模型。 Rosen 的特征价格框架 (Rosen 1974年) 用属性(面积、楼龄、区位、学区、交通便利性等)的条件价格关联描述房屋成交价。若要量化地铁开通的因果效应,还需事件时间、对照组与可辩护的识别设计,不能由普通横截面 OLS 直接得到。
这些应用共同使用线性条件均值,却服务于不同问题:预测、条件关联或在额外识别设计下的因果估计。本章从简单线性回归开始,逐步建立多元回归、推断和诊断框架。
Note: 线性回归的理论基石
高斯-马尔可夫定理(Gauss-Markov Theorem)为线性回归提供了严谨的理论支持。该定理指出:在经典线性模型假设(Classical Linear Model Assumptions)下,OLS估计量是所有线性无偏估计量中方差最小的,即它是最佳线性无偏估计量(Best Linear Unbiased Estimator, BLUE)。
具体假设包括:
- 线性性:模型相对于参数是线性的。
- 零均值:误差项的期望值为零,\(E(\epsilon|X) = 0\)。
- 同方差:所有观测值的误差项具有相同的方差,\(\text{Var}(\epsilon|X) = \sigma^2\)。
- 无自相关:不同观测值的误差项互不相关。
- 无完全共线性:预测变量之间不存在精确的线性关系。
当这些假设满足时,OLS 在线性无偏估计量类中方差最小。这不是对所有可能估计量的无条件“最高效”声明;若存在异方差或自相关,经典方差公式与 BLUE 结论都需修改。
假设—结果对照:线性参数化和无完全共线性使 OLS 解唯一;\(E(\epsilon\mid X)=0\) 给出条件无偏性;再加同方差与无自相关才得到 Gauss–Markov 的 BLUE 结果。有限样本的精确 \(t/F\) 分布还需条件正态性;大样本渐近推断则依赖适当的矩、依赖与抽样条件,并应根据异方差或时序相关使用稳健标准误。
在本书第2章中,我们讨论了如何使用统计学习方法来估计函数\(f\),该函数将输入变量\(X\)映射到输出变量\(Y\)。线性回归假设\(f\)的形式是线性的:
\[ Y = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \cdots + \beta_p X_p + \epsilon \tag{3.1}\]
其中:
- \(Y\)是响应变量(定量变量),如公司的营业收入或股票收益率。
- \(X_1, X_2, \ldots, X_p\)是预测变量,如研发投入、宏观经济指标。
- \(\beta_0\) 是截距项,\(\beta_1, \ldots, \beta_p\)是回归系数;在模型设定成立时,它们描述保持其他变量不变的条件均值变化。只有额外因果识别假设成立时才可解释为干预效应。
- \(\epsilon\) 是误差项,捕捉了所有未包含在模型中的随机因素。
案例背景:上市公司费用与营收
本章先用 2023Q4 本地财务报表中全部满足金额与完整性条件的 A 股公司研究费用与营收关联。这里列出的费用字段也是随后受控模拟所使用的变量:
- 研发费用(R&D):用于技术创新和产品开发的费用(万元)
- 销售费用(Sales):用于市场推广和销售团队的费用(万元)
- 管理费用(Admin):用于行政管理和运营的费用(万元)
我们的目标是描述这些费用与公司营业收入(Revenue)的条件关联,并建立预测模型。任何资源配置建议都需要另行评估干预成本与因果效果。
3.4 简单线性回归 (Simple Linear Regression)
简单线性回归只涉及一个预测变量\(X\)和一个响应变量\(Y\),假设它们之间的关系为:
\[ Y = \beta_0 + \beta_1 X + \epsilon \tag{3.2}\]
其中:
- \(\beta_0\)是截距(intercept):当\(X = 0\)时\(Y\)的平均值
- \(\beta_1\)是斜率(slope):\(X\)每增加1单位时\(Y\)的平均变化量
- \(\epsilon\)是误差项,表示\(Y\)中无法被\(X\)解释的部分
3.4.1 估计系数 (Estimating the Coefficients)
假设我们有\(n\)个观测\((x_1, y_1), (x_2, y_2), \ldots, (x_n, y_n)\)。我们的目标是估计\(\beta_0\)和\(\beta_1\),使得拟合的直线\(\hat{y} = \hat{\beta}_0 + \hat{\beta}_1 x\)尽可能接近实际观测点。
最小二乘法(Ordinary Least Squares, OLS)通过最小化残差平方和(Residual Sum of Squares, RSS)来寻找最优参数:
\[ \text{RSS}(\beta_0, \beta_1) = \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 = \sum_{i=1}^{n} (y_i - \beta_0 - \beta_1 x_i)^2 \tag{3.3}\]
3.4.1.1 数学推导:一阶条件 (First Order Conditions)
为了找到使 RSS 最小的 \(\beta_0\) 和 \(\beta_1\),我们分别对这两个参数求偏导,并令其等于 0:
对 \(\beta_0\) 求导: \[ \frac{\partial \text{RSS}}{\partial \beta_0} = -2 \sum_{i=1}^{n} (y_i - \beta_0 - \beta_1 x_i) = 0 \] 由此可得:\(\sum y_i - n\hat{\beta}_0 - \hat{\beta}_1 \sum x_i = 0\),即: \[ \hat{\beta}_0 = \bar{y} - \hat{\beta}_1 \bar{x} \tag{3.4}\]
对 \(\beta_1\) 求导: \[ \frac{\partial \text{RSS}}{\partial \beta_1} = -2 \sum_{i=1}^{n} (y_i - \beta_0 - \beta_1 x_i)x_i = 0 \] 代入 \(\hat{\beta}_0\) 的表达式: \[ \sum_{i=1}^{n} (y_i - (\bar{y} - \hat{\beta}_1 \bar{x}) - \hat{\beta}_1 x_i)x_i = 0 \] \[ \sum (y_i - \bar{y})x_i - \hat{\beta}_1 \sum (x_i - \bar{x})x_i = 0 \] 最终得到斜率的估计公式: \[ \hat{\beta}_1 = \frac{\sum_{i=1}^{n}(x_i - \bar{x})(y_i - \bar{y})}{\sum_{i=1}^{n}(x_i - \bar{x})^2} \tag{3.5}\]
其中,\(\bar{x} = \frac{1}{n}\sum x_i\) 和 \(\bar{y} = \frac{1}{n}\sum y_i\) 分别是样本均值。
案例应用:销售费用与营业收入的关联
让我们首先研究销售费用与公司营业收入的条件关联。分析总体是本地财务报表快照中具有 2023Q4 记录、可匹配中信一级行业的全部 A 股公司;观测单位为公司,order_book_id 必须唯一。进入回归的公司还须具有有限且为正的总资产,同时满足营业收入大于 1000 万元、销售费用大于 100 万元,以保证三个金额变量的对数变换有定义。回归使用全部合格公司;固定种子抽取的至多 250 家公司只用于散点图,绝不改变估计样本。这项横截面分析描述当前数据快照中的条件关联,不识别销售费用的因果效应。
下面从本地 2023Q4 A 股财务横截面提取全部符合事前条件公司的销售费用、营业收入和总资产,并一次合并公司资料表中的中信一级行业;行业只作为后续练习的预声明控制变量,不在这里用于挑选特定行业。对数变换用于缓解金额分布的右偏并给出双对数条件关联弹性:在模型设定成立时,斜率表示销售费用高 1% 的公司,其拟合营收平均相差约 \(\hat\beta_1\)%。这仍是观察性关联。代码随后拟合 OLS,并用散点和拟合线展示样本内关系。
接下来读取 2023Q4 全部公司记录,先核对文件、字段和公司键,再报告每一步筛选的样本数。info_date 只用于说明当前快照可能包含后续修订,本节不把它误作 2023 年末即可获得的预测特征。
# 按项目的数据根契约定位财务报表文件
BOOK_DATA_DIR = Path('/home/ubuntu/r2_data_mount/data').resolve() # 明文构造在线教材的绝对数据根
os.environ['BOOK_DATA_DIR'] = str(BOOK_DATA_DIR) # 为本章后续共享样本入口登记同一路径
assert BOOK_DATA_DIR.is_dir(), f'BOOK_DATA_DIR 不存在: {BOOK_DATA_DIR}' # 在读取前验证目录
local_data_path = BOOK_DATA_DIR / 'stock/financial_statement.h5' # 拼接财务报表路径
basic_data_path = BOOK_DATA_DIR / 'stock/stock_basic_data.h5' # 拼接静态公司行业表路径
assert local_data_path.is_file(), f'缺少财务报表文件: {local_data_path}' # 在HDF读取前验证财务文件
assert basic_data_path.is_file(), f'缺少公司行业文件: {basic_data_path}' # 在HDF读取前验证行业文件
df_2023_report = pd.read_hdf( # 在存储层选择2023Q4横截面,避免载入全部季度
local_data_path, # 指定财务报表h5文件路径
where='quarter="2023q4"', # 仅保留2023年第四季度年报数据
columns=['order_book_id', 'quarter', 'info_date', 'revenue', 'selling_expense', 'total_assets'] # 保留公司键、版本日期与三个金额字段
).copy() # 隔离本节横截面,避免后续筛选修改读取对象
company_industry = pd.read_hdf(basic_data_path, columns=['order_book_id', 'citics_2019_l1_name']).copy() # 一次读取稳定的中信一级行业字段
assert df_2023_report['order_book_id'].notna().all(), '2023Q4横截面含缺失公司键' # 公司键缺失会破坏观测单位定义
assert not df_2023_report['order_book_id'].duplicated().any(), '2023Q4横截面每家公司必须恰有一行' # 防止修订版本被重复计权
assert company_industry['order_book_id'].notna().all(), '公司行业表含缺失公司键' # 合并前核对行业表键完整
assert not company_industry['order_book_id'].duplicated().any(), '公司行业表每家公司必须恰有一行' # 阻止多对多行业合并
df_2023_report = df_2023_report.merge(company_industry, on='order_book_id', how='left', validate='one_to_one') # 一次合并财务与稳定行业字段
assert not df_2023_report['order_book_id'].duplicated().any(), '财务与行业合并后公司键必须唯一' # 验证合并没有扩张观测文件与公司键通过检查后,下一块执行事前金额与行业完整性筛选、对数变换和绘图抽样;完整合格样本仍保留给回归估计。
target_variables = ['revenue', 'selling_expense', 'total_assets'] # 声明简单模型与扩展模型共同所需金额字段
finite_amount_mask = np.isfinite(df_2023_report[target_variables]).all(axis=1) # 排除缺失和无穷金额
positive_asset_mask = finite_amount_mask & df_2023_report['total_assets'].gt(0) # 先保证资产对数有定义
eligible_amount_mask = positive_asset_mask & df_2023_report['revenue'].gt(1e7) & df_2023_report['selling_expense'].gt(1e6) # 再施加营业收入与销售费用门槛
eligible_sample_mask = eligible_amount_mask & df_2023_report['citics_2019_l1_name'].notna() # 要求扩展模型所需稳定行业可识别
df_analysis = df_2023_report.loc[eligible_sample_mask, ['order_book_id', *target_variables, 'citics_2019_l1_name']].copy() # 形成两模型共同估计样本
assert not df_analysis['order_book_id'].duplicated().any(), '筛选后每家公司必须恰有一行' # 保持横截面观测单位唯一
sample_audit = {'2023q4_rows': len(df_2023_report), 'unique_companies': df_2023_report['order_book_id'].nunique(), 'finite_amount_rows': int(finite_amount_mask.sum()), 'positive_asset_rows': int(positive_asset_mask.sum()), 'eligible_amount_rows': int(eligible_amount_mask.sum()), 'industry_matched_rows': len(df_analysis), 'eligible_unique_companies': df_analysis['order_book_id'].nunique()} # 汇总筛选与唯一键证据
print(sample_audit) # 核对目标总体、完整性门槛与最终估计分母
df_analysis['log_revenue'] = np.log10(df_analysis['revenue']) # 对营业收入取常用对数
df_analysis['log_sell_exp'] = np.log10(df_analysis['selling_expense']) # 对销售费用取常用对数
df_analysis['log_total_assets'] = np.log10(df_analysis['total_assets']) # 对正总资产取常用对数供扩展模型复用
plot_sample_size = min(250, len(df_analysis)) # 把视觉点数限制与估计样本量分开
df_plot_sample = df_analysis.sample(plot_sample_size, random_state=42).copy() # 固定抽取仅用于绘图的公司子样本
x_input_data = df_analysis['log_sell_exp'] # 用全部合格公司的对数销售费用拟合
y_output_data = df_analysis['log_revenue'] # 用全部合格公司的对数营业收入拟合
x_axis_label = 'Log10 销售费用' # 保留预测变量名称与对数底数
y_axis_label = 'Log10 营业收入' # 保留响应变量名称与对数底数{'2023q4_rows': 5340, 'unique_companies': 5340, 'finite_amount_rows': 5165, 'positive_asset_rows': 5165, 'eligible_amount_rows': 5127, 'industry_matched_rows': 4993, 'eligible_unique_companies': 4993}
以上代码完成了数据准备和对数变换。接下来我们使用OLS拟合简单线性回归模型,并绘制散点图和拟合直线,直观展示销售费用与营业收入之间的弹性关系。
# 为完整估计样本加入截距,使斜率对应双对数条件关联
marketing_features_matrix = sm.add_constant(x_input_data) # 保持设计矩阵与完整响应逐行对齐
ols_model_fit = sm.OLS(y_output_data, marketing_features_matrix).fit(cov_type='HC3') # 以全部合格公司拟合并采用HC3异方差稳健推断
estimated_beta0 = ols_model_fit.params['const'] # 读取基准对数营收水平
estimated_beta1 = ols_model_fit.params['log_sell_exp'] # 读取销售费用的条件关联弹性
r_squared_value = ols_model_fit.rsquared # 衡量完整样本相对均值基线的拟合改善
print('--- 简单线性回归分析报告 ---') # 标记随后数值属于完整横截面估计
print(f'完整估计样本: {len(df_analysis)} 家;绘图样本: {len(df_plot_sample)} 家') # 明确区分估计与展示样本
print(f'估计截距 (Beta0): {estimated_beta0:.4f}') # 报告模型基准水平
print(f'估计斜率 (Beta1): {estimated_beta1:.4f}') # 报告双对数条件关联弹性
print(f'拟合优度 (R-squared): {r_squared_value:.4f}') # 报告样本内拟合而非因果证据
print(f'HC3斜率标准误: {ols_model_fit.bse["log_sell_exp"]:.4f}') # 报告横截面异方差稳健标准误
print(f'HC3斜率 P 值: {ols_model_fit.pvalues["log_sell_exp"]:.4e}') # 报告稳健Wald检验结果
print(f'条件关联解释:销售费用高1%的公司,其拟合营业收入平均高约{estimated_beta1:.2f}%。') # 避免把横截面关联写成干预效应fig_simple_reg, ax_simple_reg = plt.subplots(figsize=(11, 7)) # 在同一坐标轴叠加固定散点样本与完整样本拟合线
# 用固定展示子样本呈现遮挡程度,拟合线仍来自完整样本
ax_simple_reg.scatter(df_plot_sample['log_sell_exp'], df_plot_sample['log_revenue'], alpha=0.5, s=50, # 只绘制固定展示子样本以减少遮挡
color='#34495e', edgecolors='white', label='固定绘图样本') # 用统一图例说明点并非完整估计样本
x_line_range = np.linspace(x_input_data.min(), x_input_data.max(), 100) # 只在完整样本支持域绘线
y_line_predicted = ols_model_fit.predict(sm.add_constant(x_line_range)) # 评估完整样本OLS条件均值
ax_simple_reg.plot(x_line_range, y_line_predicted, color='#e74c3c', # 在子图中绑制折线图
linewidth=3, label=f'OLS 拟合线 (R²={r_squared_value:.2f})') # 区分完整样本拟合与展示散点
ax_simple_reg.set_xlabel(x_axis_label, fontsize=12) # 明示销售费用使用常用对数尺度
ax_simple_reg.set_ylabel(y_axis_label, fontsize=12) # 明示营业收入使用常用对数尺度
ax_simple_reg.set_title('全部A股2023Q4横截面:销售费用与营业收入的条件关联', # 标题明确总体、时期与关联口径
fontsize=14, fontweight='bold', pad=20) # 准确标明总体、时期与解释边界
ax_simple_reg.legend(loc='upper left', frameon=True) # 说明散点与拟合线使用不同样本规模
ax_simple_reg.grid(True, linestyle='--', alpha=0.6) # 帮助读取对数坐标上的关联位置
plt.tight_layout() # 防止完整样本图例与坐标标签重叠
plt.show() # 输出完整样本估计与固定展示样本的对照
从 图 3.1 的结果可以看出:
斜率估计:在双对数条件均值模型成立时,\(\hat{\beta}_1\) 是样本中的条件关联弹性。例如估计值为 0.9 时,销售费用高 1% 的公司,其拟合营收平均高约 0.9%;这不是“增加费用会使营收增加”的干预结论。
统计显著性:若按所用标准误计算的 \(p\) 值很小,只能拒绝该模型中的零斜率假设。遗漏的公司规模、行业与反向决定关系都可能解释关联,因此不能据此断言更多推广导致更高收入。
3.4.2 评估系数估计的准确性 (Assessing the Accuracy of the Coefficient Estimates)
回忆简单线性回归模型:
\[ Y = \beta_0 + \beta_1 X + \epsilon \]
我们用有限样本估计 \(\beta_0\) 和 \(\beta_1\),所以某一次得到的 \(\hat\beta_0\)、\(\hat\beta_1\) 通常与真值不完全相同;这种单次差异叫抽样误差,不等于估计量存在系统性偏差。是否无偏要看在重复抽样下 \(E(\hat\beta)=\beta\) 是否成立,因此需要研究估计量的抽样分布(sampling distribution)。
以下结论以给定设计矩阵为条件。线性参数化、无完全共线性与条件零均值 \(E(\epsilon\mid X)=0\) 给出 OLS 的条件无偏性;再加 \(\operatorname{Var}(\epsilon\mid X)=\sigma^2I\) 才得到下列经典方差公式和 Gauss–Markov 效率结论。若还假定 \(\epsilon\mid X\) 联合正态,有限样本的 \(t/F\) 分布与区间才是精确的。仅有无条件的 \(E(\epsilon)=0\) 不足以保证解释变量与误差不相关,也不足以推出 OLS 无偏。
无偏性 (Unbiasedness):\(E(\hat{\beta}_0) = \beta_0\),\(E(\hat{\beta}_1) = \beta_1\)
方差 (Variance): \[ \text{Var}(\hat{\beta}_0) = \sigma^2 \left[\frac{1}{n} + \frac{\bar{x}^2}{\sum_{i=1}^{n}(x_i - \bar{x})^2}\right] \tag{3.6}\] \[ \text{Var}(\hat{\beta}_1) = \frac{\sigma^2}{\sum_{i=1}^{n}(x_i - \bar{x})^2} \tag{3.7}\]
标准误差 (Standard Error): \[ \text{SE}(\hat{\beta}_0) = \sqrt{\frac{\sigma^2}{n} + \frac{\sigma^2 \bar{x}^2}{\sum_{i=1}^{n}(x_i - \bar{x})^2}} \tag{3.8}\] \[ \text{SE}(\hat{\beta}_1) = \sqrt{\frac{\sigma^2}{\sum_{i=1}^{n}(x_i - \bar{x})^2}} \tag{3.9}\]
其中,\(\sigma^2\)通常使用残差标准误(Residual Standard Error, RSE)来估计:
\[ \hat{\sigma}^2 = \text{RSE}^2 = \frac{1}{n-2}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2 = \frac{\text{RSS}}{n-2} \tag{3.10}\]
Tip: 为什么除以\(n-2\)而不是\(n\)?
在估计\(\sigma^2\)时,我们使用\(n-2\)而不是\(n\)作为分母,这是为了得到\(\sigma^2\)的无偏估计。原因如下:
- 我们在估计\(\hat{\beta}_0\)和\(\hat{\beta}_1\)时,使用了数据的两个自由度(degrees of freedom)。
- 这使得残差\((y_i - \hat{y}_i)\)的方差略微小于真实误差\(\epsilon_i\)的方差。
- 除以\(n-2\)可以补偿这种低估,得到无偏估计。
更一般地,本章统一用 \(p\) 表示不含截距的预测变量个数,因此含截距模型共有 \(p+1\) 个回归系数,残差方差分母为 \(n-p-1\)。若其他资料用 \(q\) 表示包括截距在内的参数总数,同一分母会写成 \(n-q\);两种写法不能混用。这是回归自由度校正,不宜笼统等同于只针对样本方差的 \(n-1\) 贝塞尔校正。
假设检验 (Hypothesis Testing)
最常见的假设检验是检验斜率是否显著不为零:
- 零假设 \(H_0: \beta_1 = 0\)(\(X\)和\(Y\)之间没有线性关系)
- 备择假设 \(H_a: \beta_1 \neq 0\)(\(X\)和\(Y\)之间存在线性关系)
t统计量 (t-statistic):
\[ t = \frac{\hat{\beta}_1 - 0}{\text{SE}(\hat{\beta}_1)} \tag{3.11}\]
在误差项条件正态且\(H_0\)为真时,\(t\)精确服从自由度为\(n-2\)的\(t\)分布。非正态情形下,下述结论通常是大样本近似,或需稳健/自助推断。
\[ p\text{-value} = P(|T_{n-2}| \geq |t|) \tag{3.12}\]
如果p值小于显著性水平(通常为0.05),我们拒绝\(H_0\),认为\(\beta_1\)显著不为零。
置信区间 (Confidence Interval)
在条件正态的简单回归中,\(\beta_1\) 的精确 \(95\%\) 置信区间为:
\[ \hat{\beta}_1 \pm t_{0.975,n-2}\cdot \text{SE}(\hat{\beta}_1) \tag{3.13}\]
更一般地,置信水平为\(1-\alpha\)的置信区间为:
\[ \hat{\beta}_1 \pm t_{1-\alpha/2, n-2} \cdot \text{SE}(\hat{\beta}_1) \tag{3.14}\]
其中,\(t_{1-\alpha/2,n-2}\) 是自由度为 \(n-2\) 的 \(t\) 分布的 \(1-\alpha/2\) 分位数。常见的“\(\hat\beta_1\pm2\operatorname{SE}\)”只是当自由度较大时用 2 近似临界值的简写,不是小样本精确区间;statsmodels 摘要表使用相应的 \(t\) 临界值。
下面的机制实验用预先设定的线性关系生成 200 个观测,再用 statsmodels 输出系数、标准误、\(t\) 统计量、\(p\) 值和置信区间。由于斜率与误差分布由代码指定,这一结果只用于核对推断量的计算与读取方式,不能解释为真实广告投入的效果。
import statsmodels.api as sm # 拟合已知线性DGP并读取有限样本推断量
import numpy as np # 生成固定线性信号与高斯误差
np.random.seed(42) # 锁定推断量核对所用的模拟样本
market_count = 200 # 模拟200个区域市场的广告投放数据
tv_ad_budget = np.random.uniform(0, 300, market_count) # 均匀分布模拟电视广告预算(0~300万元)
# DGP: 销量 = 5 + 0.04*广告 + ε,其中ε~N(0,4)
sales_revenue = 5 + 0.04 * tv_ad_budget + np.random.normal(0, 2, market_count) # 真实线性关系+高斯噪声
ad_features_matrix = sm.add_constant(tv_ad_budget) # 使设计矩阵对应含截距的理论模型
sales_linear_model = sm.OLS(sales_revenue, ad_features_matrix).fit() # 最小二乘法拟合简单线性回归模型
print(sales_linear_model.summary()) # 核对点估计、标准误、检验与区间来自同一拟合 OLS Regression Results
==============================================================================
Dep. Variable: y R-squared: 0.765
Model: OLS Adj. R-squared: 0.763
Method: Least Squares F-statistic: 643.4
Date: Tue, 01 Sep 2026 Prob (F-statistic): 4.04e-64
Time: 08:36:57 Log-Likelihood: -415.57
No. Observations: 200 AIC: 835.1
Df Residuals: 198 BIC: 841.7
Df Model: 1
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 5.2104 0.264 19.702 0.000 4.689 5.732
x1 0.0395 0.002 25.365 0.000 0.036 0.043
==============================================================================
Omnibus: 7.028 Durbin-Watson: 2.131
Prob(Omnibus): 0.030 Jarque-Bera (JB): 9.199
Skew: 0.231 Prob(JB): 0.0101
Kurtosis: 3.943 Cond. No. 327.
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
表 3.1 提供了丰富的统计信息:
- Coef.:系数估计值
- Std.Err.:标准误差
- t:t统计量
- P>|t|:p值(两个尾部的概率)
- [0.025 0.975]:95%置信区间
3.4.3 评估模型的准确性 (Assessing the Accuracy of the Model)
在估计回归系数后,我们需要评估整体模型的拟合优度。两个最重要的指标是残差标准误(RSE)和\(R^2\)统计量。
3.4.3.1 残差标准误 (Residual Standard Error)
RSE是误差项标准差\(\sigma\)的估计:
\[ \text{RSE} = \sqrt{\frac{1}{n-2}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2} \tag{3.15}\]
RSE 是对误差项条件标准差 \(\sigma\) 的估计,其单位与 \(Y\) 相同。它不是平均绝对偏差(MAE),也不是平均残差(含截距 OLS 的平均残差为零)。
在我们的案例中,如果 RSE = 2.0,可说拟合残差的典型标准差尺度约为2千件;不能把它精确翻译成“平均偏差”。
3.4.3.2 \(R^2\)统计量 (\(R^2\) Statistic)
\(R^2\)统计量衡量的是响应变量的变异中可以被预测变量解释的比例:
\[ R^2 = \frac{\text{TSS} - \text{RSS}}{\text{TSS}} = 1 - \frac{\text{RSS}}{\text{TSS}} \tag{3.16}\]
其中:
- \(\text{TSS} = \sum_{i=1}^{n}(y_i - \bar{y})^2\)是总平方和(Total Sum of Squares),衡量\(Y\)的总变异
- \(\text{RSS} = \sum_{i=1}^{n}(y_i - \hat{y}_i)^2\)是残差平方和(Residual Sum of Squares),衡量未被模型解释的变异
当模型含截距、用同一样本做 OLS 拟合且 TSS 以样本均值为基准时,常数模型属于候选空间,因而样本内 \(R^2\in[0,1]\)。无截距回归、样本外评价或某些广义模型的伪 \(R^2\) 不具备这一保证,数值可以为负或采用不同定义。在上述含截距的样本内情形:
- \(R^2 = 1\):模型完美拟合数据
- \(R^2 = 0\):模型无法解释任何变异(等同于只用均值预测)
在简单线性回归中,\(R^2\)等于\(X\)和\(Y\)的相关系数的平方:
\[ R^2 = \text{Cor}(X, Y)^2 \tag{3.17}\]
Clarification on \(R^2\)的局限性
虽然\(R^2\)是广泛使用的模型拟合指标,但它有一些重要的局限性:
\(R^2\)不能判断模型是否正确:高\(R^2\)并不意味着模型设定正确,可能是遗漏变量问题或伪相关。
\(R^2\)会随着预测变量数量增加而增加:在多元回归中,添加任何预测变量(即使是无用的变量)都会使\(R^2\)增加或保持不变。这就是为什么我们通常使用调整\(R^2\)(Adjusted \(R^2\))。
\(R^2\)在非线性模型中的解释不同:在非线性模型中,\(R^2\)不一定代表”解释变异的比例”。
高\(R^2\)不等于因果性:\(R^2\)只是衡量相关性,不能推出因果关系。
因此,\(R^2\)应该与其他指标(如残差分析、交叉验证性能)结合使用,而不是单独依赖它来评估模型质量。
3.5 多元线性回归 (Multiple Linear Regression)
当有多个预测变量时,我们使用多元线性回归:
\[ Y = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \cdots + \beta_p X_p + \epsilon \tag{3.18}\]
其中:
- \(X_1, X_2, \ldots, X_p\)是\(p\)个不同的预测变量
- \(\beta_1, \beta_2, \ldots, \beta_p\)是相应的回归系数,表示在其他变量不变的情况下,\(X_j\)每增加1单位时\(Y\)的平均变化量
案例应用:三种费用对营业收入的综合影响
在金融科技案例中,我们通常需要同时考虑多种费用的协同作用。这里我们分析:研发费用、销售费用及管理费用对营业收入的综合贡献。
下面是一个明确标注的受控模拟,用于演示多元回归几何。标准化使系数表示“\(X_j\) 增加一个样本标准差时 \(Y\) 的条件关联变化”,但系数绝对值仍会受共线性、测量误差和变量编码影响,不能自动解释为预测重要性或因果贡献。
import numpy as np # 生成已知三变量线性DGP
import pandas as pd # 用表结构保存模拟公司的三类费用与响应变量
import matplotlib.pyplot as plt # 展示联合拟合平面的条件切片
from sklearn.linear_model import LinearRegression # 估计标准化特征上的多元OLS
from sklearn.preprocessing import StandardScaler # 统一三类费用的系数尺度
np.random.seed(42) # 锁定多元回归几何演示的一次样本
n_company_samples = 250 # 模拟250家企业样本
# 模拟三种费用数据(单位:万元)
rd_exp_input = np.random.uniform(100, 1000, n_company_samples) # 研发费用范围100~1000万
sales_exp_input = np.random.uniform(80, 800, n_company_samples) # 销售费用范围80~800万
admin_exp_input = np.random.uniform(50, 500, n_company_samples) # 管理费用范围50~500万
# 构造真实数据生成过程(DGP): Revenue = 200 + 1.5*RD + 2.2*Sales + 0.8*Admin + ε
true_revenue_values = (200 + 1.5 * rd_exp_input + # 研发费用贡献
2.2 * sales_exp_input + # 销售费用贡献(弹性最大)
0.8 * admin_exp_input) # 管理费用贡献(弹性最小)
observed_revenue = true_revenue_values + np.random.normal(0, 150, n_company_samples) # 加入随机噪声模拟数据就绪后,我们使用 scikit-learn 对三种费用进行标准化处理和多元线性回归拟合,最终将结果可视化为3D回归平面。
# 保留模拟特征与响应的逐行对应关系
df_multi_finance = pd.DataFrame({ # 形成可按列审计的多元回归样本
'RD_Exp': rd_exp_input, # 研发费用列
'Sales_Exp': sales_exp_input, # 销售费用列
'Admin_Exp': admin_exp_input, # 管理费用列
'Revenue': observed_revenue # 营业收入列(响应变量)
}) # 固定后续标准化与建模使用的四列
feature_columns = ['RD_Exp', 'Sales_Exp', 'Admin_Exp'] # 固定多元回归中三类费用特征的列顺序
X_features = df_multi_finance[feature_columns] # 保持三类费用的固定解释顺序
y_target_obs = df_multi_finance['Revenue'] # 对齐同一公司的模拟营收响应
scaler_tool = StandardScaler() # 实例化Z-score标准化器
X_features_scaled = scaler_tool.fit_transform(X_features) # 对特征矩阵做标准化(均值=0,标准差=1)
multi_ols_reg = LinearRegression() # 实例化线性回归模型
multi_ols_reg.fit(X_features_scaled, y_target_obs) # 在标准化特征上拟合OLS模型
print('--- 多元线性回归分析结果 ---') # 标记随后数值属于受控模拟
print(f'回归截距 (Beta0): {multi_ols_reg.intercept_:.4f}') # 报告标准化特征均值处的拟合水平
for name, coef in zip(feature_columns, multi_ols_reg.coef_): # 按固定列顺序报告条件关联尺度
print(f'{name} 的标准化回归系数: {coef:.4f}') # 报告条件关联尺度,不把它当作因果重要性
print(f'模型 R-squared: {multi_ols_reg.score(X_features_scaled, y_target_obs):.4f}') # 报告模拟样本内拟合度--- 多元线性回归分析结果 ---
回归截距 (Beta0): 2212.0553
RD_Exp 的标准化回归系数: 399.3895
Sales_Exp 的标准化回归系数: 476.7641
Admin_Exp 的标准化回归系数: 98.4139
模型 R-squared: 0.9460
上述结果来自事先设定系数的受控模拟,因此只用于验证标准化多元回归能否回收数据生成过程。系数大小和 \(R^2\) 由模拟参数决定,不能解释为长三角真实企业中销售、研发或管理费用的贡献排序,更不能据此提出预算干预建议。
下面将受控模拟中的拟合关系用 3D 图形展示。管理费用固定在其标准化均值 0,研发费用与销售费用构成平面坐标;散点是模拟观测,颜色只映射模拟营收。
# ---- 3D可视化(取研发与销售两个维度) ----
fig_3d_reg = plt.figure(figsize=(14, 10)) # 为两个费用轴与营收高度保留可读空间
ax_3d_reg = fig_3d_reg.add_subplot(111, projection='3d') # 在三维坐标中展示条件平面
# 绘制三维散点(颜色按营收深浅编码,viridis色阶映射收入高低)
scatter_plot_3d = ax_3d_reg.scatter(X_features_scaled[:, 0], X_features_scaled[:, 1], y_target_obs,
c=y_target_obs, cmap='viridis', s=60, alpha=0.7) # viridis渐变色映射营收
x_grid_range = np.linspace(X_features_scaled[:, 0].min(), X_features_scaled[:, 0].max(), 20) # 研发维度网格
y_grid_range = np.linspace(X_features_scaled[:, 1].min(), X_features_scaled[:, 1].max(), 20) # 销售维度网格
X_surf, Y_surf = np.meshgrid(x_grid_range, y_grid_range) # 构造二维网格矩阵
# 计算回归超平面上的预测值(管理费用取标准化均值0)
Z_surf_pred = (multi_ols_reg.intercept_ + # 截距项
multi_ols_reg.coef_[0] * X_surf + # 研发费用的边际贡献
multi_ols_reg.coef_[1] * Y_surf + # 销售费用的边际贡献
multi_ols_reg.coef_[2] * 0) # 管理费用固定为标准化均值水平
ax_3d_reg.plot_surface(X_surf, Y_surf, Z_surf_pred, alpha=0.2, color='red') # 与模拟散点对照联合线性拟合
ax_3d_reg.set_xlabel('研发费用 (标准化)', fontsize=11) # 标出第一项标准化费用轴
ax_3d_reg.set_ylabel('销售费用 (标准化)', fontsize=11) # 标出第二项标准化费用轴
ax_3d_reg.set_zlabel('营业收入 (万元)', fontsize=11) # 保留响应变量的原始金额单位
ax_3d_reg.set_title('受控模拟:多元回归的联合拟合平面', fontsize=14, pad=20) # 明示机制模拟身份
plt.show() # 输出管理费用固定于均值时的条件切片
图 3.2 只核对已知线性数据生成过程能否被 OLS 近似回收。散点围绕平面分布、销售费用方向斜率较大,都是预设系数与随机噪声共同造成的模拟结果;它们不说明真实长三角企业满足线性假设,也不验证费用投入与营收的现实或因果关联。
3.5.1 估计回归系数:矩阵视角 (Estimating via Matrix Algebra)
在多元回归中,模型可以简洁地表示为:
\[ \mathbf{Y} = \mathbf{X}\boldsymbol{\beta} + \boldsymbol{\epsilon} \tag{3.19}\]
为了理解这一数学模型,我们将各个组件拆解如下:
- \(\mathbf{Y}\) 为 \(n \times 1\) 的向量,包含所有观测响应。
- \(\mathbf{X}\) 为 \(n \times (p+1)\) 的设计矩阵(Design Matrix)。其第一列全是 1(用于截距项),随后的每列代表一个预测变量。
- \(\boldsymbol{\beta}\) 为需估计的 \((p+1) \times 1\) 系数向量。
- \(\boldsymbol{\epsilon}\) 为 \(n \times 1\) 的随机误差向量。
3.5.1.1 核心推导:正规方程 (Normal Equations)
我们寻找向量 \(\hat{\boldsymbol{\beta}}\),使得残差平方和(RSS)最小:
\[ \text{RSS}(\boldsymbol{\beta}) = (\mathbf{Y} - \mathbf{X}\boldsymbol{\beta})^T (\mathbf{Y} - \mathbf{X}\boldsymbol{\beta}) \]
展开该标量形式: \[ \text{RSS}(\boldsymbol{\beta}) = \mathbf{Y}^T \mathbf{Y} - 2\boldsymbol{\beta}^T \mathbf{X}^T \mathbf{Y} + \boldsymbol{\beta}^T \mathbf{X}^T \mathbf{X} \boldsymbol{\beta} \]
利用矩阵微积分对 \(\boldsymbol{\beta}\) 求梯度: \[ \nabla_{\boldsymbol{\beta}} \text{RSS} = -2 \mathbf{X}^T \mathbf{Y} + 2 \mathbf{X}^T \mathbf{X} \boldsymbol{\beta} \]
令梯度为零,得到著名的正规方程: \[ \mathbf{X}^T \mathbf{X} \hat{\boldsymbol{\beta}} = \mathbf{X}^T \mathbf{Y} \tag{3.20}\]
由此解得 OLS 的矩阵解析解: \[ \hat{\boldsymbol{\beta}} = (\mathbf{X}^T \mathbf{X})^{-1} \mathbf{X}^T \mathbf{Y} \tag{3.21}\]
Warning: 矩阵可逆性与多重共线性
注意,公式 式 3.21 的前提是 \(\mathbf{X}^T \mathbf{X}\) 必须可逆(即非奇异)。如果预测变量之间存在完全共线性(Perfect Collinearity),即某一列是其他列的线性组合,则 \(\mathbf{X}^T \mathbf{X}\) 不存在逆矩阵,模型无法唯一求解。在金融数据中,过多的冗余财务指标常会导致“近似多重共线性”,造成系数估计不稳。
3.5.2 一些重要问题 (Some Important Questions)
在拟合多元线性回归模型后,我们通常关注以下问题:
3.5.2.1 1. 至少有一个预测变量与响应变量相关吗? (Is There a Relationship Between the Response and Predictors?)
这个问题的答案可以通过F检验 (F-test) 来回答。
假设:
- \(H_0: \beta_1 = \beta_2 = \cdots = \beta_p = 0\)(所有预测变量都与\(Y\)无关)
- \(H_a:\) 至少有一个\(\beta_j \neq 0\)
F统计量:
\[ F = \frac{(\text{TSS} - \text{RSS}) / p}{\text{RSS} / (n - p - 1)} \tag{3.22}\]
在\(H_0\)为真时,\(F\)服从\(F_{p, n-p-1}\)分布。如果F统计量的值很大(对应的p值很小),我们拒绝\(H_0\)。
下面用受控模拟演示联合 \(F\) 检验。由于非零广告系数已写入数据生成过程,小 \(p\) 值只说明检验在该设定下识别到至少一个非零条件关联;它不是现实广告投放效果的证据。
import statsmodels.api as sm # 估计含三项斜率的整体F检验
import numpy as np # 生成固定广告投入与高斯误差
import pandas as pd # 保留渠道名称以读取联合检验设计矩阵
np.random.seed(42) # 锁定整体F检验的受控样本
sample_size = 200 # 模拟200个分公司
tv_ad_budget = np.random.uniform(0, 300, sample_size) # 电视广告预算(万元)
online_ad_budget = np.random.uniform(0, 100, sample_size) # 网络广告预算(万元)
newspaper_ad_budget = np.random.uniform(0, 100, sample_size) # 报纸广告预算(万元)
# DGP: 销量 = 5 + 0.04*电视 + 0.03*网络 + 0.01*报纸 + ε
# 根据线性DGP加高斯噪声生成模拟销售量
sales_revenue = 5 + 0.04*tv_ad_budget + 0.03*online_ad_budget + 0.01*newspaper_ad_budget + np.random.normal(0, 2, sample_size)数据模拟完成后,我们将三种广告渠道作为特征矩阵输入OLS模型,提取F统计量进行整体联合显著性检验。
# 构造含截距的特征矩阵(三种广告投入)
media_features_matrix = sm.add_constant(pd.DataFrame({ # 对齐三种渠道并加入整体检验所需截距
'电视': tv_ad_budget, # 电视广告预算列
'网络': online_ad_budget, # 网络广告预算列
'报纸': newspaper_ad_budget # 报纸广告预算列
})) # 固定零假设覆盖的三项斜率
media_sales_model = sm.OLS(sales_revenue, media_features_matrix).fit() # OLS拟合多元回归模型
f_stat = media_sales_model.fvalue # 读取“三项斜率同时为零”的检验统计量
f_pvalue = media_sales_model.f_pvalue # 读取同一联合零假设的尾部概率
print('F检验结果:') # 标记随后证据属于受控模拟的联合检验
print('=' * 50) # 分隔线
print(f'F统计量 = {f_stat:.4f}') # 报告联合信号相对残差噪声的尺度
print(f'p值 = {f_pvalue:.4e}') # 报告联合零假设的样本证据
print(f'\n结论:p值 < 0.05,拒绝零假设。') # 给出统计学结论
print('在受控模拟中,至少一个预设斜率被检验识别为非零。') # 限定结论范围
print(f'\nR² = {media_sales_model.rsquared:.4f}') # 报告不惩罚复杂度的样本内拟合
print(f'调整R² = {media_sales_model.rsquared_adj:.4f}') # 对三项预测变量施加自由度修正F检验结果:
==================================================
F统计量 = 203.1853
p值 = 6.8172e-60
结论:p值 < 0.05,拒绝零假设。
在受控模拟中,至少一个预设斜率被检验识别为非零。
R² = 0.7567
调整R² = 0.7530
运行结果应以代码现场输出为准。若联合 \(p\) 值很小,可拒绝“受控模拟中的所有斜率同时为零”,但不能把这一机械检验解释为现实广告渠道的影响、重要性或因果贡献。\(R^2\) 同样只是该模拟样本的拟合摘要。
3.5.2.2 2. 所有预测变量都重要吗?哪些变量重要? (Do All Predictors Help? Which Variables Matter?)
要判断单个预测变量是否重要,我们可以对每个系数进行t检验:
\[ H_0: \beta_j = 0 \quad \text{vs} \quad H_a: \beta_j \neq 0 \]
t统计量为:
\[ t = \frac{\hat{\beta}_j}{\text{SE}(\hat{\beta}_j)} \tag{3.23}\]
Clarification on 多重共线性
当预测变量之间高度相关时,会出现多重共线性(multicollinearity)问题。这会导致:
- 系数估计不稳定:小样本变化可能导致系数估计大幅变化。
- 标准误增大:系数的标准误会变大,导致t统计量变小,即使变量实际重要,也可能无法拒绝零假设。
- 解释困难:系数的符号可能与直觉相反,或难以解释。
检测多重共线性的方法:
- 计算预测变量之间的相关系数矩阵
- 计算方差膨胀因子 (VIF):\(\text{VIF} = \frac{1}{1 - R_j^2}\),其中\(R_j^2\)是用其他预测变量预测\(X_j\)的\(R^2\)
- 5 或 10 有时被用作经验警戒线,但不是通用检验阈值;VIF 应结合研究目的、变量定义和估计不确定性解释
处理多重共线性的方法:
- 若目标是解释,先明确要保持的估计对象,再考虑重参数化并报告扩大的不确定性
- 若目标是预测,可在训练内部比较正则化、降维或精简特征的样本外表现
- 不应只因越过某个经验阈值就机械删除有理论含义的变量
3.5.2.3 3. 模型拟合数据的效果如何? (How Well Does the Model Fit the Data?)
在多元回归中,我们使用\(R^2\)和调整\(R^2\)来评估模型拟合:
\[ R^2 = 1 - \frac{\text{RSS}}{\text{TSS}} \tag{3.24}\] \[ \text{Adjusted } R^2 = 1 - \frac{\text{RSS} / (n - p - 1)}{\text{TSS} / (n - 1)} \tag{3.25}\]
调整\(R^2\)惩罚了添加无用变量的行为,因此在比较不同模型时更有用。
3.5.2.4 4. 给定一组预测变量的值,如何预测响应变量? (Given a Set of Predictor Values, What Response Value Should We Predict?)
对于新的观测\(\mathbf{x}_0 = (1, x_{01}, x_{02}, \ldots, x_{0p})^T\),预测值为:
\[ \hat{y}_0 = \mathbf{x}_0^T \hat{\boldsymbol{\beta}} \tag{3.26}\]
在经典同方差正态线性模型下,若 \(s^2=\mathrm{RSS}/(n-p-1)\),令 \(h_0=\mathbf x_0^T(X^TX)^{-1}\mathbf x_0\),则有限样本区间使用 \(t_{n-p-1,0.975}\) 分位数,而不是无条件使用“2”。
95%置信区间(针对条件平均响应): \[ \hat{y}_0 \pm t_{n-p-1,0.975}\,s\sqrt{h_0} \]
95%预测区间(针对同一误差分布下的新个体): \[ \hat{y}_0 \pm t_{n-p-1,0.975}\,s\sqrt{1+h_0}. \tag{3.27}\]
大样本下 \(t\) 分位数接近 1.96,才可粗略写成 \(\pm2\,\mathrm{SE}\)。存在异方差、相关误差、模型错设或外推时,需要相应的稳健推断、时间序列方法或预测评估,不能直接套用上述有限样本公式。
3.6 回归模型的其他考虑 (Other Considerations in the Regression Model)
3.6.1 定性预测变量 (Qualitative Predictors)
在实际的商业与经济研究中,预测变量并不总是定量的。在中国资本市场的研究中,最典型的定性变量包括:
- 企业性质(Ownership):国企 vs 民企。
- 地区分布(Region):长三角、大湾区 vs 其他地区。
- 行业类别(Industry):科技、金融、制造等。
3.6.1.1 虚拟变量 (Dummy Variables)
对于只有两个水平的定性变量,我们引入一个虚拟变量(dummy variable) \(D\):
\[ D_i = \begin{cases} 1 & \text{若第 } i \text{ 个公司是国有企业} \\ 0 & \text{若第 } i \text{ 个公司是非国有企业} \end{cases} \]
将其放入模型: \[ y_i = \beta_0 + \beta_1 D_i + \epsilon_i \]
此时系数的经济含义非常直观:
- \(\beta_0\) 是非国有企业的平均响应值。
- \(\beta_0 + \beta_1\) 是国有企业的平均响应值。
- \(\beta_1\) 代表了两者之间的平均差异指标。
受控模拟:虚拟变量系数的解释
下面用带有明确数据生成过程的模拟样本说明虚拟变量系数;本节不估计真实国企与民企的盈利差异。
代码把产权标签编码为 0/1,并在模拟中控制对数资产规模。拟合系数说明如何读取“给定模型和其他变量时的组间条件均值差”,而不是提供关于现实产权效率的证据。
import pandas as pd # 对齐规模、产权标签与ROE响应
import numpy as np # 生成受控产权差异与随机误差
import statsmodels.api as sm # 估计含虚拟变量的条件均值模型
np.random.seed(42) # 固定随机种子
n_firms = 300 # 模拟样本量为300家企业
# 模拟企业资产规模(对数值),范围约为20~26,对应现实中的中型上市公司
log_asset_size = np.random.uniform(20, 26, n_firms) # 覆盖预设的对数规模支持域
# 模拟产权性质:0代表民营企业、1代表国有企业,比例7:3
is_state_owned = np.random.choice([0, 1], size=n_firms, p=[0.7, 0.3]) # 随机抽样
# 真实DGP: ROE = 0.02 + 0.005*(Size-23) - 0.015*SOE + ε
# 负号意味着在该模拟设定下,国企的平均ROE低于民企
# 根据DGP线性结构加高斯噪声生成模拟ROE值
firm_roe_values = 0.02 + 0.005 * (log_asset_size - 23) - 0.015 * is_state_owned + np.random.normal(0, 0.02, n_firms)模拟数据生成完毕后,我们利用 statsmodels 的OLS函数来拟合虚拟变量回归模型,并解读国有产权标签对ROE的系数含义。
# 对齐每家模拟公司的规模、产权编码与ROE
df_firm_data = pd.DataFrame({ # 形成虚拟变量回归的逐行样本
'LogSize': log_asset_size, # 对数资产规模
'IsSOE': is_state_owned, # 国有企业虚拟变量
'ROE': firm_roe_values # 净资产收益率
}) # 固定后续条件均值模型使用的三列
# 构造含截距的自变量矩阵
X_dummy_matrix = sm.add_constant(df_firm_data[['LogSize', 'IsSOE']]) # 使产权系数相对民企基准解释
# OLS拟合
ownership_ols_model = sm.OLS(df_firm_data['ROE'], X_dummy_matrix).fit() # 最小二乘法估计回归参数
print(ownership_ols_model.summary().tables[1]) # 并列核对规模与产权标签的条件系数
print('\n--- 结果解读 ---') # 分隔模型摘要与条件关联解释
soe_coefficient = ownership_ols_model.params['IsSOE'] # 读取给定规模后的产权条件均值差
print(f'国有企业(IsSOE)的系数为: {soe_coefficient:.4f}') # 报告模拟编码1相对0的差异
# 解读:控制资产规模后,国企ROE平均低于民企的幅度
# 把比例尺度换算为业务更易读的百分点
print('模拟样本中,在给定规模后,产权标签对应的条件均值差为 {:.2f} 个百分点。'.format(soe_coefficient*100))==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const -0.0796 0.015 -5.178 0.000 -0.110 -0.049
LogSize 0.0043 0.001 6.498 0.000 0.003 0.006
IsSOE -0.0179 0.003 -7.126 0.000 -0.023 -0.013
==============================================================================
--- 结果解读 ---
国有企业(IsSOE)的系数为: -0.0179
模拟样本中,在给定规模后,产权标签对应的条件均值差为 -1.79 个百分点。
若拟合结果接近代码设定的系数,只能说明估计程序在这一受控模拟中工作正常。显著性、符号和数值都由数据生成过程预设,不能被称为实证发现,也不能支持规模经济、产权效率或治理机制的现实解释。真实研究还需要可追溯数据、明确的抽样框架与因果识别设计。
3.6.2 线性模型的扩展 (Extensions of the Linear Model)
标准的线性模型基于两个极强的假设:可加性(Additivity)和线性性(Linearity)。
3.6.2.1 交互效应与协同作用 (Interaction Effects)
可加性假设认为一个预测变量对响应的影响与各其他变量的水平无关。但在商业决策中,这种假设往往过于简单。
例如,一个公司的研发投入 (\(X_{RD}\)) 和行业集中度 (\(X_{HHI}\)) 可能会产生相互影响:低竞争行业的研发投入产出比可能远高于高竞争行业。这种协同作用可以通过交互项(Interaction Term)来建模:
\[ Y = \beta_0 + \beta_1 X_{RD} + \beta_2 X_{HHI} + \beta_3 (X_{RD} \times X_{HHI}) + \epsilon \tag{3.28}\]
3.6.2.2 非线性关系:多项式回归 (Non-Linearity)
线性假设认为 \(X\) 每变化一单位,\(Y\) 的变化是恒定的。然而,许多金融关系呈现 U 型、倒 U 型或随 \(X\) 改变的边际斜率。
案例:公司规模与创新效率
著名的“熊彼特假设”认为大企业更具创新优势,但当企业规模过大时,组织官僚化可能导致创新效率下降。这可以用二次项来捕捉:
\[ \text{Innovation} = \beta_0 + \beta_1 \text{Size} + \beta_2 \text{Size}^2 + \epsilon \tag{3.29}\]
若 \(\beta_2<0\),拟合二次函数是凹的;其候选转折点为 \(x^*=-\beta_1/(2\beta_2)\)。只有当 \(x^*\) 位于事前定义的可行域与观测支持域内,且转折点及两侧斜率的不确定性允许这种解释时,才可把样本曲线称为支持域内的倒 U 型;否则只能报告凹性,不能宣称存在可行“最优规模”。观察回归也不把这个转折点识别为因果最优政策。
下面用受控模拟检验多项式管道是否能回收一个已知的凹二次数据生成过程。真实条件均值设为 \(5+0.09x-0.0002x^2\),所以理论转折点为 225 万元;固定种子实现的广告预算支持域约为 \([1.657,296.066]\) 万元,转折点两侧分别有 149 与 51 个观测。因而本例可以讨论支持域内先增后减的形状,但它只验证方法,不是现实广告因果证据。
import numpy as np # 生成已知凹二次DGP并诊断幂基条件数
import matplotlib.pyplot as plt # 对照训练、验证、真值与候选曲线
import pandas as pd # 整理训练与验证证据表
from sklearn.preprocessing import PolynomialFeatures # 构造预先登记阶数的幂基
from sklearn.preprocessing import StandardScaler # 在训练期缩放原始预算以改善幂基数值条件
from sklearn.linear_model import LinearRegression # 在每组幂基上估计最小二乘系数
from sklearn.pipeline import Pipeline # 保证缩放与幂基仅在训练样本拟合
from sklearn.metrics import mean_squared_error # 分别计算训练与验证均方误差
from sklearn.model_selection import train_test_split # 用固定切分构造未参与拟合的验证证据
np.random.seed(42) # 锁定三种复杂度共享的模拟观测
observation_count = 200 # 设定模拟样本量为200个观测
tv_ad_budget = np.random.uniform(0, 300, observation_count) # 模拟电视广告预算,范围0~300万
turning_point_budget = 225.0 # 由已知二次DGP的一阶条件得到支持域内转折点
true_sales_revenue = 5 + 0.09 * tv_ad_budget - 0.0002 * tv_ad_budget**2 # 构造先增后减的凹二次条件均值
sales_revenue_obs = true_sales_revenue + np.random.normal(0, 2, observation_count) # 添加高斯噪声模拟观测误差
budget_train, budget_validation, sales_train, sales_validation = train_test_split(tv_ad_budget.reshape(-1, 1), sales_revenue_obs, test_size=0.3, random_state=42) # 固定留出30%验证样本
assert tv_ad_budget.min() < turning_point_budget < tv_ad_budget.max() # 拒绝把支持域外顶点解释为样本内转折
assert np.sum(tv_ad_budget < turning_point_budget) > 0 and np.sum(tv_ad_budget > turning_point_budget) > 0 # 要求转折点两侧都有证据数据准备完成后,表 3.4 在同一固定训练—验证切分上比较三种复杂度。缩放器只在训练样本中拟合;十次模型是否过拟合由验证损失而不是曲线是否难看来判断。
degrees = [1, 2, 10] # 分别尝试线性、二次和十次多项式
colors = ['steelblue', 'darkgreen', 'crimson'] # 各阶次对应的曲线颜色
titles = ['线性模型 (Linear)', '二次多项式 (Quadratic)', '10次多项式 (Degree 10)'] # 子图标题
polynomial_models = {} # 保存只在训练样本拟合的候选模型
polynomial_metric_records = [] # 收集训练损失、验证损失与条件数
for degree in degrees: # 在同一切分上逐一拟合预先声明的阶数
polynomial_model = Pipeline([ # 封装训练期缩放、幂基与OLS以防验证泄漏
('scale', StandardScaler()), # 只用训练预算估计中心与尺度
('poly', PolynomialFeatures(degree=degree, include_bias=False)), # 将标准化预算扩展为幂基
('linear', LinearRegression()) # 对扩展后的特征矩阵做OLS拟合
]) # 固定当前阶数的完整估计流程
polynomial_model.fit(budget_train, sales_train) # 禁止验证样本参与缩放与拟合
polynomial_models[degree] = polynomial_model # 保存预先固定候选供同一支持域绘图
transformed_train = polynomial_model[:-1].transform(budget_train) # 取得训练设计矩阵用于条件数诊断
design_condition_number = np.linalg.cond(np.column_stack([np.ones(len(transformed_train)), transformed_train])) # 量化幂基的数值敏感性
polynomial_metric_records.append({'degree': degree, 'train_mse': mean_squared_error(sales_train, polynomial_model.predict(budget_train)), 'validation_mse': mean_squared_error(sales_validation, polynomial_model.predict(budget_validation)), 'condition_number': design_condition_number}) # 登记可复算证据
polynomial_validation_evidence = pd.DataFrame(polynomial_metric_records).round(4) # 形成实际输出表而不预写胜者
polynomial_validation_evidence # 并列核对拟合、泛化与数值敏感性| degree | train_mse | validation_mse | condition_number | |
|---|---|---|---|---|
| 0 | 1 | 4.9719 | 5.4079 | 1.0000 |
| 1 | 2 | 3.5801 | 4.0633 | 2.8806 |
| 2 | 10 | 3.3807 | 4.5165 | 4984.3221 |
图 3.3 把拟合限制在实现样本的支持域,并标出已知转折点;虚线是真实模拟条件均值,不是模型可访问的训练标签。
x_grid_range = np.linspace(tv_ad_budget.min(), tv_ad_budget.max(), 300).reshape(-1, 1) # 只在实现样本支持域内绘制
true_sales_grid = 5 + 0.09 * x_grid_range[:, 0] - 0.0002 * x_grid_range[:, 0]**2 # 计算已知DGP供视觉校验
fig, axes = plt.subplots(1, 3, figsize=(18, 5)) # 让三种复杂度共享视觉尺度
for axis, degree, color, title in zip(axes, degrees, colors, titles): # 为每个预先固定候选绘制独立子图
y_predicted_values = polynomial_models[degree].predict(x_grid_range) # 在共同支持域网格预测
axis.scatter(budget_train[:, 0], sales_train, alpha=0.45, s=45, color='gray', label='训练样本') # 展示用于拟合的观测
axis.scatter(budget_validation[:, 0], sales_validation, alpha=0.65, s=45, color='#F0A700', label='验证样本') # 展示未参与拟合的观测
axis.plot(x_grid_range[:, 0], true_sales_grid, color='#2C3E50', linestyle='--', linewidth=1.8, label='真实条件均值') # 以不可供模型使用的真值作教学校验
axis.plot(x_grid_range[:, 0], y_predicted_values, color=color, linewidth=2.5, label=title) # 展示训练样本估计的候选曲线
axis.axvline(turning_point_budget, color='#008080', linestyle=':', linewidth=2, label='DGP转折点') # 标记支持域内顶点
axis.set_xlabel('电视广告预算(万元)', fontsize=11) # 明示输入支持域与金额单位
axis.set_ylabel('销售量(千件)', fontsize=11) # 明示响应尺度便于读取拟合偏差
axis.set_title(title, fontsize=12, fontweight='bold') # 标明候选复杂度
axis.legend(fontsize=9) # 解释样本、真实曲线与拟合曲线
axis.grid(True, alpha=0.3) # 帮助比较共同支持域中的形状偏差
plt.tight_layout() # 防止三幅候选图的图例与轴标签重叠
plt.show() # 输出复杂度、泛化损失与数值条件的视觉对照
当前固定种子下,表 3.4 给出的二次模型训练/验证 MSE 约为 3.5801/4.0633,十次模型约为 3.3807/4.5165:十次模型降低训练损失却提高验证损失,因此这里的“过拟合”有样本外证据。十次幂基的条件数也远高于二次模型,提示数值敏感性;但数值条件差与验证过拟合是两种诊断,前者不能单独证明后者。图 3.3 中二次拟合在 225 万元附近转为下降,与 DGP 和两侧样本覆盖一致;支持域之外不作形状外推。
3.6.3 潜在问题与诊断 (Potential Problems and Diagnostics)
在将线性回归应用于金融场景(如多因子选股或财务预测)时,经常会遇到破坏 OLS 假设的情形:
- 非线性关系 (Non-linearity):响应变量与预测变量的真实关系并非线性。
- 诊断:绘制残差图(Residual Plot)。若残差呈现明显模式(如抛物线),则需考虑多项式项。
- 误差项的相关性 (Correlation of Errors):观测值之间不独立(常见于时间序列或公司面板数据)。
- 诊断与处理:Durbin–Watson 统计量主要检查特定的一阶残差自相关模式。误差相关会使按独立抽样计算的经典标准误不一致,但偏差方向取决于相关结构与设计矩阵,不能一概写成低估。应依据时间、公司或行业依赖结构采用 HAC、聚类稳健协方差或相应模型,并说明其条件。
- 异方差性 (Heteroscedasticity):模型误差的方差不是常数。
- 诊断:漏斗形的残差图。
- 解决:使用加权最小二乘(WLS)或稳健标准误(Robust Standard Errors)。
- 异常值与高杠杆点 (Outliers and High Leverage Points):
- 诊断:计算学生化残差(Studentized Residuals)和 Cook 距离。
- 多重共线性 (Collinearity):预测变量之间高度相关。
诊断:计算方差膨胀因子(VIF)。 \[ \text{VIF}(\hat{\beta}_j) = \frac{1}{1 - R_j^2} \] 其中 \(R_j^2\) 是将 \(X_j\) 对其他所有预测变量进行回归得到的拟合优度。
判定:较大的 VIF 表明该系数在当前设计矩阵中难以精确分离,但 5 或 10 只是经验警戒线。是否重参数化、正则化或保留变量,应由估计对象与预测目标决定,而非机械删列。
3.7 本章小结 (Chapter Summary)
本章详细介绍了线性回归,这是统计学习中最基础也最重要的方法之一:
简单线性回归:使用一个预测变量来预测响应变量,通过最小二乘法估计截距和斜率。
多元线性回归:使用多个预测变量,系数描述控制模型内其他变量后的条件关联;普通观察性回归不自动分离独立或因果效应。
模型评估:
- 使用 RSE 估计模型内残差尺度;未来预测精度另由样本外损失评价
- 使用\(R^2\)衡量拟合优度
- 使用F检验判断整体显著性
- 使用t检验判断单个变量的显著性
模型扩展:
- 虚拟变量处理定性预测变量
- 交互项建模变量间的交互效应
- 多项式项和变换建模非线性关系
潜在问题:非线性、多重共线性、异方差、异常值等,需要诊断和适当处理。
线性回归是许多更复杂方法的基础,掌握它对于理解后续章节中的高级方法至关重要。关于经典线性模型、统计学习视角及其与更灵活方法的联系,可参见 James 等 (2023年) 与 Hastie 等 (2009年)。
3.8 理论来源与前沿
线性回归的理论基础来自最小二乘与正态线性模型:在误差独立同分布且同方差时,OLS 具有最佳线性无偏性(Gauss-Markov 定理);在正态误差假设下,OLS 与极大似然估计一致,并能导出精确的 t/F 检验与区间估计。现代统计学习强调在线性回归中系统处理三类工程问题:诊断(残差与杠杆点)、稳健性(厚尾/异方差)与可解释性(系数含义与变量选择)。
研究前沿方面,线性回归并未‘过时’,而是以新的形式持续活跃:
- 高维与稀疏建模:Lasso/Elastic Net 使 \(p\gg n\) 仍可估计,并把变量选择纳入优化。
- 稳健回归与分位数回归:在异常值与异方差普遍存在的金融数据中更可靠。
- 因果推断中的回归:在差分、匹配、工具变量等识别设计下,回归承担‘控制混杂’与‘估计处理效应’的角色。
3.9 综合案例:未来回撤的线性概率基线
本章前面的费用—营收分析是独立的真实财务案例,用于学习 OLS 推断与诊断。贯穿项目严格沿用 小节 2.7 的数据身份:响应仍是 drawdown_event_20d,键、特征和日期分段均不改变。为使本章从干净内核独立运行,下面在本章内定义并调用同一构造器;它不读取外部辅助脚本,也不改换研究对象。
先定义前向最大回撤函数。对路径 \(P_0,\ldots,P_{20}\),先计算运行峰值 \(H_j=\max_{0\le k\le j}P_k\),再取 \(\min_j(P_j/H_j-1)\)。因此价格路径 \(100\to120\to105\) 的最大回撤是 \(105/120-1=-12.5\%\),即使终点仍高于预测日价格,也必须记为事件。
展开共享回撤标签函数
import os # 读取跨平台数据根环境变量
from pathlib import Path # 用路径对象安全拼接本地文件
import numpy as np # 构造有限特征和时间分段
import pandas as pd # 处理公司—日期面板
def calculate_forward_max_drawdown(price_values, horizon_days=20): # 计算含预测日在内的前向峰谷最大回撤
price_array = np.asarray(price_values, dtype=float) # 转为连续数值数组以构造滑动路径
window_size = horizon_days + 1 # 路径包含t日与其后horizon_days个交易日
drawdowns = np.full(price_array.size, np.nan, dtype=float) # 末端窗口不完整时保持未知
if price_array.size < window_size: # 保护短序列而不虚构标签
return drawdowns # 返回全缺失结果表示没有完整路径
price_windows = np.lib.stride_tricks.sliding_window_view(price_array, window_size) # 构造每个预测日的完整价格路径
running_peaks = np.maximum.accumulate(price_windows, axis=1) # 逐路径记录每一时点之前的最高价
drawdowns[:price_windows.shape[0]] = np.min(price_windows / running_peaks - 1, axis=1) # 取峰值后最深谷值跌幅
return drawdowns # 返回与原价格序列等长的前向最大回撤
m02_fixture_drawdown = calculate_forward_max_drawdown([100, 120, 105], horizon_days=2)[0] # 用先涨后跌路径区分峰谷回撤与起点收益
assert np.isclose(m02_fixture_drawdown, -0.125) # 核对峰值120到谷值105的跌幅为百分之十二点五
assert m02_fixture_drawdown <= -0.10 # 核对该路径必须被判为百分之十回撤事件下面把数据读取、预测日特征和峰谷标签封装为一个准备函数。固定公司名单是教学研究边界,不是事后按收益挑出的“优胜者”;return_1d 等特征均以 \(t\) 日结束,只有标签计算向未来展开。
展开共享行情与标签准备函数
def prepare_m02_price_panel(book_data_dir): # 从统一数据根构造未切分的公司—预测日样本
data_root = Path(book_data_dir).expanduser().resolve() # 同时接受环境变量字符串或Path对象
assert data_root.is_dir(), f'BOOK_DATA_DIR 不存在: {data_root}' # 在读取前验证数据挂载
price_path = data_root / 'stock/stock_price_post_adjusted.h5' # 指向后复权日行情
assert price_path.is_file(), f'缺少后复权行情文件: {price_path}' # 在read_hdf前给出明确缺失文件
company_ids = ('600104.XSHG', '002415.XSHE', '600276.XSHG', '002230.XSHE') # 固定四家长三角教学公司
price_frames = [pd.read_hdf(price_path, where=f'order_book_id={company_id!r}', columns=['close', 'volume']).reset_index() for company_id in company_ids] # 在存储层选择所需公司和字段
price_panel = pd.concat(price_frames, ignore_index=True) # 合并为公司—交易日面板
price_panel['prediction_date'] = pd.to_datetime(price_panel['date']) # 把交易日定义为收盘后预测原点
price_panel = price_panel.loc[price_panel['prediction_date'].between('2012-01-01', '2024-12-31')].copy() # 固定研究观察期
price_panel = price_panel.sort_values(['order_book_id', 'prediction_date']).reset_index(drop=True) # 保证窗口只在公司内按时间推进
grouped_prices = price_panel.groupby('order_book_id', sort=False) # 建立公司内窗口边界
price_panel['return_1d'] = grouped_prices['close'].transform(lambda prices: prices.pct_change(fill_method=None)) # 计算截至预测日的一日收益
price_panel['momentum_5d'] = grouped_prices['close'].transform(lambda prices: prices.pct_change(5, fill_method=None)) # 计算截至预测日的五日动量
price_panel['volatility_20d'] = grouped_prices['return_1d'].transform(lambda returns: returns.rolling(20, min_periods=20).std()) # 计算过去二十日波动率
volume_mean_20d = grouped_prices['volume'].transform(lambda volume: volume.rolling(20, min_periods=20).mean()) # 估计预测日前成交量常态
price_panel['volume_ratio_20d'] = price_panel['volume'] / volume_mean_20d # 表示当日成交量相对过去均值的活跃度
feature_names = ['return_1d', 'momentum_5d', 'volatility_20d', 'volume_ratio_20d'] # 固定跨章特征名称与顺序
price_panel['max_drawdown_20d'] = grouped_prices['close'].transform(lambda prices: calculate_forward_max_drawdown(prices, horizon_days=20)) # 计算t至t+20真实峰谷最大回撤
price_panel['label_end_date'] = grouped_prices['prediction_date'].shift(-20) # 记录未来第二十个公司交易日
price_panel['drawdown_event_20d'] = price_panel['max_drawdown_20d'].le(-0.10).where(price_panel['max_drawdown_20d'].notna()).astype('Int64') # 完整路径才生成二元事件
price_panel = price_panel.replace([np.inf, -np.inf], np.nan).dropna(subset=feature_names + ['max_drawdown_20d', 'drawdown_event_20d', 'label_end_date']).copy() # 排除无效特征和未完成路径
return price_panel, feature_names # 把未切分样本交给公共构造函数公共函数负责固定切分、边界断言和四项跨章交付。任何章节在干净内核中都可以用 build_m02_shared_sample(BOOK_DATA_DIR) 重建同一对象,不需要依赖前一章的内存状态。
展开共享样本切分函数
def build_m02_shared_sample(book_data_dir): # 返回第2至第9章唯一的共享分析对象
price_panel, feature_names = prepare_m02_price_panel(book_data_dir) # 从统一数据根重建真实峰谷标签
validation_start = pd.Timestamp('2020-01-01') # 固定验证段起点
test_start = pd.Timestamp('2022-01-01') # 固定封存测试段起点
study_end = pd.Timestamp('2024-12-31') # 固定研究终点
train_mask = (price_panel['prediction_date'] < validation_start) & (price_panel['label_end_date'] < validation_start) # 清除伸入验证期的训练标签
validation_mask = price_panel['prediction_date'].between(validation_start, test_start, inclusive='left') & (price_panel['label_end_date'] < test_start) # 清除伸入测试期的验证标签
test_mask = price_panel['prediction_date'].between(test_start, study_end, inclusive='both') # 标记只供第九章最终评价的日期
price_panel['split'] = np.select([train_mask, validation_mask, test_mask], ['train', 'validation', 'test'], default='unused') # 赋予互斥时间段
shared_columns = ['order_book_id', 'prediction_date', 'label_end_date', *feature_names, 'max_drawdown_20d', 'drawdown_event_20d', 'split'] # 复用第2章权威字段顺序
shared_sample = price_panel.loc[price_panel['split'] != 'unused', shared_columns].copy() # 排除边界隔离行并形成共享对象
assert list(shared_sample.columns) == shared_columns # 阻止同名对象发生字段或顺序漂移
assert not shared_sample.duplicated(['order_book_id', 'prediction_date']).any() # 验证公司—预测日键唯一
assert np.isfinite(shared_sample['max_drawdown_20d']).all() and shared_sample['max_drawdown_20d'].le(0).all() # 验证连续目标有限且非正
assert shared_sample['drawdown_event_20d'].astype(bool).eq(shared_sample['max_drawdown_20d'].le(-0.10)).all() # 验证二元标签由连续目标唯一派生
assert set(shared_sample['drawdown_event_20d'].unique()) == {0, 1} # 验证二元标签包含两类
assert shared_sample.loc[shared_sample['split'] == 'train', 'label_end_date'].max() < validation_start # 验证训练标签不触及验证期
assert shared_sample.loc[shared_sample['split'] == 'validation', 'label_end_date'].max() < test_start # 验证验证标签不触及测试期
training_event_rate = float(shared_sample.loc[shared_sample['split'] == 'train', 'drawdown_event_20d'].mean()) # 只用训练标签估计概率基线
test_keys = shared_sample.loc[shared_sample['split'] == 'test', ['order_book_id', 'prediction_date']].copy() # 固定第九章最终比较键
return shared_sample, feature_names, training_event_rate, test_keys # 返回约定的四项跨章交付book_data_dir_value = os.environ.get('BOOK_DATA_DIR') # 安全读取统一数据根配置
assert book_data_dir_value, '请先设置 BOOK_DATA_DIR,使其指向包含 stock/ 子目录的数据根' # 缺失时说明修复方法
BOOK_DATA_DIR = Path(book_data_dir_value).expanduser().resolve() # 解析统一数据根
m02_shared_sample, m02_features, m02_training_event_rate, m02_test_keys = build_m02_shared_sample(BOOK_DATA_DIR) # 在当前干净内核构造共享对象构造完成后,开发期应同时包含事件与非事件,训练、验证和测试键均非空;本章后续不会索引测试段响应。
线性概率模型把 \(E(Y\mid X)\) 写成线性函数,优点是系数透明,缺点是原始拟合值可能落在 \([0,1]\) 外且误差必然异方差。因此它在这里是预测基线而不是最终概率模型:原始分数用于 AUROC 排序,裁剪到 \([0,1]\) 后才计算 Brier;系数只描述训练样本中的条件关联。
下面先执行字段和测试隔离检查,再只取训练段拟合、验证段评价。测试键不进入任何矩阵。
from sklearn.metrics import brier_score_loss, roc_auc_score # 计算有效概率误差和排序能力
m03_expected_columns = ['order_book_id', 'prediction_date', 'label_end_date', *m02_features, 'max_drawdown_20d', 'drawdown_event_20d', 'split'] # 声明第2章连续与二元目标的有序交接模式
assert list(m02_shared_sample.columns) == m03_expected_columns # 阻止字段、目标或顺序漂移
m03_train_sample = m02_shared_sample.loc[m02_shared_sample['split'] == 'train'].copy() # 只取固定训练键
m03_validation_sample = m02_shared_sample.loc[m02_shared_sample['split'] == 'validation'].copy() # 只取固定验证键
assert set(m02_shared_sample['split']) >= {'train', 'validation', 'test'} # 确认封存测试段仍存在但未使用
m03_train_features = sm.add_constant(m03_train_sample[m02_features], has_constant='add') # 为线性概率基线加入截距
m03_validation_features = sm.add_constant(m03_validation_sample[m02_features], has_constant='add') # 复用相同列顺序构造验证矩阵
m03_linear_probability = sm.OLS(m03_train_sample['drawdown_event_20d'].astype(float), m03_train_features).fit() # 仅在训练段拟合线性条件均值
m03_validation_raw_score = m03_linear_probability.predict(m03_validation_features) # 保留未约束分数用于排序和范围诊断
m03_validation_probability = np.clip(m03_validation_raw_score, 0, 1) # 裁剪后才作为概率计算Brier
m03_event_rate_probability = np.repeat(m02_training_event_rate, len(m03_validation_sample)) # 复用第2章训练期事件率基线动态输出应按固定顺序阅读:先核对训练/验证样本数和原始分数范围;若范围越界,正好说明线性概率模型的限制。再比较裁剪后 Brier 与事件率基线,数值越低越好;AUROC 只衡量排序,不能证明校准。若 Brier 没有下降,就保留事件率基线。
m03_metric_table = pd.DataFrame({'model': ['training_event_rate', 'linear_probability_clipped'], 'validation_brier': [brier_score_loss(m03_validation_sample['drawdown_event_20d'], m03_event_rate_probability), brier_score_loss(m03_validation_sample['drawdown_event_20d'], m03_validation_probability)]}) # 在同一验证键比较概率误差
print('训练/验证样本数:', len(m03_train_sample), len(m03_validation_sample)) # 核对评价分母
print('线性概率原始验证分数范围:', float(m03_validation_raw_score.min()), float(m03_validation_raw_score.max())) # 诊断是否越过概率边界
print('线性概率验证AUROC:', roc_auc_score(m03_validation_sample['drawdown_event_20d'], m03_validation_raw_score)) # 报告不要求概率校准的排序指标
print(m03_metric_table.to_string(index=False)) # 比较模型与训练事件率基线
print(m03_linear_probability.params.rename('coefficient').to_string()) # 读取方向和尺度但不作因果解释训练/验证样本数: 7572 1864
线性概率原始验证分数范围: 0.026764654067052873 0.9226507354417434
线性概率验证AUROC: 0.691939783697171
model validation_brier
training_event_rate 0.229024
linear_probability_clipped 0.206281
const -0.120933
return_1d -0.048724
momentum_5d 0.087454
volatility_20d 16.601110
volume_ratio_20d 0.027054
决策结论必须条件化:只有验证 Brier 相对训练事件率基线下降、排序有用且原始分数越界风险可接受时,线性概率模型才值得作为后续分类器的比较对象。无论系数方向如何,都不能说某个行情特征“导致”未来回撤。
3.10 练习
3.10.1 概念题
区分某次系数估计的抽样误差与估计量的偏差。 [核心|难度:1|分值:5|任务:独立]
说明 OLS 点估计、经典标准误和正态模型下精确 \(t/F\) 推断分别需要哪些条件。 [核心|难度:2|分值:8|任务:独立]
在含截距模型中解释 \(R^2\),并说明为什么样本外 \(R^2\) 可以为负。 [核心|难度:2|分值:6|任务:独立]
解释双对数模型中的斜率,并说明为什么它默认不是因果弹性。 [核心|难度:2|分值:6|任务:独立]
3.10.2 应用题
使用本地财务数据估计对数营收对对数销售费用的简单回归,再加入对数资产和行业虚拟变量。比较斜率、标准误与残差诊断。 [核心|难度:2|分值:20|任务:独立]
分两部分完成:(a)为“研发强度与下一期营收增长”设计一个含交互项的回归,明确分析单位、信息时点、基准组和交互系数解释;(b)复现 小节 3.9 的线性概率基线,解释原始分数越界、裁剪后 Brier 与 AUROC 各自回答什么问题。 [核心|难度:3|分值:20|任务:综合案例]
3.10.3 理论题
从 RSS 的一阶条件推导简单线性回归斜率 \(\hat\beta_1\)。 [拓展|难度:2|分值:15|任务:推导]
推导多元 OLS 正规方程,并说明 \(X^TX\) 不可逆时应如何处理。 [拓展|难度:3|分值:20|任务:推导]
展开完整参考解答与评分键
3.11 练习参考解答
3.11.1 概念题解答
抽样误差是某个样本估计值与真值的差,例如 \(\hat\beta_1-\beta_1\);偏差是重复抽样平均误差 \(E(\hat\beta_1)-\beta_1\)。无偏估计量在单次样本中仍几乎总有抽样误差。
计算 OLS 点估计需要设计矩阵满列秩。若要在给定 \(X\) 下无偏,需要 \(E(\epsilon\mid X)=0\);经典同方差标准误还要求条件方差恒定且观测误差结构适合独立抽样。正态误差使标准化统计量在有限样本精确服从 \(t/F\) 分布;大样本稳健推断可在较弱条件下使用,但必须匹配异方差或相关结构。
含截距的样本内 OLS 中,\(R^2=1-\mathrm{RSS}/\mathrm{TSS}\) 是相对于样本均值基线的拟合改善比例,所以位于 \([0,1]\)。样本外若模型预测误差平方和大于用训练期均值作预测的误差平方和,则相同形式的测试 \(R^2<0\),表示模型不如基线。
在 \(\log Y=\beta_0+\beta_1\log X+\cdots+\epsilon\) 中,\(\beta_1\) 是控制模型内其他变量后 \(X\) 增加 1% 时条件均值约变化 \(\beta_1\)%。遗漏变量、反向决定、选择偏差和测量误差都可能破坏因果解释,因此普通观察性 OLS 默认只给条件关联弹性。
3.11.2 应用题解答
销售费用回归(20 分):复用 图 3.1 对应的 2023Q4 公司横截面、事前金额门槛、公司唯一键、
log_revenue与log_sell_exp,不要复制数据读取和清洗代码。再从同一时点的资产字段与公司资料表取得total_assets和行业编码;取对数前要求资产为正,并在合并后再次断言公司键唯一。估计以下两个含截距模型:\[ M_A:\ \log_{10}(Revenue_i)=\beta_0+\beta_1\log_{10}(SellingExpense_i)+\epsilon_i, \]
\[ M_B:\ \log_{10}(Revenue_i)=\beta_0+\beta_1\log_{10}(SellingExpense_i)+\beta_2\log_{10}(Assets_i)+\sum_{k\ne k_0}\gamma_kIndustry_{ik}+\epsilon_i. \]
行业 \(k_0\) 必须在拟合前锁定为样本数最多的行业;稀有行业若合并为“其他”,规则也须事前声明。两个模型均报告估计样本数、\(\hat\beta_1\)、HC3 标准误、95% 置信区间、\(R^2\)、调整 \(R^2\) 与残差自由度,并给出 \(\hat\beta_1\) 在加入规模和行业控制后的变化。诊断按 小节 3.6.3 执行:至少提交残差—拟合值图、学生化残差、杠杆值/Cook 距离、Breusch–Pagan 异方差检验以及模型 B 的条件数或 VIF;任何高影响点都只能做有记录的敏感性分析,不能为改善结论而删除。横截面 HC3 处理形式未知的异方差,不处理遗漏变量、反向决定或跨公司相关;因此斜率只能解释为给定模型和样本内的条件关联,不能写成销售费用的因果回报。评分为数据身份与唯一键 4 分、两模型与基准行业 4 分、规定输出 4 分、诊断 4 分、边界解释 4 分。
3.11.2.1 练习 5:可执行参考实现
本实现直接复用正文已经读取、合并并筛选的 df_analysis,不重复载入数据。行业处理规则在拟合前固定为:样本少于 30 家的中信一级行业合并为“其他”,合并后样本数最多的行业锁定为基准组;该规则不查看营业收入或回归结果。模型 A 与模型 B 使用同一组公司,因此斜率变化不会混入样本构成变化。
from statsmodels.stats.diagnostic import het_breuschpagan # 计算Breusch--Pagan异方差诊断
m03_ex5_rare_floor = 30 # 在拟合前声明稀有行业至少需要30家公司
m03_ex5_industry_counts = df_analysis['citics_2019_l1_name'].value_counts() # 只按行业规模执行预声明合并
m03_ex5_rare_industries = m03_ex5_industry_counts[m03_ex5_industry_counts.lt(m03_ex5_rare_floor)].index # 识别稀有行业
m03_ex5_sample = df_analysis.copy() # 复用正文唯一的2023Q4合格横截面
m03_ex5_sample['industry_group'] = m03_ex5_sample['citics_2019_l1_name'].where(~m03_ex5_sample['citics_2019_l1_name'].isin(m03_ex5_rare_industries), '其他') # 合并稀有行业
m03_ex5_baseline = m03_ex5_sample['industry_group'].value_counts().idxmax() # 在拟合前锁定样本数最多行业
m03_ex5_other_levels = sorted(set(m03_ex5_sample['industry_group']) - {m03_ex5_baseline}) # 固定其余行业的列顺序
m03_ex5_levels = [m03_ex5_baseline, *m03_ex5_other_levels] # 把基准行业放在虚拟变量首位
m03_ex5_sample['industry_group'] = pd.Categorical(m03_ex5_sample['industry_group'], categories=m03_ex5_levels) # 固定类别编码
m03_ex5_dummies = pd.get_dummies(m03_ex5_sample['industry_group'], prefix='industry', drop_first=True, dtype=float) # 删除基准行业列
m03_ex5_design_a = sm.add_constant(m03_ex5_sample[['log_sell_exp']], has_constant='add') # 构造简单模型设计矩阵
m03_ex5_design_b = sm.add_constant(pd.concat([m03_ex5_sample[['log_sell_exp', 'log_total_assets']], m03_ex5_dummies], axis=1), has_constant='add') # 加入规模和行业控制
assert len(m03_ex5_design_a) == len(m03_ex5_design_b) == len(m03_ex5_sample) # 确认两个模型使用相同公司
assert np.linalg.matrix_rank(m03_ex5_design_b.to_numpy()) == m03_ex5_design_b.shape[1] # 确认扩展设计矩阵满列秩下一块使用同一响应拟合两个含截距模型,并把规定的数值证据集中到 表 3.6。HC3 只改变协方差估计,不改变 OLS 点估计。
m03_ex5_response = m03_ex5_sample['log_revenue'] # 取得两个模型共享的对数营收响应
m03_ex5_model_a = sm.OLS(m03_ex5_response, m03_ex5_design_a).fit(cov_type='HC3') # 以HC3拟合简单模型A
m03_ex5_model_b = sm.OLS(m03_ex5_response, m03_ex5_design_b).fit(cov_type='HC3') # 以HC3拟合扩展模型B
m03_ex5_models = {'A:销售费用': m03_ex5_model_a, 'B:加规模与行业': m03_ex5_model_b} # 固定比较表的模型顺序
m03_ex5_records = [] # 收集统一口径的模型证据
for m03_ex5_name, m03_ex5_fit in m03_ex5_models.items(): # 对两模型提取同一组统计量
m03_ex5_ci = m03_ex5_fit.conf_int().loc['log_sell_exp'] # 取得销售费用斜率的HC3区间
m03_ex5_records.append({'模型': m03_ex5_name, 'n': int(m03_ex5_fit.nobs), '销售费用斜率': m03_ex5_fit.params['log_sell_exp'], 'HC3标准误': m03_ex5_fit.bse['log_sell_exp'], '95%区间下限': m03_ex5_ci.iloc[0], '95%区间上限': m03_ex5_ci.iloc[1], 'R2': m03_ex5_fit.rsquared, '调整R2': m03_ex5_fit.rsquared_adj, '残差自由度': int(m03_ex5_fit.df_resid)}) # 保存可核对输出
m03_ex5_comparison = pd.DataFrame(m03_ex5_records) # 形成模型比较表
m03_ex5_slope_change = m03_ex5_model_b.params['log_sell_exp'] - m03_ex5_model_a.params['log_sell_exp'] # 计算加入控制后的斜率变化
m03_ex5_comparison['相对A斜率变化'] = [0.0, m03_ex5_slope_change] # 明示共同样本下的变化量m03_ex5_comparison.round(4) # 渲染样本数、斜率、稳健推断与拟合统计量| 模型 | n | 销售费用斜率 | HC3标准误 | 95%区间下限 | 95%区间上限 | R2 | 调整R2 | 残差自由度 | 相对A斜率变化 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | A:销售费用 | 4993 | 0.5620 | 0.0115 | 0.5395 | 0.5846 | 0.3681 | 0.3680 | 4991 | 0.0000 |
| 1 | B:加规模与行业 | 4993 | 0.1961 | 0.0080 | 0.1804 | 0.2118 | 0.8577 | 0.8569 | 4964 | -0.3659 |
诊断表同时保留两模型的残差与影响度量。Cook 距离超过经验阈值 \(4/n\) 的公司只被计数和标记,不从估计样本删除;Breusch–Pagan 检验使用原始 OLS 残差,而报告的系数推断仍采用 HC3。
m03_ex5_influence_a = m03_ex5_model_a.get_influence() # 计算模型A学生化残差、杠杆值与Cook距离
m03_ex5_influence_b = m03_ex5_model_b.get_influence() # 计算模型B学生化残差、杠杆值与Cook距离
m03_ex5_studentized_b = m03_ex5_influence_b.resid_studentized_internal # 预先保存模型B内部学生化残差供表图共用
m03_ex5_cook_b = m03_ex5_influence_b.cooks_distance[0] # 预先保存模型B逐公司Cook距离供表图共用
m03_ex5_influences = {'A:销售费用': m03_ex5_influence_a, 'B:加规模与行业': m03_ex5_influence_b} # 对齐模型与影响诊断
m03_ex5_diagnostic_records = [] # 收集两模型同口径诊断
for m03_ex5_name, m03_ex5_fit in m03_ex5_models.items(): # 对两模型执行相同诊断规则
m03_ex5_influence = m03_ex5_influences[m03_ex5_name] # 取得当前模型影响对象
m03_ex5_bp = het_breuschpagan(m03_ex5_fit.resid, m03_ex5_fit.model.exog) # 计算BP的LM统计量及p值
m03_ex5_cook = m03_ex5_influence.cooks_distance[0] # 取得逐公司Cook距离
m03_ex5_diagnostic_records.append({'模型': m03_ex5_name, 'BP LM': m03_ex5_bp[0], 'BP p值': m03_ex5_bp[1], '最大绝对内部学生化残差': np.abs(m03_ex5_influence.resid_studentized_internal).max(), '最大杠杆值': m03_ex5_influence.hat_matrix_diag.max(), '最大Cook距离': m03_ex5_cook.max(), 'Cook大于4/n的公司数': int((m03_ex5_cook > 4 / m03_ex5_fit.nobs).sum()), '条件数': m03_ex5_fit.condition_number}) # 保存异方差、异常值与共线性证据
m03_ex5_diagnostics = pd.DataFrame(m03_ex5_diagnostic_records) # 形成可评分诊断表m03_ex5_diagnostics.round(4) # 渲染完整诊断结果而不删除任何公司| 模型 | BP LM | BP p值 | 最大绝对内部学生化残差 | 最大杠杆值 | 最大Cook距离 | Cook大于4/n的公司数 | 条件数 | |
|---|---|---|---|---|---|---|---|---|
| 0 | A:销售费用 | 22.1602 | 0.0 | 4.3759 | 0.0037 | 0.0151 | 237 | 89.1459 |
| 1 | B:加规模与行业 | 312.9472 | 0.0 | 6.9613 | 0.0304 | 0.0098 | 343 | 235.2459 |
图 3.4 把表中的诊断定位到具体图形模式。前两幅图检查残差均值结构;后两幅图分别显示模型 B 的内部学生化残差,以及杠杆值与 Cook 距离的共同位置。
m03_ex5_fig, m03_ex5_axes = plt.subplots(2, 2, figsize=(12, 9)) # 创建四联诊断图
m03_ex5_axes[0, 0].scatter(m03_ex5_model_a.fittedvalues, m03_ex5_model_a.resid, alpha=0.45, s=18, color='#3b6ea8') # 绘制模型A残差与拟合值
m03_ex5_axes[0, 0].axhline(0, color='#b22222', linewidth=1) # 标出模型A零残差基准
m03_ex5_axes[0, 0].set(title='模型 A:残差—拟合值', xlabel='拟合对数营收', ylabel='残差') # 标明模型A坐标含义
m03_ex5_axes[0, 1].scatter(m03_ex5_model_b.fittedvalues, m03_ex5_model_b.resid, alpha=0.45, s=18, color='#2e8b57') # 绘制模型B残差与拟合值
m03_ex5_axes[0, 1].axhline(0, color='#b22222', linewidth=1) # 标出模型B零残差基准
m03_ex5_axes[0, 1].set(title='模型 B:残差—拟合值', xlabel='拟合对数营收', ylabel='残差') # 标明模型B坐标含义
m03_ex5_axes[1, 0].scatter(np.arange(len(m03_ex5_studentized_b)), m03_ex5_studentized_b, alpha=0.5, s=16, color='#7b3294') # 按公司位置展示异常残差
m03_ex5_axes[1, 0].axhline(2, color='#b22222', linestyle='--', linewidth=1) # 标出正向经验参考线
m03_ex5_axes[1, 0].axhline(-2, color='#b22222', linestyle='--', linewidth=1) # 标出负向经验参考线
m03_ex5_axes[1, 0].set(title='模型 B:内部学生化残差', xlabel='样本位置', ylabel='学生化残差') # 标明异常残差读法
m03_ex5_axes[1, 1].scatter(m03_ex5_influence_b.hat_matrix_diag, m03_ex5_cook_b, alpha=0.5, s=18, color='#d17c21') # 联合展示杠杆值和影响度
m03_ex5_axes[1, 1].axhline(4 / m03_ex5_model_b.nobs, color='#b22222', linestyle='--', linewidth=1) # 标出Cook经验阈值
m03_ex5_axes[1, 1].set(title='模型 B:杠杆值与 Cook 距离', xlabel='杠杆值', ylabel='Cook 距离') # 标明影响诊断坐标
for m03_ex5_axis in m03_ex5_axes.flat: # 对四个面板应用一致网格
m03_ex5_axis.grid(alpha=0.2) # 提高参考线与点云的可读性
m03_ex5_fig.tight_layout() # 防止标题与坐标标签重叠
plt.show() # 输出仅由既有诊断对象生成的图形
最后一块让解释随现场输出变化。斜率变化的方向与大小由共同样本的两个拟合决定;BP 检验和 Cook 阈值只触发诊断提示,不作为删样规则。
m03_ex5_change_direction = '下降' if m03_ex5_slope_change < 0 else '上升' if m03_ex5_slope_change > 0 else '未变' # 按实际斜率差决定措辞
m03_ex5_bp_p = m03_ex5_diagnostics.loc[m03_ex5_diagnostics['模型'].eq('B:加规模与行业'), 'BP p值'].iloc[0] # 读取模型B异方差证据
m03_ex5_bp_conclusion = '拒绝同方差原假设' if m03_ex5_bp_p < 0.05 else '未拒绝同方差原假设' # 按5%阈值条件化诊断
m03_ex5_influential_count = int((m03_ex5_cook_b > 4 / m03_ex5_model_b.nobs).sum()) # 读取实际高影响公司数
print(f'共同样本中,加入规模和行业控制后销售费用斜率{m03_ex5_change_direction}{abs(m03_ex5_slope_change):.4f};这说明简单模型斜率混合了控制变量关联,但不证明遗漏偏差已消除。') # 条件解释斜率变化
print(f'模型B的BP p值为{m03_ex5_bp_p:.4g},在5%水平上{m03_ex5_bp_conclusion};无论结果如何,表中的HC3均用于允许未知形式异方差。') # 条件解释异方差检验
print(f'Cook距离超过4/n的公司有{m03_ex5_influential_count}家;它们应进入有记录的敏感性分析,而不是为改善结论而删除。') # 条件解释影响点
print('两模型都是2023Q4观察性横截面;HC3不处理遗漏变量、反向决定或跨公司相关,销售费用斜率只能解释为给定样本与模型内的条件关联。') # 保留非因果边界共同样本中,加入规模和行业控制后销售费用斜率下降0.3659;这说明简单模型斜率混合了控制变量关联,但不证明遗漏偏差已消除。
模型B的BP p值为6.535e-50,在5%水平上拒绝同方差原假设;无论结果如何,表中的HC3均用于允许未知形式异方差。
Cook距离超过4/n的公司有343家;它们应进入有记录的敏感性分析,而不是为改善结论而删除。
两模型都是2023Q4观察性横截面;HC3不处理遗漏变量、反向决定或跨公司相关,销售费用斜率只能解释为给定样本与模型内的条件关联。
(a)可设 \[g_{i,t+1}=\beta_0+\beta_1 RD_{it}+\beta_2 D_i+\beta_3(RD_{it}D_i)+\gamma^TZ_{it}+\epsilon_{i,t+1},\] 其中 \(g_{i,t+1}\) 是公司 \(i\) 下一报告期营收增长,\(RD_{it}\) 是时点 \(t\) 已披露研发强度,\(D_i=1\) 表示高技术行业,\(Z_{it}\) 含规模等控制。基准组为非高技术行业:其研发强度斜率为 \(\beta_1\);高技术行业斜率为 \(\beta_1+\beta_3\),两组差为 \(\beta_3\)。使用按披露日排列的前向切分,并按公司聚类或采用与误差结构匹配的标准误。观察性结果不自动识别研发投入的因果回报。
(b)完整实现直接复用 小节 3.9,不复制共享行情构造器;可评分数值输出以 表 3.5 为唯一规范表。必须保持正文参数:四个既定行情特征、训练事件率常数概率、线性概率 OLS,以及将原始分数裁剪到 \([0,1]\) 后计算验证 Brier;同时报告原始分数的最小值、最大值、越界比例、验证 AUROC、训练/验证样本数和日期边界。原始线性分数可越过 \([0,1]\),所以它适合诊断与排序;裁剪值才是 Brier 的合法概率输入,但裁剪会改变损失,必须明示;AUROC 对严格单调变换不敏感,只衡量排序。若模型 Brier 未低于训练事件率基线、排序没有增量或越界严重,结论必须保留常数基线。验证期只用于本章诊断,不得打开
test,也不得把系数写成行情特征导致未来回撤。
3.11.3 理论题解答
对 \[RSS(\beta_0,\beta_1)=\sum_i(y_i-\beta_0-\beta_1x_i)^2\] 求导并令零: \[\sum_i(y_i-\hat\beta_0-\hat\beta_1x_i)=0,\] \[\sum_ix_i(y_i-\hat\beta_0-\hat\beta_1x_i)=0.\] 第一式给出 \(\hat\beta_0=\bar y-\hat\beta_1\bar x\)。代入第二式并中心化: \[\sum_i(x_i-\bar x)(y_i-\bar y) =\hat\beta_1\sum_i(x_i-\bar x)^2,\] 因而 \[\hat\beta_1=\frac{\sum_i(x_i-\bar x)(y_i-\bar y)} {\sum_i(x_i-\bar x)^2}.\]
矩阵目标为 \(RSS(\boldsymbol\beta)=(\mathbf y-X\boldsymbol\beta)^T(\mathbf y-X\boldsymbol\beta)\)。展开并求梯度: \[\nabla RSS=-2X^T\mathbf y+2X^TX\boldsymbol\beta.\] 令梯度为零得到正规方程 \(X^TX\hat{\boldsymbol\beta}=X^T\mathbf y\)。若 \(X\) 满列秩, \[\hat{\boldsymbol\beta}=(X^TX)^{-1}X^T\mathbf y.\] 若不可逆,系数不唯一;应先诊断完全共线性并删除冗余列或重新参数化。预测任务也可在明确目标下使用广义逆或正则化,但此时系数解释和推断必须随估计目标一起改变。
3.12 章末回顾
读完本章,应能把“一个回归结果”拆成估计对象、抽样条件、点估计、标准误、区间和诊断证据。尤其要记住:单次估计偏离真值不等于估计量有偏;含截距是样本内 \(R^2\in[0,1]\) 的关键条件;有限样本区间使用相应的 \(t\) 分位数;条件关联不等于因果效应。
无提示检索
- OLS 点估计、条件无偏性、经典同方差标准误和有限样本精确 \(t/F\) 推断分别需要哪些条件?
- 为什么 HC3 能处理横截面异方差,却不能自动处理公司间或时间上的误差相关?
- 在线性概率基线中,原始分数、裁剪后概率、Brier 分数和 AUROC 分别回答什么问题?
展开检索反馈与定向返回
若第 1 题不确定,返回“评估系数估计的准确性”及 式 3.14;若第 2 题不确定,返回“潜在问题与诊断”,并对照本章 2023Q4 横截面的 HC3 输出;若第 3 题不确定,返回 小节 3.9 的动态指标阅读顺序。回答时必须分别写出估计对象、依赖条件和评价数据,不能只列软件函数名。
下一章将把二元结果的条件均值写成合法概率模型,并比较逻辑回归、判别分析与朴素贝叶斯;本章的线性概率模型继续作为透明基线,而不是最终概率模型。