1  统计学习导论 (Introduction)

1.1 导读

先修:能阅读基础 Python 数据处理代码,并理解均值、方差、条件期望、平方损失与时间先后。

1.2 学习目标

  1. 区分监督学习与无监督学习;达成标准是能对三个新任务分类并说明依据。
  2. 在平方损失下写出 \(f(X)=E(Y\mid X)\)\(E(\epsilon\mid X)=0\);达成标准是能给出反例说明无条件零均值不足够。
  3. 为金融预测任务定义样本单位、特征、标签与可用时点;达成标准是画出无交叉的时间线。
  4. 用训练期信息定义朴素基线;达成标准是测试标签只出现在最终评价中。
  5. 区分样本关联、预测力与因果/市场机制结论;达成标准是为图形结论写出可被证据支持的边界。

1.2.1 本章路线

先把商业问题写成样本单位、特征、目标与决策时点,再判断它属于监督或无监督任务。随后用长三角财务与行情案例练习数据身份、信息时点与训练期基线,最后区分“样本关联”、“样本外预测证据”与“因果解释”。完成章末数据任务后,带着已定义的损失和证据边界进入第 2 章。

1.2.2 学习前自测

若用 \(t\) 日及以前信息预测 \(t+1\) 日涨跌,哪些变量可用?

只能使用在 \(t\) 日收盘前确已可得的特征;常数类别必须由训练期多数类决定,不能用验证期或测试期的涨跌比例反过来更新预测规则。

1.3 统计学习概述 (An Overview of Statistical Learning)

统计学习(Statistical Learning)是一组用于理解数据关系、进行预测并量化不确定性的工具与框架。在商业与经济分析中,它帮助研究者把结构化或非结构化数据转化为可检验的模式和决策证据。这些工具通常分为两大类:监督学习(Supervised Learning)和无监督学习(Unsupervised Learning)。

从形式化角度看,假设我们观测到响应变量 (Response Variable,或因变量) \(Y\)(例如公司的季报净利润、某只股票的次日收益率,或者某个客户是否会违约),以及 \(p\) 个特征 (Features,或自变量) \(X_1, X_2, \dots, X_p\)(例如宏观经济指标、公司财务比率、客户的历史交易记录等)。我们把 \(Y\) 与特征向量 \(X=(X_1,X_2,\dots,X_p)\) 的关系抽象为:

\[ Y = f(X) + \epsilon \tag{1.1}\]

在平方损失下,令 \(f(X)=E(Y\mid X)\),则 \(\epsilon=Y-f(X)\) 满足关键条件 \(E(\epsilon\mid X)=0\)。这个“零条件均值”保证 \(f(X)\) 是给定 \(X\) 后的最优平方损失预测。它不要求 \(\epsilon\)\(X\) 独立,也不要求同方差或正态:金融数据中 \(\operatorname{Var}(\epsilon\mid X)\) 随市场状态改变很常见。独立性是更强的附加假设;同方差主要用于经典效率结果;正态性主要用于小样本精确推断。现代统计学习中的 \(f\) 既可以是线性模型,也可以是复杂的非线性映射。

在监督学习中,历史数据同时包含特征与响应 \(Y\),模型据此学习预测规则或描述条件关系。常见任务包括信用风险估计、需求预测和客户流失识别。在无监督学习中,数据只有输入变量而没有目标标签 \(Y\);研究者可以据此探索客户分群、变量共同变化或证券特征的相似结构,但所得群组不自动对应真实经济类型。

为了直观展示统计学习的工作流程,我们可以参考 图 1.1

原始数据 (X, Y) 学习算法 (f) 估计值 (Ŷ) 性能评估 训练 预测 对比误差
图 1.1: 统计学习工作流示意图

为了说明统计学习的一些应用,我们简要讨论本书中考虑的三个真实世界数据集。

统计学习在中国金融领域的典型应用

统计学习可以用于中国金融与商业分析中的多类任务。下列例子说明问题如何映射为统计目标,不代表某个具体机构已经采用某一模型:

  • 智能信贷风控:互联网金融平台可以用梯度提升树和深度学习整合用户行为特征,估计贷款申请人的违约概率。效果取决于标签窗口、基准模型和样本外评估,没有可核验对照证据时不声称固定降幅。
  • 量化研究:可用 Lasso 或弹性网络筛选高维候选因子,也可把树模型和序列模型列为非线性候选;任何增量价值都需要独立时期、基准收益和交易成本检验。
  • 保险精算与定价:可用生存分析估计理赔、退保或失效时间;定价解释还需考虑删失机制、监管约束和公平性。
  • 供应链优化:可用时间序列模型预测区域需求,并把预测误差映射到库存、缺货和运输成本。

这些应用共享一个核心挑战:在有限样本与噪声下估计 \(f\),并用样本外评价判断模型相对基线是否提供稳定增量,而不是把随机波动误认成可重复规律。

1.4 上市公司财务数据 (Corporate Financial Data)

在这个应用中,我们研究影响中国长三角地区上市公司净利润 (Net Profit) 的多个因素。具体而言,我们希望了解公司的营业收入 (Revenue)、总资产 (Total Assets) 以及所处行业与其盈利能力之间的关联。这个数据集通过 RQSDK 获取, 并经过本地清洗和整理。

考虑 图 1.2 的左侧面板。阅读时先核对图中样本量、观察期和坐标单位,再根据当次散点与平滑曲线描述净利润和营业收入的样本关联。点云在相同收入水平上的离散程度反映了仅用收入预测净利润的剩余误差;它不支持因果解释。

我们还有每家公司的总资产规模和行业分类信息。图 1.2 的中图和右图用于比较不同资产区间与子行业中的利润分布;方向、离散程度和组间差异必须从当次输出读取,并受样本构成与会计口径限制。

后续章节会检验营业收入、总资产和行业信息是否相对简单均值或单变量基线改善样本外预测。变量更多不保证预测更准,结论取决于切分、损失和验证证据。

下面分步读取财务报表和公司基本信息,说明样本合并、支持域筛选、对数变换与描述性作图。图形用于检查净利润与营收、资产的样本关联,不替代预测评价或因果识别。

第一步:导入库与配置环境

首先,我们需要导入数据分析和可视化所需的Python库,并进行中文字体和数据路径的基本配置。numpy 负责数值运算,pandas 负责数据框操作,matplotlibseaborn 负责图形绘制,os 负责操作系统兼容的路径处理。

import numpy as np  # 为金额对数变换和有限值清洗提供数值运算
import pandas as pd  # 读取并按公司键合并财务表与基础信息表
import matplotlib.pyplot as plt  # 绘制三个财务变量的样本关联面板
import seaborn as sns  # 按行业展示对数利润的条件分布
import os  # 将在线教材的固定数据根同步给本章后续独立代码块
from pathlib import Path  # 用跨平台路径对象解析数据根目录

plt.rcParams['font.family'] = ['Source Han Serif SC']  # 统一使用思源宋体避免出版字体回退
plt.rcParams['axes.unicode_minus'] = False  # 解决坐标轴负号显示为方块的兼容性问题

# 明文给出在线教材数据根,便于理解绝对路径、目录与文件之间的层级关系
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}'  # 在读取前显式验证数据根目录
# 构造上市公司财务报表数据文件的完整路径
FINANCIAL_STMT_PATH = BOOK_DATA_DIR / 'stock/financial_statement.h5'  # 定位2023年财务结果的季度报表来源
# 构造上市公司基本信息数据文件的完整路径
STOCK_BASIC_PATH = BOOK_DATA_DIR / 'stock/stock_basic_data.h5'  # 定位地区与行业属性的公司主表来源
assert FINANCIAL_STMT_PATH.is_file(), f'缺少财务报表文件: {FINANCIAL_STMT_PATH};请检查 BOOK_DATA_DIR'  # 在读取前定位缺失的季度财务数据
assert STOCK_BASIC_PATH.is_file(), f'缺少公司基础信息文件: {STOCK_BASIC_PATH};请检查 BOOK_DATA_DIR'  # 在读取前定位缺失的公司属性数据

上面这段代码完成了三项基本配置:(1) 加载后续分析所需的 Python 库;(2) 将 matplotlib 的默认字体设置为“思源宋体”;(3) 用 Path('/home/ubuntu/r2_data_mount/data') 明文构造在线教材的数据根,并在读取前核对目录。/ 运算符继续把数据根、stock 子目录和文件名拼成完整路径;本地复现时只需替换这一字符串。

第二步:加载数据并筛选长三角地区上市公司

下面从本地 h5(HDF5)文件读取财务报表和公司基本信息两张表,按股票代码合并后筛选长三角地区(上海、江苏、浙江、安徽)公司。HDF5 在这里承担按季度和字段选择性读取的存储接口;是否比 CSV 更快取决于表结构、压缩、查询方式与硬件,不作为本例结论。

financial_data_2023 = pd.read_hdf(  # 在磁盘端下推年份和列筛选以控制内存
    FINANCIAL_STMT_PATH, where="quarter='2023q4'",  # 只读取2023年年报观测
    columns=['order_book_id', 'quarter', 'revenue', 'total_assets', 'net_profit']  # 只读取本例需要的列
).copy()  # 创建可安全派生变量的副本
stock_basic_info = pd.read_hdf(  # 基础信息表仅读取地区与行业字段
    STOCK_BASIC_PATH, columns=['order_book_id', 'province', 'industry_name', 'special_type']  # 避免载入无关列
).copy()  # 隔离后续列选择对共享基础信息表的影响

join_key = 'order_book_id'  # 定义两张表的合并键,order_book_id是每只股票的唯一标识符(如'600104.XSHG')

# 将财务数据与基本信息按照股票代码进行内连接合并,从而获取每家公司的名称、地区和行业分类
merged_company_data = pd.merge(  # 合并数据表
    financial_data_2023,  # 左表:2023年年报财务数据
    stock_basic_info[[join_key, 'province', 'industry_name']],  # 右表:仅选取需要的3列(province=省份, industry_name=行业名称)
    on=join_key,  # 按照股票代码进行匹配
    how='inner'  # 内连接:仅保留两张表中都存在的公司
)  # 形成财务结果与公司属性均可观测的交集样本

yrd_provinces_list = ['上海市', '江苏省', '浙江省', '安徽省']  # 定义长三角四省市的名称列表(province列使用全称)
# 从合并后的数据中筛选出注册地位于长三角地区的上市公司
yrd_companies_data = merged_company_data[  # 对合并后数据进行地区筛选
    merged_company_data['province'].isin(yrd_provinces_list)  # 保留省份名称在长三角列表中的行
].copy()  # 使用.copy()创建独立副本,避免SettingWithCopyWarning

这段代码的核心操作是表的合并pd.merge)。在金融数据分析中,公司的财务指标和行业分类信息通常存储在不同的数据表中。通过 order_book_id(股票代码)作为唯一键将它们连接起来,我们就能将”某公司的净利润是多少”与”该公司属于什么行业、位于哪个省份”这两类信息整合到同一行记录中,从而支持后续的分组分析。

第三步:数据清洗与对数变换

拿到原始数据后,不能直接拿来分析。金融数据中常见的问题包括:缺失值、零值(如新上市公司尚无营收)、以及极端的长尾分布(个别巨型企业的营收可能是中小企业的上万倍)。下面的代码完成了两项关键的预处理操作:剔除无效记录和对数变换。

# 构建数据有效性过滤条件:要求营业收入>100万、总资产>100万、且净利润为正
# 正净利润筛选只为了能对净利润取对数,它不是 ST 身份判定
is_valid_record = (
    (yrd_companies_data['revenue'] > 1e6) &  # 营业收入大于100万
    (yrd_companies_data['total_assets'] > 1e6) &  # 总资产大于100万
    (yrd_companies_data['net_profit'] > 0)  # 净利润为正
)  # 合并三项口径,避免对非正金额取对数

yrd_cleaned_data = yrd_companies_data[is_valid_record].copy()  # 仅保留满足全部三个条件的观测值

# 对数变换(Log-Transformation):将具有长尾分布的财务绝对金额转换为对数尺度
# 这样做的好处是:(1)压缩极端值的影响;(2)使变量分布更接近正态;(3)对数尺度下的线性关系对应原始尺度下的幂律关系
yrd_cleaned_data['log_revenue'] = np.log10(yrd_cleaned_data['revenue'])  # 对营业收入取以10为底的对数
yrd_cleaned_data['log_assets'] = np.log10(yrd_cleaned_data['total_assets'])  # 对总资产取以10为底的对数
yrd_cleaned_data['log_profit'] = np.log10(yrd_cleaned_data['net_profit'])  # 对净利润取以10为底的对数

# 统计每个行业的公司数量,并选取公司数目最多的前5个行业用于后续对比分析
top_industries_names = yrd_cleaned_data['industry_name'].value_counts().head(5).index  # 按行业名称统计公司数量,取前5大行业
# 过滤出仅属于前5大行业的公司子集,作为最终的绘图数据
plotting_data = yrd_cleaned_data[yrd_cleaned_data['industry_name'].isin(top_industries_names)]  # 保留行业名在前5大行业列表中的记录

为什么要做对数变换?在现实中,中国A股最大的上市公司营收可以达到数千亿元,而小型公司可能只有几千万元。取常用对数后,10亿元、100亿元、1000亿元分别变为9、10、11:原数每增加一个数量级,对数值增加1。

第四步:绘制散点图与箱线图

完成数据准备后,我们使用 matplotlib 创建一个包含三个子图(Subplot)的图形面板:左图展示净利润与营收的关系,中图展示净利润与资产的关系,右图通过箱线图对比不同行业的利润分布差异。

academic_colors_palette = ['#E3120B', '#2C3E50', '#008080', '#F0A700', '#8E9EAA']  # 固定行业箱线图颜色以保持类别辨识一致
fig, axes = plt.subplots(1, 3, figsize=(16, 5))  # 并排比较两项规模关联与行业条件分布

# ---- 左图:净利润 vs 营业收入散点图 ----
axes[0].scatter(plotting_data['log_revenue'], plotting_data['log_profit'], alpha=0.4, s=25, c='#2C3E50')  # 绘制散点,alpha=0.4使重叠点可见
poly_coeff = np.polyfit(plotting_data['log_revenue'], plotting_data['log_profit'], 1)  # 用一次多项式(即线性回归)拟合趋势线
axes[0].plot(plotting_data['log_revenue'], np.polyval(poly_coeff, plotting_data['log_revenue']), color='#E3120B', lw=2.5)  # 绘制红色趋势线
axes[0].set_xlabel('Log10 营业收入', fontsize=11)  # 明确横轴采用收入数量级而非原始金额
axes[0].set_ylabel('Log10 净利润', fontsize=11)  # 明确纵轴仅覆盖正净利润公司的对数值
axes[0].set_title('净利润与营业收入的样本关联', fontweight='bold')  # 用描述性措辞避免预断关系强度

# ---- 中图:净利润 vs 总资产散点图 ----
axes[1].scatter(plotting_data['log_assets'], plotting_data['log_profit'], alpha=0.4, s=25, c='#008080')  # 用青色绘制资产-利润散点
poly_coeff_assets = np.polyfit(plotting_data['log_assets'], plotting_data['log_profit'], 1)  # 对资产-利润关系进行线性拟合
axes[1].plot(plotting_data['log_assets'], np.polyval(poly_coeff_assets, plotting_data['log_assets']), color='#E3120B', lw=2.5)  # 绘制红色趋势线
axes[1].set_xlabel('Log10 总资产', fontsize=11)  # 标明资产规模已作对数变换
axes[1].set_title('净利润与总资产的样本关联', fontweight='bold')  # 避免把描述性散点解释为稳定增长规律

# ---- 右图:不同行业的利润分布箱线图 ----
sns.boxplot(x='industry_name', y='log_profit', data=plotting_data, ax=axes[2], palette=academic_colors_palette)  # 按行业名称分组绘制箱线图
axes[2].set_xlabel('细分行业', fontsize=11)  # 标明箱线分组来自公司行业字段
axes[2].set_title('不同行业净利润的样本分布差异', fontweight='bold')  # 未执行显著性检验
plt.xticks(rotation=30)  # 将x轴行业标签旋转30度以避免重叠

for ax in axes:  # 遍历三个子图
    ax.grid(True, linestyle='--', alpha=0.5)  # 为每个子图添加虚线网格,增强可读性

plt.tight_layout()  # 自动调整子图间距,防止标签重叠
plt.show()  # 输出三面板财务关联图供样本口径解读
/tmp/ipykernel_1399346/1106275207.py:20: FutureWarning: 

Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.

  sns.boxplot(x='industry_name', y='log_profit', data=plotting_data, ax=axes[2], palette=academic_colors_palette)  # 按行业名称分组绘制箱线图
三联图:净利润分别对营业收入和总资产的对数散点,以及前五大行业净利润箱线分布。
图 1.2: 长三角上市公司财务数据:净利润与营收、资产、行业的关系 (2023年年报数据)

图 1.2 只提供描述性证据。前两图显示对数营收、对数资产与对数净利润的样本正相关;要判断哪个变量预测力更强,还需在同一时间外测试集比较损失。右图展示的是净利润水平,而不是利润率;未做组间检验,也不应称为“显著差异”。

1.5 股票市场数据 (Stock Market Data)

在统计学习中,预测连续型或定量输出值(如净利润)的过程通常被称为回归问题 (Regression Problem)。然而,在许多商业决策场景中,我们更关心定性或离散型的输出,这被称为分类问题 (Classification Problem)。

例如,在 章节 4 章中,我们研究一个具有代表性的股票市场数据集。对于交易员或基金经理而言,一个核心的分类任务是预测指数或个股在给定日期会上涨还是下跌。

考虑注册地位于长三角的上市汽车企业——上汽集团(600104.XSHG)。我们使用其历史日度收益率数据:当日收益率为正时标记为“上涨”,否则标记为“下跌”,并以过去几日的滞后收益作为候选特征。

图 1.3 比较历史滞后收益率在当日上涨组和下跌组中的样本分布。箱线图若高度重叠,只能说明该样本中单个滞后收益的组间区分有限;它本身不能检验市场效率。

下面我们将通过代码分步演示如何加载上汽集团的历史日频价格数据,计算每日收益率,并利用特征工程中经常使用的 shift 函数构建”滞后收益率”作为预测特征。

第一步:加载上汽集团股价数据并计算收益率

我们首先从本地前复权价格文件中读取上汽集团的全部历史行情,然后基于收盘价计算每日百分比收益率(pct_change() 方法的含义是 \((P_t - P_{t-1})/P_{t-1}\))。

PRICE_DATA_PATH = BOOK_DATA_DIR / 'stock/stock_price_pre_adjusted.h5'  # 定位构造滞后收益的前复权日行情
assert PRICE_DATA_PATH.is_file(), f'缺少前复权行情文件: {PRICE_DATA_PATH};请检查 BOOK_DATA_DIR'  # 在HDF查询前报告可直接核对的输入缺口

saic_motor_data = pd.read_hdf(  # 从本地h5文件中按股票代码选择性读取上汽集团的日度前复权数据,避免全量载入全市场行情
  PRICE_DATA_PATH,  # 使用已通过存在性检查的行情输入
  where="order_book_id='600104.XSHG'",  # 仅保留股票代码等于上汽集团的记录
  columns=['date', 'order_book_id', 'close']  # 只读取后续分析真正需要的日期、股票代码和收盘价三列
).reset_index()  # 将返回结果的MultiIndex还原为普通列,便于后续排序与特征工程

# 对选择性读取后的上汽集团数据创建独立副本,避免后续链式赋值影响原始对象
saic_motor_data = saic_motor_data.copy()
saic_motor_data['date'] = pd.to_datetime(saic_motor_data['date'])  # 将交易日期转为可排序的时间标尺
saic_motor_data = saic_motor_data.sort_values('date')  # 按日期升序排列,确保时间序列的前后顺序正确
# 计算每日简单收益率:(今日收盘价 - 昨日收盘价) / 昨日收盘价
saic_motor_data['daily_return'] = saic_motor_data['close'].pct_change()  # 以相邻收盘价比值定义一日简单收益

上面的代码完成了数据的加载和收益率计算。pct_change()pandas 时间序列分析中最常用的方法之一,它自动计算相邻两个时间点之间的变化百分比。对于股价时间序列而言,这正好就是每日投资回报率。

第二步:构建滞后特征并定义分类标签

接下来,我们利用 shift() 函数创建过去 1 到 5 天的”回溯”收益率作为预测特征,并将当日收益率的正负作为二分类标签。shift(lag) 的作用是把整列数据向下移动 lag 行——这意味着第 \(t\) 行的 lag_return_1 值实际上是第 \(t-1\) 天的收益率。

num_lags = 5  # 定义需要构建的滞后特征数量为5(即使用过去5天的收益率信息)

for lag in range(1, num_lags + 1):  # 循环创建lag_return_1到lag_return_5共5列
    # shift(lag)将daily_return列向下平移lag行,实现"回溯"效果
    saic_motor_data[f'lag_return_{lag}'] = saic_motor_data['daily_return'].shift(lag)  # 将收益率向下平移lag行,生成第lag天前的历史收益率

# 定义二分类目标标签:当日收益率>0标记为1(上涨),否则标记为0(下跌),并转换为整数类型
saic_motor_data['market_direction'] = (saic_motor_data['daily_return'] > 0).astype(int)

# 仅保留2015年以后的数据,并删除因shift操作产生的前5行缺失值
saic_analysis_dataset = saic_motor_data[saic_motor_data['date'] >= '2015-01-01'].dropna()  # 筛选2015年后数据并剔除缺失行

通过这段特征工程代码,我们将一维的收益率时间序列”展开”成了一个包含5个特征的数据表。对于第 \(t\) 天的记录,其特征向量为 \((r_{t-1}, r_{t-2}, r_{t-3}, r_{t-4}, r_{t-5})\),标签为 \(y_t \in \{0, 1\}\)。这是将时间序列预测问题转化为标准分类问题的经典手法,也是量化投资中最基础的建模范式之一。

第三步:绘制滞后收益率与涨跌方向的箱线图

最后我们将数据按照”上涨日”和”下跌日”分组,分别绘制不同滞后天数下的收益率分布箱线图,从而直观展示历史收益率对未来涨跌的区分能力。

fig, axes = plt.subplots(1, 3, figsize=(15, 5))  # 在统一尺度下并排比较三个滞后期的条件分布
lag_indices = [1, 2, 3]  # 选择前3个滞后项进行可视化展示

for i, lag_idx in enumerate(lag_indices):  # 对每个滞后项依次绘制箱线图
    sns.boxplot(  # 使用seaborn绑制分组箱线图,按涨跌方向分组展示滞后收益率分布
        x='market_direction',  # x轴为今日涨跌方向(0或1)
        y=f'lag_return_{lag_idx}',  # y轴为对应滞后天数的历史收益率
        data=saic_analysis_dataset,  # 使用前面构建的分析数据集
        ax=axes[i],  # 指定绑制到第i个子图上
        palette=['#E3120B', '#008080'],  # 下跌日用红色,上涨日用青色
        showfliers=False  # 不显示离群点,使箱体形状更清晰
    )  # 完成单个滞后项的箱线图绑制
    axes[i].set_xticklabels(['下跌日', '上涨日'])  # 将数字标签替换为中文描述
    axes[i].set_title(f'滞后项 Lag {lag_idx} 收益分布', fontweight='bold')  # 设置子图标题标明滞后阶数
    axes[i].set_xlabel('今日方向')  # 标明箱线分组是当日涨跌标签
    axes[i].set_ylabel('过去收益率')  # 标明纵轴是预测日前的历史信息
    axes[i].grid(True, axis='y', alpha=0.3)  # 仅在y轴方向添加淡色网格线

plt.tight_layout()  # 自动调整子图间距
plt.show()  # 输出监督学习与无监督学习任务边界图
/tmp/ipykernel_1399346/2765537835.py:5: FutureWarning: 

Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.

  sns.boxplot(  # 使用seaborn绑制分组箱线图,按涨跌方向分组展示滞后收益率分布
/tmp/ipykernel_1399346/2765537835.py:13: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
  axes[i].set_xticklabels(['下跌日', '上涨日'])  # 将数字标签替换为中文描述
/tmp/ipykernel_1399346/2765537835.py:5: FutureWarning: 

Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.

  sns.boxplot(  # 使用seaborn绑制分组箱线图,按涨跌方向分组展示滞后收益率分布
/tmp/ipykernel_1399346/2765537835.py:13: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
  axes[i].set_xticklabels(['下跌日', '上涨日'])  # 将数字标签替换为中文描述
/tmp/ipykernel_1399346/2765537835.py:5: FutureWarning: 

Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.

  sns.boxplot(  # 使用seaborn绑制分组箱线图,按涨跌方向分组展示滞后收益率分布
/tmp/ipykernel_1399346/2765537835.py:13: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
  axes[i].set_xticklabels(['下跌日', '上涨日'])  # 将数字标签替换为中文描述
三幅箱线图比较上汽集团今日涨跌两类下前 1、2、3 日收益率的位置与离散程度。
图 1.3: 股票市场数据: 上汽集团 (600104.SH) 历史收益率滞后分析 (2015-2025)

图 1.3 中两类箱体的位置与尺度在视觉上接近,只说明这些单日滞后收益在本样本中的边际区分度看起来较弱。箱线图不提供准确率、交易收益或弱式有效市场检验;这些问题需要事前定义基线、多个前向窗口与扣费后评价。

这些滞后项把任务写成了完整的监督学习三元组:样本单位是上汽集团的公司—交易日 \(t\),特征是 \(t\) 日之前的五个收益率,标签是 \(t\) 日涨跌。这里的目标是建立任务身份、信息时点和朴素比较规则,而不是在导论中提前竞赛尚未讲授的分类器。

第四步:只用早期训练段锁定多数类基线

金融数据必须保留时间顺序。下面把最早 75% 观测定义为训练段,并只用其中的标签决定常数分类规则;后 25% 暂不读取标签,也不在本章产生任何模型排名。训练期多数类比例只是“总猜同一类”在拟合样本上的透明参照,不是样本外成绩。

feature_columns = [f'lag_return_{lag}' for lag in range(1, 6)]  # 固定预测日前五个交易日的收益特征
training_end_index = int(0.75 * len(saic_analysis_dataset))  # 按日期位置锁定最早四分之三为训练段
saic_training_sample = saic_analysis_dataset.iloc[:training_end_index].copy()  # 只开放早期观测供任务核对与基线定义
saic_future_sample = saic_analysis_dataset.iloc[training_end_index:].drop(columns=['market_direction']).copy()  # 隐去后期标签以防导论阶段偷看
training_majority_class = int(saic_training_sample['market_direction'].mode().iloc[0])  # 仅由训练标签锁定常数预测类别
training_majority_rate = float((saic_training_sample['market_direction'] == training_majority_class).mean())  # 报告训练期常数规则的参照正确率
print({'features': feature_columns, 'training_rows': len(saic_training_sample), 'future_rows': len(saic_future_sample), 'training_majority_class': training_majority_class, 'training_majority_rate': training_majority_rate})  # 留存时点、分母和训练期基线证据
{'features': ['lag_return_1', 'lag_return_2', 'lag_return_3', 'lag_return_4', 'lag_return_5'], 'training_rows': 2005, 'future_rows': 669, 'training_majority_class': 0, 'training_majority_rate': 0.5221945137157107}

后续 章节 4 将在合法概率、损失与分类指标讲清后比较逻辑回归、LDA、QDA 等方法;章节 5 再说明如何只用训练期内部的验证或前向交叉验证选择模型。到那时,锁定的训练期多数类才会与未参与选择的未来期结果共同出现。当前箱线图仍只是描述性图形,不能据此检验弱式有效市场假说或宣称可获得扣费后超额收益。

关于股市预测的重要警告与理论约束

在尝试应用统计学习进行股市预测时,必须警惕以下核心风险:

  1. 弱式有效市场 (Weak-form EMH):若过去的价格信息已反映在当前价格中,仅凭历史收益率稳定获得扣费后超额收益会很困难;这是一项需要样本外证据检验的市场假说,不是由一次分类准确率直接推出的结论。
  2. 数据窥探 (Data Snooping): 当研究者反复测试大量特征和模型组合时,部分模型会因随机巧合而在已查看样本中表现较好;应以未参与选择的时期和多重性控制约束这种风险。
  3. 结构突变 (Structural Break): 金融市场的底层规律可能随着制度、宏观环境或参与者行为的改变而发生突变,导致历史训练的模型完全失效。

1.6 量化多因子模型与高维问题 (Quantitative Multi-Factor Models and High-Dimensionality)

在现代商业和金融分析中,我们越来越多地面临所谓的“高维数据”挑战。在此类情境中,\(p\)(预测变量或特征的数量)可能接近甚至远远大于 \(n\)(观测样本的数量)。

以多因子研究为例,候选特征可能同时包括财务比率、滞后价量指标与文本特征,因而出现特征数 \(p\) 接近甚至超过有效样本数 \(n\) 的情形。此时最小二乘可能不唯一或方差很高,模型比较必须在训练期内部完成降维或正则化。

数学视角:维数灾难 (The Curse of Dimensionality)

当含截距设计矩阵有 \(p+1>n\) 列时,其列秩至多为 \(n\),所以 \(\mathbf{X}^T\mathbf{X}\) 不满秩,未加约束的 OLS 系数不再唯一。不过,\(p>n\) 本身既不保证任意学习器能插值训练样本,也不推出样本外预测能力接近零:能否插值取决于模型容量、设计矩阵与响应,泛化则还取决于数据生成机制、信号强度和分布漂移。正则化或降维通过附加结构选择稳定解,但仍须用未参与拟合的前向验证检验预测力。

为了控制高维估计的方差并处理秩不足,本书在 章节 6 中讨论正则化 (Regularization) 和降维。例如:

  • Lasso 回归:它在拟合模型的同时收缩系数,并可能把部分系数压缩为零,从而形成较稀疏的特征集合;变量是否稳定入选仍需在前向验证中检查。
  • 岭回归 (Ridge):通过 \(\ell_2\) 惩罚收缩系数,在高度相关特征下通常能改善估计的数值稳定性并降低方差,但会引入偏差;是否改善样本外误差取决于惩罚强度、数据机制与训练期验证结果。

随着算力提升和数据类型扩展,高维统计学习成为可行的候选分析框架。理解如何在 \(p \gg n\) 时明确估计目标、控制选择偏差并用前向样本评价,是高阶商业数据分析的核心素养;具体机构是否采用某种方法,需要可核查的公开证据,不能由方法流行度推断。

1.7 机器学习时代的理论融合 (Theoretical Integration in the Era of Machine Learning)

在现代数据科学形成统一实践框架之前,统计学和古典人工智能长期沿不同路径发展。经典统计文献重点研究估计量的无偏性、一致性与推断条件,早期计算机科学则更多利用规则引擎或启发式搜索解决特定任务。

近几十年,统计学与机器学习在高维、非线性数据分析中逐渐交汇。一方面,最大似然、贝叶斯决策与误差控制为模型估计和不确定性分析提供基础;另一方面,计算资源和优化算法的发展扩大了随机森林、梯度提升树与深度网络等非线性模型的可计算范围。模型复杂度增加并不保证样本外误差下降,仍需通过适当切分、基线与损失函数比较来判断。

在数字信贷、资本市场和平台经营中,研究者可以把传统统计模型、树模型或神经网络作为候选。复杂模型是否值得采用,取决于数据量、标签质量、样本外增量、解释成本和业务损失,而不是模型名称的新旧。

统计学习与机器学习、人工智能的关系与定位

对于商学院学生而言,在面对铺天盖地的技术名词时保持清晰的认知尤为重要。这三个词语的内涵具有嵌套或交叉的属性:

首先,人工智能 (Artificial Intelligence) 是一个拥有宏大愿景的学科大类,目标是打造能够模拟人类认知和决策的系统;而机器学习 (Machine Learning) 是当前实现这一目标最具生命力的技术路径,它侧重于让计算机自己从数据中”学习”规律而非依靠人类硬编码规则。

统计学习 (Statistical Learning) 不只比较预测损失,也研究估计的不确定性、模型假设、变量关联和结论边界。本书从这一视角说明算法,使读者能够判断复杂模型相对简单基线是否提供了可重复的证据。

1.8 本书概览 (This Book)

本书提供了对统计学习领域广泛而深入的介绍。我们的目标是解释统计学习方法何时以及为何有用,并举例说明这些方法如何应用于实际数据分析。我们专注于理解统计学习的概念和基础,而不是深入探讨每个领域的理论发展。

本书的组织结构如下:

  • 章节 1 章(统计学习导论):介绍监督学习和无监督学习的区别,并通过实例说明统计学习的应用领域
  • 章节 2 章(什么是统计学习?):形式化地介绍统计学习理论,定义损失函数、偏差-方差权衡等核心概念
  • 章节 3 章(线性回归):详细介绍简单线性回归和多元线性回归,包括最小二乘估计、假设检验、置信区间等
  • 章节 4 章(分类):讨论逻辑回归、线性判别分析、二次判别分析、K近邻等分类方法
  • 章节 5 章(重采样方法):介绍交叉验证和Bootstrap方法,用于模型评估和选择
  • 章节 6 章(线性模型选择与正则化):讨论子集选择、岭回归、Lasso、主成分分析等方法
  • 章节 7 章(超越线性):介绍多项式回归、样条光滑、广义加性模型等非线性方法
  • 章节 8 章(基于树的方法):讨论决策树、Bagging、随机森林、Boosting等方法
  • 章节 9 章(支持向量机):详细介绍最大间隔分类器、支持向量机、支持向量回归等
  • 章节 10 章(深度学习):介绍神经网络、反向传播、卷积神经网络、循环神经网络等
  • 章节 11 章(生存分析与删失数据):讨论Kaplan-Meier估计、对数秩检验、Cox比例风险模型等
  • 章节 12 章(无监督学习):介绍主成分分析、聚类分析(K-means、层次聚类)等方法
  • 章节 13 章(多重检验):讨论多重检验问题、错误发现率(FDR)控制等

本书使用的工具和数据集

本书使用Python作为主要的编程语言,主要使用以下工具包:

  • NumPy:数值计算基础库
  • Pandas:数据操作和分析
  • MatplotlibSeaborn:数据可视化
  • Scikit-learn:机器学习算法库
  • Statsmodels:统计建模和计量经济学
  • PyTorch:深度学习框架

本书案例使用中国本土市场数据;在线教材把数据根明文定义为 /home/ubuntu/r2_data_mount/data,并以 BOOK_DATA_DIR 变量在各章复用。主要数据源包括:

  1. 上市公司基本信息 (stock/stock_basic_data.h5):股票代码、名称、行业分类、上市时间、所在地区等。
  2. 上市公司财务报表 (stock/financial_statement.h5):2005-2025年A股全部上市公司的季度财务数据,涵盖资产负债表、利润表和现金流量表核心指标。
  3. 股票日度行情数据 (stock/stock_price_pre_adjusted.h5 等):2005-2025年A股日度OHLC行情,提供前复权、后复权和不复权三个版本。
  4. 估值因子数据 (stock/valuation_factors_quarterly_15_years.h5):2011-2025年A股全部上市公司的季度估值因子指标。
  5. 指数、期货与基金数据 (index/future/fund/ 文件夹):各类市场指数、期货合约和基金净值数据。
  6. 中国地理数据 (map/ 文件夹):省、市、县级GeoJSON地图数据,用于地理可视化。

1.9 谁应该阅读本书? (Who Should Read This Book?)

本书是为那些希望在理论和实践之间建立桥梁的读者设计的。具体而言,本书适合以下读者:

  • 高年级本科生:希望系统学习统计学习理论基础和实际应用的学生。我们假设读者具备微积分、线性代数、概率论和数理统计的基本知识
  • 研究生:需要将统计学习方法应用于各自研究领域的学生,包括统计学、计算机科学、经济学、工程学、生物信息学等
  • 数据分析师和科学家:希望深入了解所用方法的理论基础,并能够根据具体问题选择合适方法的专业人士
  • 对人工智能和机器学习感兴趣的从业者:希望理解机器学习算法背后的统计原理,而不仅仅是调用API包的用户

本书既适合课堂教学,也适合自学。每章都包含丰富的图表、实例和习题,帮助读者理解和掌握统计学习的核心概念和方法。

先修知识要求

为了充分理解本书内容,读者应具备以下数学基础:

  1. 数学基础
    • 微积分:导数、偏导数、梯度、积分
    • 线性代数:矩阵运算、特征值和特征向量、矩阵分解
    • 概率论:随机变量、概率分布、期望、方差、协方差
    • 数理统计:估计理论、假设检验、置信区间
  2. 编程能力
    • 本书主要读者应已具备 Python、pandas、NumPy 与 Matplotlib 的入门能力,能够排序表格、构造滞后变量并绘制带标题和坐标轴的图。
    • 逐行中文注释用于解释统计与业务意图,不替代 Python 入门课程。若尚不能完成 index.qmd 的四项开始前诊断,应先补修 Python 基础,再进入第 2 章。
    • 建议补修后重新独立完成“按公司和日期排序并构造一期滞后”以及“选择性读取 HDF 子集并绘图”两项;两项均通过后再继续核心路线。
  3. 补充资源: 对于数学基础较弱的读者,推荐阅读Gilbert Strang的《Introduction to Linear Algebra》,以及Deisenroth等人编写的《Mathematics for Machine Learning》。

1.10 符号与记号 (Notation and Symbols)

本书采用以下标准记号:

  • 标量:使用小写字母,如\(x, y, z\)
  • 向量:使用粗体小写字母,如\(\mathbf{x}, \mathbf{y}, \mathbf{z}\)
  • 矩阵:使用粗体大写字母,如\(\mathbf{X}, \mathbf{Y}, \mathbf{Z}\)
  • 随机变量:使用大写字母,如\(X, Y, Z\)
  • 估计量:在参数上方添加帽子,如\(\hat{\beta}, \hat{y}, \hat{\sigma}^2\)
  • 转置:使用上标\(^T\),如\(\mathbf{A}^T\)
  • 逆矩阵:使用上标\(^{-1}\),如\(\mathbf{A}^{-1}\)
  • 期望:使用\(E[\cdot]\)
  • 方差:使用\(\text{Var}(\cdot)\)
  • 协方差:使用\(\text{Cov}(\cdot, \cdot)\)

在回归问题中:

  • \(n\):观测数量(样本量)
  • \(p\):预测变量(特征)数量
  • \(\mathbf{x}_i\):第\(i\)个观测的预测变量向量,\(p \times 1\)
  • \(y_i\):第\(i\)个观测的响应变量(标量)
  • \(\mathbf{X}\):所有观测的预测变量矩阵,\(n \times p\)
  • \(\mathbf{y}\):所有观测的响应变量向量,\(n \times 1\)

在分类问题中:

  • \(K\):类别数量
  • \(G_k\):第\(k\)个类别
  • \(p_k(x) = P(Y=k | X=x)\):给定\(X=x\)时属于第\(k\)类的条件概率

关于数学符号的说明

本书尽量使用标准的统计学习符号系统,但也注意到不同文献可能采用不同的约定。读者在阅读其他资料时可能会遇到符号差异,例如:

  • 有些文献使用\(\beta\)表示回归系数,有些使用\(\theta\)\(w\)
  • 有些文献使用\(\mathbf{x}\)表示列向量,有些表示行向量
  • 有些文献用\(n\)表示样本量,有些用\(N\)\(m\)

关键是要理解每个符号在具体上下文中的含义,而不是死记硬背。本书在每次引入新符号时都会明确定义其含义和维度。

1.11 数据集说明 (Data Sets Used in This Book)

本书的核心实证案例优先使用本地中国商业与金融数据。个别用于解释抽样分布、已知真值或算法边界的例子会明确标为“机制实验”;它们不能被解释为真实市场证据。数据许可、路径或运行环境不可用时,正文会标明“未执行”,不会用模拟结果替代实证结论。

本地数据路径(在线环境明文给出,不依赖操作系统猜测):

  • 在线教材:BOOK_DATA_DIR = Path('/home/ubuntu/r2_data_mount/data').resolve()
  • 本地复现:把引号中的绝对路径替换为本机直接包含 stock/ 等子目录的数据根。

读者可以从教材的数据网站获取可再分发的数据,将相对路径接在 https://assets.qiufei.site/data/ 后即可。例如,stock/stock_basic_data.h5 的下载地址是 https://assets.qiufei.site/data/stock/stock_basic_data.h5。使用前还应核对文件许可、观察期、字段口径与教材版本;链接可访问不等于所有外部数据都可再分发。

核心数据文件一览

数据文件 说明 典型应用场景
stock/stock_basic_data.h5 上市公司基本信息(代码、名称、行业、地区等) 公司筛选、行业分析
stock/financial_statement.h5 2005-2025年季度财务报表数据 财务分析、面板数据建模
stock/stock_price_pre_adjusted.h5 2005-2025年日度前复权行情 收益率计算、技术分析
stock/stock_price_post_adjusted.h5 2005-2025年日度后复权行情 长期投资收益率分析
stock/valuation_factors_quarterly_15_years.h5 2011-2025年季度估值因子 多因子模型、价值投资
index/ 文件夹 各类市场指数数据 市场基准分析
map/ 文件夹 省市县级GeoJSON地图 地理可视化

Python依赖安装(基于python 3.10):

pip install numpy pandas matplotlib seaborn scikit-learn statsmodels torch

所有在本书中使用的数据集都会在首次使用时详细说明其来源、结构和含义。我们优先使用:

  • 本地存储的真实数据:确保所有读者都可以离线复现
  • 可追溯的数据版本:使用固定时间截面的数据快照
  • 中国本土上市公司数据:优先选择长三角地区企业作为案例

1.12 本章小结 (Chapter Summary)

本章建立了全书的基本语言:监督学习使用已观测响应学习预测规则,无监督学习在没有目标标签时探索结构。平方损失下,\(f(X)=E(Y\mid X)\)\(E(\epsilon\mid X)=0\);无条件零均值不足以替代这一条件。

统计学习任务必须同时声明样本单位、特征可得时点、标签窗口、损失与基线。基线和模型选择只能使用训练信息,测试标签只用于最终评价。单个图形、个股或一次切分只支持样本特定的描述,不能独立识别因果、市场机制或稳定交易价值。

1.13 理论来源与前沿

统计学习既继承了数理统计中关于估计与推断的传统,也吸收了计算机科学中关于算法与泛化的思想。回归与分类等基本任务可以追溯到最小二乘与极大似然;而现代意义上的‘学习’强调在有限样本下控制泛化误差,并在偏差-方差权衡、正则化与模型选择等框架下形成一套统一语言。全书的基本术语、监督/无监督任务边界和模型评价框架以统计学习的标准教材体系为起点 (James 等 2023年; Hastie 等 2009年)

近十年来,更大规模的数据与算力推动了深度学习方法的应用;与此同时,现实部署也更加重视可解释性、公平性与稳健性。在中国的商业场景中,常见挑战包括行业结构变化带来的分布漂移、监管对模型解释的要求,以及财务、交易、文本和图像等多源数据的融合。

1.14 练习

1.14.1 概念题

  1. [核心|难度:1|分值:10|任务:概念] 基本定义:解释什么是统计学习。用一句话分别概括“监督学习”与“无监督学习”的核心目标。

  2. [核心|难度:2|分值:15|任务:应用] 可解释性与准确度:本书强调“预测准确度”与“模型可解释性”之间的权衡。请给出一个中国商业场景(如银行信贷审批),说明两者冲突的具体表现。

  3. [拓展|难度:1|分值:10|任务:概念] 研究可复现性:为什么在学术研究和企业分析中强调“可复现性”?请列出实现可复现所需的至少三个要素。

  4. [核心|难度:2|分值:15|任务:推导] 数学符号理解:在 式 1.1 中,\(f\)\(\epsilon\) 分别代表什么?为什么平方损失下的关键条件是 \(E(\epsilon\mid X)=0\)

1.14.2 应用题

  1. [核心|难度:2|分值:25|任务:数据分析] 本地行情探索:利用你本机的 A 股数据,选择一家长三角地区的上市公司,完成以下任务:
    • 使用 Python 读取其 2015-2025 年的后复权收盘价数据。
    • 绘制股价走势图。
    • 计算并报告日收益率的峰度 (Kurtosis) 和偏度 (Skewness)。
    • 思考题:如果收益率分布表现出明显的“肥尾”特征,这对传统的统计假设(如正态分布)意味着什么?
  2. [核心|难度:2|分值:15|任务:应用] 业务场景建模:某电商平台希望预测哪些用户在未来一个月内会流失。
    • 这是一个监督学习还是无监督学习问题?
    • 这是一个回归问题还是分类问题?
    • 列出至少三个可能有助于预测流失的特征(Features)。

1.14.3 理论题

  1. [拓展|难度:2|分值:15|任务:推导] 损失函数分析:在回归问题中,我们通常最小化平方损失 (Squared Loss) \((y - \hat{y})^2\)。请给出一个该损失函数可能不适用的场景,并建议一个替代方案。

  2. [补救|难度:1|分值:5|任务:概念] 泛化误差:一句话解释什么是“泛化误差” (Generalization Error),以及为什么我们不能直接使用训练集误差来估计它。

1.15 练习参考解答

展开第 1 章完整解答与评分键

统一评分与验收说明:各题按元数据分值计分;概念题的定义、依据和结论边界各占可分配要点,代码题则按数据输入、时点/清洗、输出和解释分项。浮点结果与参考运行相对误差在 \(10^{-6}\) 内可接受;随机过程必须固定题定种子,图形验收看轴、单位、口径与时段,不要求像素级一致。常见失败包括用测试标签定基线、混淆 \(E(\epsilon)\)\(E(\epsilon\mid X)\)、路径/字段口径不一致,以及把图形关联写成因果或 EMH 检验。

1.15.1 概念题解答

  1. 统计学习定义:统计学习是一套用于建模和理解复杂数据集的数学框架。监督学习的目标是学习输入与输出之间的映射以进行预测;无监督学习的目标是发现数据内部的隐含结构。

  2. 权衡示例:在银行信贷审批中,使用深度神经网络可能实现极高的违约预测准确度。但根据监管要求,银行必须能向被拒贷客户解释原因(如“负债率过高”)。高准确度的黑盒模型往往缺乏这种可解释性,导致业务无法合规。

  3. 可复现性:可复现性确保了研究结论的客观性和运行过程的可检查性。要素包括:(1) 数据版本控制;(2) 完整的源代码;(3) 统一使用 peter 环境,并用 requirements.txt 登记代码直接依赖的库。

  4. 数学符号\(f\) 代表给定特征后的系统部分;\(\epsilon=Y-f(X)\) 代表在给定 \(X\) 后仍不可由 \(f\) 预测的随机误差,而不是估计量的“偏差”。在平方损失下取 \(f(X)=E(Y\mid X)\) 时,关键条件是 \(E(\epsilon\mid X)=0\),它由重期望定律推出 \(E(\epsilon)=0\)。反向不成立:若 \(X\) 已中心化且 \(\epsilon=X\),则 \(E(\epsilon)=0\),但 \(E(\epsilon\mid X)=X\ne0\)

1.15.2 应用题解答

  1. 上汽集团行情分析代码实现

下面的参考代码用 pandas 读取行情数据,转换日期并按时间升序排列。随后以相邻收盘价的对数差计算对数收益率,再用 .skew().kurt() 描述当前样本的偏度与峰度。这两个样本统计量可用于检查正态模型是否与数据的非对称性和尾部形态相符,但不能单独确定总体分布。

import pandas as pd  # 读取并整理公司—交易日行情
import numpy as np  # 计算对数收益与样本分布统计量
import matplotlib.pyplot as plt  # 绘制上汽集团后复权价格与收益分布
import os  # 读取全书统一的数据根环境变量
from pathlib import Path  # 用路径对象解析教材数据目录

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()  # 把用户路径解析为绝对数据根
assert BOOK_DATA_DIR.is_dir(), f'BOOK_DATA_DIR 不是有效目录: {BOOK_DATA_DIR}'  # 阻止从错误根目录继续读取
price_file_path = BOOK_DATA_DIR / 'stock/stock_price_post_adjusted.h5'  # 构造后复权行情文件路径
assert price_file_path.is_file(), f'缺少后复权行情文件: {price_file_path};请检查 BOOK_DATA_DIR'  # 在HDF读取前标准确切缺口

saic_stock_data = pd.read_hdf(  # 从本地h5文件中按股票代码选择性读取上汽集团的后复权股价数据,避免一次性载入全市场行情
  price_file_path,  # 指定后复权股价数据文件路径
  where="order_book_id='600104.XSHG'",  # 仅保留股票代码等于上汽集团的记录
  columns=['date', 'order_book_id', 'close']  # 只读取分析所需的日期、股票代码和收盘价列
).reset_index()  # 将MultiIndex重置为普通列,方便后续进行日期排序与收益率计算

# 对选择性读取后的上汽集团数据创建独立副本,保证后续新增列操作安全
saic_stock_data = saic_stock_data.copy()
saic_stock_data['date'] = pd.to_datetime(saic_stock_data['date'])  # 将日期列转换为标准datetime格式
saic_stock_data = saic_stock_data.sort_values('date')  # 按照交易日期升序排列
saic_stock_data = saic_stock_data[saic_stock_data['date'].between('2015-01-01', '2025-12-31')].copy()  # 严格落实题目指定时段

# 使用对数差分法计算日对数收益率:ln(P_t / P_{t-1})
saic_stock_data['daily_log_return'] = np.log(saic_stock_data['close'] / saic_stock_data['close'].shift(1))
cleaned_return_series = saic_stock_data['daily_log_return'].dropna()  # 删除首行因shift产生的缺失值
# 汇总同一清洗后收益序列,保持数据读取与结果报告分块可核对

skewness_val = cleaned_return_series.skew()  # 计算收益率分布的偏度(衡量分布的不对称程度)
kurtosis_val = cleaned_return_series.kurt()  # 计算收益率分布的峰度(衡量尾部厚度,正态分布的超额峰度为0)

print(f'上汽集团收益率偏度: {skewness_val:.4f}')  # 输出偏度值,负值表示左偏(下跌尾部更长)
print(f'上汽集团收益率峰度: {kurtosis_val:.4f}')  # 输出峰度值,正值表示肥尾(极端事件更频繁)
上汽集团收益率偏度: 0.0983
上汽集团收益率峰度: 4.6807
fig, price_axis = plt.subplots(figsize=(11, 5))  # 创建可独立阅读的习题结果图
price_axis.plot(saic_stock_data['date'], saic_stock_data['close'], color='#2C3E50', linewidth=1.2)  # 绘制指定时段后复权价格
price_axis.set_title('上汽集团后复权收盘价(2015—2025)')  # 标明公司、口径与时段
price_axis.set_xlabel('交易日期')  # 标明时间轴
price_axis.set_ylabel('后复权收盘价(元)')  # 标明价格口径和单位
price_axis.grid(True, linestyle=':', alpha=0.4)  # 提高时间趋势读取性
plt.tight_layout()  # 避免轴标签被裁切
plt.show()  # 输出题目要求的完整图形
上汽集团 2015 至 2025 年后复权收盘价随交易日期变化的单条时间序列曲线。
图 1.4: 上汽集团2015—2025年后复权收盘价(数据来源:本地A股日度行情库)

图 1.4 是本题必须提交的价格路径证据;偏度和峰度数值必须来自与该图完全相同的公司、复权口径和日期样本。

思考题解答:肥尾特征意味着极端收益发生的概率远高于正态分布的预测。这意味着基于正态假设的风险模型(如传统的 VaR)可能会严重低估金融危机期间的潜在损失。

  1. 用户流失预测
    • 监督学习。
    • 分类问题(二分类:流失 vs 留存)。
    • 特征:过去 30 天登录频率、平均客单价、最近一次消费距今时长。

1.15.3 理论题解答

  1. 损失函数:当数据中存在大量离群值(Outliers)时,平方损失会由于对大误差的二阶惩罚而过度向离群值偏移。替代方案是使用绝对损失 (L1 Loss),即 \(|y - \hat{y}|\),它对离群值更稳健。

  2. 泛化误差:泛化误差是模型在先前未见过的全新数据上的预期误差。训练误差通常会由于模型对噪声的过拟合而偏低,因此不能作为真实预测性能的可靠指标。

1.16 章末回顾

1.16.1 学习自检

请先遮住答案,逐项给自己标记“已达成”“需补修”或“需重测”:

  1. 能否用一个中国商业决策分别说明监督学习、无监督学习、回归与分类,而不只背诵定义?
  2. 能否写出 \(Y=f(X)+\epsilon\),并解释平方损失下 \(f(X)=E(Y\mid X)\)\(E(\epsilon\mid X)=0\) 的关系?
  3. 能否把预测时点、特征可得日、标签窗口和评价日画在同一时间线上?
  4. 能否说明训练、验证、测试各自只承担什么职责,并只用训练标签定义常数基线?
  5. 能否从真实 A 股图表提出可检验问题,同时把描述性关联、预测证据和因果结论分开?

若某一项只能复述术语而不能说明原因,可回到对应学习目标,用一个新的商业情境重新作答。

1.16.2 禁用情境与常见误区

当目标变量没有可操作定义、特征在决策时点尚不可得、测试集被用于选模型,或错误代价无法表达时,不应急于部署统计学习系统。数据量大并不会自动修复标签错位、选择偏差或概念漂移。

常见误区包括:把监督学习等同于因果推断;把训练误差当泛化误差;把高准确率当业务价值;把一次漂亮图形当稳定规律;把无法解释的部分都称为随机噪声。遇到这些表述,应追问“证据来自哪个集合、损失是什么、信息在何时可得、结论能外推到谁”。

1.16.3 无提示检索与迁移

不看正文,在纸上完成以下三题:

  1. 写出条件均值为什么最小化期望平方损失的三步推导。
  2. 为“未来 30 日客户流失”画出特征截止日、标签窗口和最终评价期,并指出一种潜在泄漏。
  3. 在类别极不平衡时,解释为何训练期多数类准确率必须进入比较,以及为什么它不能由验证或测试标签重新定义。

迁移任务:选择一个陌生的长三角企业决策(如供应商违约预警),用五句话写清决策主体、预测时点、观测单位、结果定义与错误代价;再判断当前证据最多支持描述、预测还是因果结论。

展开检索答案与继续学习判据
  1. 对任意预测值 \(a\),有 \(E[(Y-a)^2\mid X]=\operatorname{Var}(Y\mid X)+[E(Y\mid X)-a]^2\);第一项不随 \(a\) 变化,第二项在 \(a=E(Y\mid X)\) 时取零,因此条件均值最优。
  2. 特征截止于预测日,标签覆盖随后 30 日,最终评价期必须晚于开发期且在选模时不可见。把标签窗内的退款、注销或未来活跃度作为特征是一种泄漏。
  3. 多数类基线揭示模型是否只利用类别占比;它必须由训练标签锁定,因为用验证或测试占比更新会让评价期结果反过来改变预测规则。

若答案有误,可回到 式 1.1 重做条件均值推导,用“未来 14 日退货”重画时间线,并比较训练期与验证期多数类相反时的基线定义。

下一章将在本章的任务、损失与评价边界上展开误差分解和模型复杂度选择。

Hastie, Trevor, Robert Tibshirani, 和 Jerome Friedman. 2009年. The Elements of Statistical Learning: Data Mining, Inference, and Prediction. 2 版. Springer. https://doi.org/10.1007/978-0-387-84858-7.
James, Gareth, Daniela Witten, Trevor Hastie, Robert Tibshirani, 和 Jonathan Taylor. 2023年. An Introduction to Statistical Learning: With Applications in Python. Springer. https://doi.org/10.1007/978-3-031-38747-0.