22 零售超市销售额预测
22.1 引言销售预测的商业价值
零售超市销售预测:
- 库存管理: 避免缺货或积压
- 人员安排: 高峰期增加人手
- 营销策略: 促销时机选择
- 财务规划: 现金流预测
22.2 本章学习目标
本章以零售超市周销售额预测为例,把前面章节的数据处理与可视化技能串联成一条完整的建模流水线。通过本章学习,你将掌握:
- 销售预测类任务的标准流程:数据读取、特征构造、特征筛选、编码、模型训练与评估
- 从日期字段构造时间特征(星期、月份、年份)并按业务判断取舍的思路
- 用散点图矩阵与相关系数热力图支撑特征筛选的方法
- 分类变量(商店编号、月份)的独热编码(
pd.get_dummies) - 训练集/测试集划分、特征标准化与R²评估三个建模前置概念的含义
先修内容:第 章节 10 章(Pandas数据框基础)与第 章节 21 章(相关系数热力图的绘制与解读);绘图细节可回看第 章节 19 章。
22.3 本任务需要的前置概念
在照抄平台代码之前,先建立三个直觉概念。它们分别对应平台代码中的几行关键语句,理解之后再动手,输入平台时就不容易”抄错行”。
22.3.1 训练集与测试集
模型的最终价值在于对没见过的数据做预测,因此不能拿模型”学习过”的数据来给它打分。解决办法是把样本切成两份:一份交给模型学习规律,称为训练集;另一份只用来考试,称为测试集。平台代码中 X_train, X_test, y_train, y_test = train_test_split(...) 一行对应这一步,train_size=0.8 表示八成样本用于训练、两成用于考试,random_state=100 固定切分的随机性,保证每次重跑得到同一份考卷。如果省略这一步,模型可以”背下”训练样本,得到虚高的评估分数。
22.3.2 特征标准化
各特征的量纲相差悬殊:温度是几十的量级,CPI上百,失业率只有个位数。量纲不同并不代表数值大的特征更有用,但会让系数之间失去可比性,也让基于距离与梯度的数值计算变得不稳定。标准化的做法是把每一列变换成均值为0、标准差为1的量纲无关尺度。平台代码中 scaler = StandardScaler()、X_train = scaler.fit_transform(X_train)、X_test = scaler.transform(X_test) 三行对应这一步。特别要注意:测试集只执行 transform 而不再 fit,即测试集必须沿用训练集学到的均值和标准差,否则相当于让考卷迁就考生,评估随之失效——既可能虚高,也可能像本章模型构建一节的常见错误演示那样大幅偏低,总之不再是泛化能力的可信度量。
22.3.3 R²评估
R²(决定系数)回答的问题是:与”无脑地直接用均值来猜”相比,模型把预测误差降低了多少。R²=1表示完美预测,R²=0表示与均值基准一样差,越接近1越好;在测试集上它还可能为负,意味着模型比均值基准更差。平台代码中 r2 = r2_score(y_test, y_pred) 一行对应这一步,它只用测试集的真实值与预测值计算。R²是线性回归最常用的单一数字摘要,但它不区分高估与低估,也反映不出季节性等误差结构,本章末尾的分析结论会再回到这一点。
22.4 数据准备与探索
任务要求:读取平台内置的 01_Walmart_Store_sales.csv,完成一条完整的销售额预测流水线——从 Date 列提取 weekday/month/year 特征并删除日期、星期、年份等非特征列;用散点图矩阵与相关系数热力图考察各特征与 Weekly_Sales 的关系并剔除 Fuel_Price;对 Store 与 month 做独热编码;按 8:2 划分训练集与测试集(random_state=100)并做标准化;训练多元线性回归模型;最后打印测试集 R²。
# 注:01_Walmart_Store_sales.csv数据文件本地没有,但平台已经内置
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
# 导入相关包
import pandas as pd
import numpy as np # 导入NumPy数值计算库
import seaborn as sns # 导入Seaborn可视化库
import matplotlib.pyplot as plt # 导入Matplotlib绘图库
from sklearn.preprocessing import StandardScaler # 导入Scikit-learn的StandardScaler模块
from sklearn.model_selection import train_test_split # 导入Scikit-learn的train_test_split模块
from sklearn.linear_model import LinearRegression # 导入Scikit-learn的LinearRegression模块
from sklearn.metrics import r2_score # 导入Scikit-learn的r2_score模块
# 读取数据
df = pd.read_csv('01_Walmart_Store_sales.csv', parse_dates = True, infer_datetime_format = True)
# 整理数据
df.Date=pd.to_datetime(df.Date)
df['weekday'] = df.Date.dt.weekday # 提取日期时间属性
df['month'] = df.Date.dt.month # 提取日期时间属性
df['year'] = df.Date.dt.year # 提取日期时间属性
# 删除日期、星期、年份等非特征列
df.drop(['Date','weekday','year'], axis=1, inplace=True)
target = 'Weekly_Sales' # 指定目标变量为"Weekly_Sales"
# 相关性分析和特征选择
plt.figure(figsize=(10,8))
# 绘制变量两两关系图
sns.pairplot(df, x_vars=['Holiday_Flag', 'Temperature', 'Fuel_Price', 'CPI', 'Unemployment','month'],
y_vars=['Weekly_Sales'], height=4, aspect=0.7, kind='reg') # 定义列表y_vars
plt.savefig("1.png") # 保存图形至文件
plt.show() # 显示图形
df1=df.copy(deep=True) # 深拷贝数据框用于独立分析(不影响原始数据)
df1.drop(['Store'], axis=1, inplace=True) # 删除不需要的列'Store'
corr_matrix = df1.corr() # 计算相关系数矩阵
plt.figure(figsize=(10,8)) # 创建图形画布
sns.heatmap(corr_matrix, annot=True, cmap='Reds') # 绘制热力图
plt.savefig("2.png") # 保存图形至文件
plt.show() # 显示图形
df.drop(['Fuel_Price'], axis=1, inplace=True) # 删除不需要的列'Fuel_Price'
# 分类数据处理
df = pd.get_dummies(df, columns=['Store','month'])
# 提取特征和标签
X = df.drop([target],axis=1)
y = df[target] # 提取目标变量列赋值给y
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, test_size=0.2, random_state=100)
# 数据标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train) # 对数据进行变换
X_test = scaler.transform(X_test) # 对数据进行变换
# 训练多元线性回归模型
model = LinearRegression()
model.fit(X_train, y_train) # 在数据上训练model模型
# 测试集预测
y_pred = model.predict(X_test)
r2 = r2_score(y_test, y_pred) # 计算R²评估模型拟合优度
# 评估模型预测效果
print('R平方值(R^2):', r2)预期输出:数据文件平台内置,本地无数据,具体数值以平台运行结果为准。判读要点:
- 文本输出:仅最后一行
R平方值(R^2): ...的一个数值。判读时先看符号与量级:接近1说明测试集拟合好;明显偏低甚至为负,说明线性假设或特征集还有改进空间。 - 图形输出(1.png):Holiday_Flag、Temperature、Fuel_Price、CPI、Unemployment、month 六个特征对 Weekly_Sales 的带回归线散点图。判读要点:各子图回归线斜率是否明显、点云是否松散;斜率接近0且点云松散的特征,预测能力弱。
- 图形输出(2.png):剔除 Store 列后数值变量的相关系数热力图(Reds顺序配色,颜色越深正相关越强,负相关与无相关均呈浅色)。判读要点:各特征与 Weekly_Sales 所在行列的颜色深浅,以及特征彼此之间是否高度相关(多重共线性线索)。
22.5 特征工程
# 注:本块是平台任务的分步讲解版(重述特征工程与相关性分析部分)。若已在平台运行过完整任务,请勿紧接着重复运行本块(Fuel_Price 列已被删除,会报 KeyError);建议在平台新会话中单独按块顺序运行讲解块
# =============================================================================
# 题目:特征工程与特征相关性分析
# =============================================================================
# 本代码进行特征工程,包括相关性分析和特征选择。通过可视化各特征
# 与目标变量的关系,识别重要特征并剔除冗余特征,提升模型性能和
# 可解释性。这是机器学习项目中至关重要的一步。
# ==================== 定义特征列表 ====================
# 选择可能影响销售额的特征变量
features = ['Holiday_Flag', 'Temperature', 'Fuel_Price', 'CPI', 'Unemployment', 'month']
# Holiday_Flag: 假期标志,假期销售通常更高
# Temperature: 温度,可能影响购物意愿
# Fuel_Price: 燃油价格,影响消费者的出行和购买力
# CPI: 消费价格指数,衡量通胀水平
# Unemployment: 失业率,反映经济状况
# month: 月份,捕捉季节性规律
# 定义目标变量(要预测的变量)
target = 'Weekly_Sales' # 周销售额是本项目的预测目标
# ==================== 绘制散点图矩阵 ====================
# 使用Seaborn绘制散点图矩阵,展示各特征与目标变量的关系
sns.pairplot(
df, # 数据源DataFrame
x_vars=features, # x轴变量列表(所有特征)
y_vars=[target], # y轴变量列表(目标变量)
height=4, # 每个子图的高度(英寸)
aspect=0.7, # 子图的宽高比(宽度/高度)
kind='reg' # 绘制带回归线的散点图
)
plt.suptitle('各特征与销售额的关系', y=1.02) # 设置总标题,y=1.02将标题向上移动避免重叠
plt.show() # 显示散点图矩阵
# ==================== 计算相关性矩阵 ====================
# 创建数据副本,避免修改原始数据
df1 = df.copy(deep=True) # deep=True创建深拷贝,包括数据和索引
# 删除Store列(分类变量,无法计算相关系数)
df1.drop(['Store'], axis=1, inplace=True) # axis=1表示删除列,inplace=True直接在原数据上修改
# 计算所有数值变量的相关系数矩阵
corr_matrix = df1.corr() # corr()方法计算皮尔逊相关系数,返回对称矩阵
# 相关系数范围[-1, 1],绝对值越大相关性越强
# ==================== 绘制相关性热力图 ====================
plt.figure(figsize=(10, 8)) # 创建画布,尺寸为10x8英寸
# 使用Seaborn绘制热力图,可视化相关系数矩阵
sns.heatmap(corr_matrix, annot=True, cmap='Reds', center=0)
# annot=True在每个格子中显示相关系数的数值
# cmap='Reds'使用红色系配色方案(颜色越深相关性越强)
# center=0将颜色映射的中心设为0(负值与0落在色标浅端,0的具体深浅取决于最强负相关的位置,正值越强越深)
plt.title('特征相关性热力图') # 设置图表标题
plt.show() # 显示热力图
# ==================== 特征选择 ====================
# 根据相关性分析结果,删除与目标变量相关性低的特征
df.drop(['Fuel_Price'], axis=1, inplace=True) # 删除燃油价格列
# 如果Fuel_Price与Weekly_Sales的相关系数接近0,说明它对预测没有帮助
# 删除不相关特征可以降低模型复杂度,避免过拟合
# ==================== 输出解读 ====================
# 散点图矩阵展示了各特征与销售额的线性关系:
# - 如果点的分布近似直线,说明存在线性关系
# - 如果回归线斜率较大,说明该特征对销售额影响显著
# - 如果点很分散,说明该特征预测能力弱
#
# 热力图展示了所有特征之间的相关性:
# - 本图使用Reds顺序配色,颜色越深表示正相关越强
# - 负相关与无相关均呈浅色(接近白色),二者无法直接区分
# - 若需同时直观展示正负相关,应改用RdBu等双色渐变(diverging)配色
#
# 特征选择原则:
# 1. 删除与目标变量相关性低的特征
# 2. 删除与其他特征高度相关的特征(多重共线性)
# 3. 保留与目标变量相关性高的特征22.6 模型构建
上面特征工程讲解块覆盖了平台流水线的前半段(特征构造与筛选);后半段的”编码→划分→标准化→训练→评估”不再逐行重述,改为把其中最容易写错的一步单独拎出来做常见错误演示:把平台代码中的 X_test = scaler.transform(X_test) 换成初学者常写的 X_test = scaler.fit_transform(X_test),其余环节保持不变。演示在一份可本地运行的合成数据上进行(150行”单店周度”记录,列名与平台数据一致),并刻意给 train_test_split 加上 shuffle=False 以保留时间顺序,模拟”冬春季历史周训练、夏季未来周考试”的真实预测场景。请重点观察三个数字:考试期(测试集)的温度均值、两种写法各自的测试集R²、以及测试集温度被标准化后的均值。
# 本地演练说明:平台内置的01_Walmart_Store_sales.csv本地没有,以下用同结构的内联合成数据演示标准化泄漏
import numpy as np # 导入NumPy数值计算库
import pandas as pd # 导入Pandas数据分析库
from sklearn.preprocessing import StandardScaler # 导入标准化器
from sklearn.model_selection import train_test_split # 导入训练集测试集划分函数
from sklearn.linear_model import LinearRegression # 导入线性回归模型
from sklearn.metrics import r2_score # 导入R²评估函数
rng = np.random.default_rng(100) # 固定随机种子,与平台代码的random_state=100呼应
temperature = np.concatenate([rng.normal(5, 5, 120), rng.normal(30, 8, 30)]) # 前120行为冬春季周记录、后30行进入夏季,温度整体抬升
unemployment = rng.normal(8, 2, 150) # 失业率特征,均值8、标准差2
weekly_sales = 1000 + 15*temperature - 40*unemployment + rng.normal(0, 20, 150) # 线性数据生成机制:温度推高销售、失业率压制销售
X = pd.DataFrame({'Temperature': temperature, 'Unemployment': unemployment}) # 特征矩阵,列名同平台数据
y = pd.Series(weekly_sales, name='Weekly_Sales') # 目标变量,列名同平台数据
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, shuffle=False) # 按时间顺序8:2切分:历史周训练、未来周考试
scaler = StandardScaler() # 正确做法:标准化器只在训练集上学习均值和标准差
X_train_s = scaler.fit_transform(X_train) # 训练集执行fit_transform
X_test_s = scaler.transform(X_test) # 测试集只执行transform,沿用训练集的统计量
r2_correct = r2_score(y_test, LinearRegression().fit(X_train_s, y_train).predict(X_test_s)) # 正确流程的测试集R²
leaky = StandardScaler() # 错误做法:给测试集另配一个标准化器
X_train_l = leaky.fit_transform(X_train) # 训练集的处理与上面完全相同
X_test_l = leaky.fit_transform(X_test) # 常见错误:测试集误用fit_transform,改用测试集自己的均值和标准差
r2_leaky = r2_score(y_test, LinearRegression().fit(X_train_l, y_train).predict(X_test_l)) # 泄漏流程的测试集R²
print('训练期/考试期温度均值: %.2f / %.2f' % (X_train['Temperature'].mean(), X_test['Temperature'].mean())) # 两段样本的温度水平差异
print('正确做法(测试集只transform)的测试集R²: %.4f' % r2_correct) # 正确流程的评估结果
print('常见错误(测试集fit_transform)的测试集R²: %.4f' % r2_leaky) # 泄漏流程的评估结果
print('测试集Temperature标准化后的均值: 正确 %.4f / 泄漏 %.4f' % (X_test_s[:, 0].mean(), X_test_l[:, 0].mean())) # 泄漏把考试期数据"重新居中"预期输出(本机实际运行结果):
训练期/考试期温度均值: 5.42 / 31.42
正确做法(测试集只transform)的测试集R²: 0.9790
常见错误(测试集fit_transform)的测试集R²: -7.3393
测试集Temperature标准化后的均值: 正确 5.4645 / 泄漏 -0.0000
两种写法只差一个 fit,评估结论却天差地别:正确做法的测试集R²约为0.98,误用 fit_transform 后跌到约-7.34。原因在最后一行输出:考试期的温度均值是31.42℃,按训练期口径标准化后其均值约为+5.46,模型”知道”这是一段比历史更热的时期;泄漏写法却用测试集自己的均值和标准差重新居中,均值被压回0,模型看到的考试样本仿佛回到了”平均温度的历史周”,夏季带来的销售额抬升被整体抹掉,预测因此严重偏低。两点提醒:其一,泄漏的方向并不固定——本例表现为大幅偏低,换一种数据形态也可能表现为虚高,所以它的性质不是”乐观偏差”,而是”评估失效”;其二,凡是带 fit 的预处理都只能在训练集上拟合,测试集一律只 transform,这是流水线中不可妥协的纪律。回到平台任务本身,请保持平台原样代码中的 scaler.transform(X_test) 不变。
22.7 分析结论
本章流水线完成的事情可以概括为:把一张含日期、商店编号与若干外部变量的周度销售表,整理成”数值特征矩阵 + 销售额标签”的标准建模形态,再以多元线性回归在8:2划分下完成训练与测试,并用测试集R²作为唯一的汇报指标。本章数值结论以平台运行结果为准(本地无数据),以下仅做定性判读。
特征工程方面,流水线做了三件事:一是从 Date 派生星期、月份、年份,并按业务常识删除日期与年份列,只保留月份以捕捉季节性;二是用散点图矩阵与热力图做相关性检查,据此删除与目标相关性弱的 Fuel_Price;三是把 Store 与 month 两个类别变量独热编码,使其能够进入线性模型——商店间的固有差异往往是这类数据中最重要的解释来源之一。
R² 的解读要克制:它只度量测试集上预测值与真实值的总体贴近程度,既不保证每个商店、每个时段都预测得准,也不说明因果关系。若平台结果显示R²较高,合理解释是商店个体差异与月份季节性被虚拟变量捕捉到了;若偏低,应先怀疑线性假设或遗漏交互项,而不是直接断言”数据没有规律”。该模型可用于备货量级层面的粗估;用于精细到单店单周的排班与促销决策之前,还应补充残差分析并经业务复核。
22.8 本章小结
本章新增的技能要点:
- 流水线意识:读数、特征构造、特征筛选、编码、划分、标准化、训练、评估八步顺序不可颠倒,尤其编码与划分的先后、标准化与划分的先后各有讲究。
- 日期特征化:
pd.to_datetime之后用.dt.weekday、.dt.month、.dt.year把日期变成可建模的数值特征。 - 特征筛选:
sns.pairplot(kind='reg')看单特征线性趋势,df.corr()加热力图看两两相关,双图互补决定特征去留。 - 独热编码:
pd.get_dummies处理类别变量;删除Fuel_Price这类弱相关特征可简化模型、降低过拟合风险。 - 防泄漏习惯:
fit_transform只作用于训练集,测试集只transform;random_state固定保证结果可复现。
易错点:
- 测试集误写
X_test = scaler.fit_transform(X_test):测试集被自己的均值和标准差重新居中,评估随之失效,正文本地演练中 R² 从约0.98跌到约-7.34 - 对全部数据先标准化再划分:统计量混入测试集信息,与上一条同属泄漏;正确顺序是先
train_test_split,再在训练集上fit_transform - 以为
pd.read_csv(..., parse_dates=True)已把Date列转为日期:不指定列名时它并不转换,必须再执行pd.to_datetime(df.Date)才能用.dt.weekday等提取特征 - 把 R² 当 0~1 的”准确率”读:它度量的是与均值基准的差距,可以为负,负值意味着比直接猜均值更差
- 算相关系数时把
Store编号留在数值列里:商店编号是ID型类别,相关系数没有意义,应先删除再corr();且cmap='Reds'下负相关与无相关同为浅色,浅色格不能一律读成”不相关” - 平台任务跑完后紧接着重跑特征工程讲解块会报
KeyError:Fuel_Price列已被删除,讲解块应在平台新会话中单独运行
22.9 动手与思考
以下练习每题附参考答案(默认折叠);概念题与变式题可对照自查,商业判断题不设唯一答案,判断依据与口径比结论更重要。
概念辨析:训练集R²与测试集R²有何区别?为什么本章只汇报测试集R²,而且它有可能为负?
参考答案(点开前请先独立完成)
解题思路:训练集R²是把模型“学习过”的样本既当课本又当考卷算出来的分数,衡量的是拟合能力;测试集R²用的全是模型没见过的样本,衡量的是泛化能力。本章只汇报测试集R²,是因为模型的最终用途是预测没见过的未来周——训练集R²再高也只说明“背书”背得好,不能证明对新样本有用。它有可能为负,是因为R²的定义是与“直接用测试集均值来猜”的基准比较:当模型的残差平方和超过该基准的总离差平方和时,R²就小于零,含义是比无脑猜均值更差(本章“模型构建”一节的常见错误演示中,泄漏流程的测试集R²约为-7.34,正是这种情形)。另需说明:在简单线性模型加干净数据的情形下两个R²可能非常接近;模型越复杂、样本越少、噪声越大,训练集R²越容易明显高于测试集R²,两者之差是过拟合的常用信号。
回扣本章:对应“本章新增的技能要点”第1条(划分、标准化、训练、评估的先后顺序不可颠倒)与第5条(
random_state固定保证结果可复现)。概念辨析:
scaler.fit_transform(X_train)与scaler.transform(X_test)的差别是什么?如果对测试集单独fit,会造成什么问题?参考答案(点开前请先独立完成)
解题思路:
fit_transform等于先fit(在传入的数据上计算均值与标准差)再transform(按这一套统计量做缩放);transform只做缩放,统计量沿用标准化器上次fit的结果。平台任务(列表 22.1)在训练集上fit_transform、测试集上只transform,含义是考试期的数据必须按历史期的均值和标准差来缩放。若对测试集单独fit(典型错误写法X_test = scaler.fit_transform(X_test)),测试集会被它自己的均值和标准差重新居中、缩放,相当于让考卷迁就考生——评估不再度量真实泛化能力,且偏差方向并不固定:可能虚高,也可能像正文本地演练那样,考试期偏高的温度被压回均值0、夏季带来的销售额抬升被整体抹掉,R²从约0.98跌到约-7.34。结论是:凡带fit的预处理只能在训练集上做,这是评估可信的前提。回扣本章:对应“本章新增的技能要点”第5条(
fit_transform只作用于训练集,测试集只transform)。变式任务:把评估指标从R²换成MAE或RMSE(从
sklearn.metrics导入相应函数),改造思路是什么?这两个指标在业务上分别如何解读(平均每期偏差多少金额)?参考答案(点开前请先独立完成)
改造思路:流水线前段(读数、日期特征构造、特征筛选、独热编码、划分、标准化、训练、预测)一行不动,只替换评估环节的三处:导入语句由
r2_score换成mean_absolute_error与mean_squared_error;指标计算两行分别求 MAE 与 RMSE;print输出行同步替换。业务解读:MAE 把每期预测偏差取绝对值后平均,单位与Weekly_Sales相同,可直接读成“平均每期偏差多少金额”;RMSE 先把偏差平方、取平均再开方,同样以金额为单位,但对大偏差惩罚更重。两者结合可判断误差形态:RMSE 明显大于 MAE,说明误差集中在少数期;两者接近,说明误差均匀分布在各期。# 变式代码:只替换评估环节(读数、特征构造、独热编码与@lst-ch22-platform-task完全一致,此处不重复) from sklearn.metrics import mean_absolute_error, mean_squared_error # ← 改动点1:导入MAE与均方误差函数(可保留r2_score作对照) X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, test_size=0.2, random_state=100) # 与平台任务一致 scaler = StandardScaler() # 与平台任务一致 X_train = scaler.fit_transform(X_train) # 与平台任务一致:训练集拟合并变换 X_test = scaler.transform(X_test) # 与平台任务一致:测试集只变换(防泄漏纪律不变) model = LinearRegression() # 与平台任务一致 model.fit(X_train, y_train) # 与平台任务一致 y_pred = model.predict(X_test) # 与平台任务一致:测试集预测 mae = mean_absolute_error(y_test, y_pred) # ← 改动点2:MAE即逐期偏差取绝对值后平均,单位为金额 rmse = mean_squared_error(y_test, y_pred) ** 0.5 # ← 改动点2:RMSE即偏差平方取平均后开方,对大误差惩罚更重 print('测试集MAE(平均每期偏差金额):', mae) # ← 改动点3:输出行同步替换 print('测试集RMSE(放大大误差的口径):', rmse) # ← 改动点3:输出行同步替换结构性判读:文本输出由一行
R平方值(R^2): …变为两行金额口径的误差值,两幅图形输出完全不变(特征工程部分未改动)。判读要点:先看MAE的量级是否在业务可接受范围内(备货量级允许的每期偏差),再比较RMSE与MAE的相对大小——RMSE远大于MAE说明个别周预测严重失准,应回查异常周;接近则说明误差普遍而温和。本地演练版(模拟数据):在与本章“模型构建”一节同型的150行合成数据上实跑(列名同平台数据,
random_state=100):# 本地演练版:在与正文本地演练同型的合成数据上对比MAE与RMSE两种误差口径 import numpy as np # 导入NumPy数值计算库 import pandas as pd # 导入Pandas数据分析库 from sklearn.model_selection import train_test_split # 导入训练集测试集划分函数 from sklearn.linear_model import LinearRegression # 导入线性回归模型 from sklearn.metrics import mean_absolute_error, mean_squared_error # 导入MAE与均方误差评估函数 rng = np.random.default_rng(100) # 固定随机种子,与平台代码的random_state=100呼应 temperature = np.concatenate([rng.normal(5, 5, 120), rng.normal(30, 8, 30)]) # 前120行冬春季记录、后30行进入夏季 unemployment = rng.normal(8, 2, 150) # 失业率特征,均值8、标准差2 weekly_sales = 1000 + 15 * temperature - 40 * unemployment + rng.normal(0, 20, 150) # 线性数据生成机制 X = pd.DataFrame({'Temperature': temperature, 'Unemployment': unemployment}) # 特征矩阵,列名同平台数据 y = pd.Series(weekly_sales, name='Weekly_Sales') # 目标变量,列名同平台数据 X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, random_state=100) # 8:2随机划分 model = LinearRegression().fit(X_train, y_train) # 训练多元线性回归模型 y_pred = model.predict(X_test) # 测试集预测 print('MAE(平均每期偏差金额): %.2f 元' % mean_absolute_error(y_test, y_pred)) # 平均绝对误差:逐期偏差取绝对值后平均 print('RMSE(均方根误差): %.2f 元' % mean_squared_error(y_test, y_pred) ** 0.5) # 均方根误差:先平方再平均后开方,放大大误差 print('测试集周数:', len(y_test)) # 测试集样本量模拟数据演练结果(本机 peter 环境实际运行结果,仅演示流程与判读方法,真实数值以平台运行结果为准):
MAE(平均每期偏差金额): 15.63 元 RMSE(均方根误差): 18.31 元 测试集周数: 30演练判读:这份干净合成数据上RMSE(18.31)仅略高于MAE(15.63),误差接近均匀分布;若平台数据上RMSE远大于MAE,应优先排查个别异常周(节假日、促销脉冲)。
预期输出:数据文件由教学平台内置,本机无法复现,具体数值以平台运行结果为准。
注意:以上为本题变式的独立代码;列表 22.1 对应平台任务的原始代码块仍须原样输入教学平台,不要用本变式替换。
回扣本章:对应“本章新增的技能要点”第1条(评估是八步流水线的末环,换指标只动末环,不动前段)。
变式任务:若把
train_size改为0.7、random_state改为其他数值,流水线中哪些环节完全不受影响、哪些输出会随之改变?请说明理由,再到平台上验证。参考答案(点开前请先独立完成)
改造思路:唯一改动在
train_test_split一行——train_size=0.8, test_size=0.2, random_state=100换成train_size=0.7, test_size=0.3, random_state=42(比例之和仍须为1)。完全不受影响的环节:读取数据、pd.to_datetime与.dt特征构造、散点图矩阵与相关系数热力图、剔除Fuel_Price、get_dummies独热编码、X 与 y 的生成——这些只依赖数据本身,与样本怎么切分无关。随之改变的输出:训练集与测试集的行数、StandardScaler学到的均值与标准差、回归系数,以及最终R²数值。理由:划分参数决定多少样本进入“课本”,随机种子决定具体抽到哪些行;标准化统计量与模型都在新的训练集上重新估计,但流水线结构不变。# 变式代码:切换train_size与random_state(两参数所在行即唯一改动,其余环节与平台任务一致) X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7, test_size=0.3, random_state=42) # ← 改动点:0.8/0.2/100改为0.7/0.3/42 scaler = StandardScaler() # 与平台任务一致 X_train = scaler.fit_transform(X_train) # 与平台任务一致:统计量随新训练集重算 X_test = scaler.transform(X_test) # 与平台任务一致:测试集只变换 model = LinearRegression().fit(X_train, y_train) # 与平台任务一致:模型在新训练集上重估 y_pred = model.predict(X_test) # 与平台任务一致 r2 = r2_score(y_test, y_pred) # 与平台任务一致 print('R平方值(R^2):', r2) # 与平台任务一致:数值会随划分参数改变结构性判读:文本输出仍是最后一行的单个R²,数值会变但一般仍处同一量级;两幅图形输出与原任务完全一致。判读要点:对比改参前后的R²变化幅度——小幅波动说明结论对划分方式稳健;大幅波动说明样本量偏小或结论不稳,应报告多种划分下的结果区间而不是单点数值。
本地演练版(模拟数据):在同一份合成数据上切换三组划分参数(列名同平台数据,
random_state=100):# 本地演练版:同一合成数据上切换train_size与random_state,观察哪些输出随之改变 import numpy as np # 导入NumPy数值计算库 import pandas as pd # 导入Pandas数据分析库 from sklearn.model_selection import train_test_split # 导入训练集测试集划分函数 from sklearn.linear_model import LinearRegression # 导入线性回归模型 from sklearn.preprocessing import StandardScaler # 导入标准化器 from sklearn.metrics import r2_score # 导入R²评估函数 rng = np.random.default_rng(100) # 固定数据生成种子,三组实验共用同一份数据 temperature = rng.normal(20, 10, 150) # 温度特征 unemployment = rng.normal(8, 2, 150) # 失业率特征 weekly_sales = 1000 + 15 * temperature - 40 * unemployment + rng.normal(0, 20, 150) # 线性数据生成机制 X = pd.DataFrame({'Temperature': temperature, 'Unemployment': unemployment}) # 特征矩阵,列名同平台数据 y = pd.Series(weekly_sales, name='Weekly_Sales') # 目标变量,列名同平台数据 print('特征工程输出与划分参数无关:温度列均值 %.4f' % X['Temperature'].mean()) # 划分之前的一切输出只依赖数据本身 for train_size, seed in [(0.8, 100), (0.7, 100), (0.7, 42)]: # 三组划分参数:先改比例,再换种子 X_tr, X_te, y_tr, y_te = train_test_split(X, y, train_size=train_size, random_state=seed) # 改动点:两个参数在此切换 scaler = StandardScaler() # 标准化器在新的训练集上重新拟合 X_tr_s = scaler.fit_transform(X_tr) # 训练集拟合并变换 model = LinearRegression().fit(X_tr_s, y_tr) # 在标准化后的训练集上训练 r2 = r2_score(y_te, model.predict(scaler.transform(X_te))) # 测试集沿用训练集统计量做变换后评估 print('train_size=%.1f, random_state=%d → 训练集%d行,测试集R² %.4f' % (train_size, seed, len(X_tr), r2)) # 随参数变化的输出模拟数据演练结果(本机 peter 环境实际运行结果,仅演示流程与判读方法,真实数值以平台运行结果为准):
特征工程输出与划分参数无关:温度列均值 21.0266 train_size=0.8, random_state=100 → 训练集120行,测试集R² 0.9881 train_size=0.7, random_state=100 → 训练集105行,测试集R² 0.9899 train_size=0.7, random_state=42 → 训练集105行,测试集R² 0.9838演练判读:三组参数下训练集行数由120变为105、R²在0.98上下小幅波动,而特征工程的输出(温度列均值)与划分参数无关——与上面的推理一致。
预期输出:数据文件由教学平台内置,本机无法复现,具体数值以平台运行结果为准。
注意:以上为本题变式的独立代码;列表 22.1 对应平台任务的原始代码块仍须原样输入教学平台,不要用本变式替换。
回扣本章:对应“本章新增的技能要点”第5条(
random_state固定保证可复现;切换参数检验结论稳健性是同一纪律的延伸)。商业判断:若平台运行结果显示R²仅略高于0.5,而门店经理希望据此直接制定下周的排班与促销预算,你会给出什么经营建议?请从”模型可用边界”与”还需补充的信息”两方面作答。
参考答案(点开前请先独立完成)
参考作答框架:本题不设唯一结论,优秀作答应覆盖以下维度。
- 口径先行:先确认这个R²的口径——测试集、8:2划分、
random_state=100下的单次结果(列表 22.1);第4题的演练说明换一个随机种子数值就会波动,因此“略高于0.5”应作为区间读数而非精确分数;R²也不区分高估与低估,反映不出季节性与节假日等误差结构。 - 模型可用边界:R²约0.5只支持量级层面的参考(如总量备货粗估),不支持直接落到单店单周的排班与促销预算;使用前至少应按商店、按月份分解残差,确认误差没有集中在个别门店或时段,并做滚动回测(近几周“预测对实际”)。
- 常见错误作答形态:一是“R²不到0.6就是坏模型”或“R²超过0.5就够用”两个极端,把单一指标当充分条件;二是把R²误当预测准确率;三是不交代划分方式与口径就下结论。
- 还需补充的信息:促销与假期日历、天气、更长历史(覆盖更多季节周期)、分店误差分布与预测区间、业务可接受的金额误差容限(用第3题的MAE或RMSE口径表述)。
- 建议表述方向:先把它定位为“总量级参考+人工复核”的辅助工具,同步开展误差分解与回测,达标后再逐步用于预算决策。
回扣本章:对应“本章新增的技能要点”第1条(评估结论必须连同口径一起汇报)。
- 口径先行:先确认这个R²的口径——测试集、8:2划分、