ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

R语言钻石价格回归分析:从数据清洗到交互项与模型评估

R语言钻石价格回归分析:从数据清洗到交互项与模型评估

简介:围绕钻石价格影响因素展开的R语言数据分析实践文档,面向掌握R基础语法、希望打通从数据清洗到回归建模全流程的学习者,可用于课程作业、统计分析入门练习与回归分析案例参考。压缩包共1个文件,为docx格式的完整分析报告,约2.4MB,内含变量说明表、数据探索截图与结果解读,便于按章节对照阅读。已有285人学习下载。内容依托ggplot2自带的diamonds数据集,涵盖缺失值与重复值检查、箱线图异常值剔除、价格直方图与核密度分布刻画,以及切割、颜色、净度、克拉重量与价格的箱线图和小提琴图对比;并对定性变量做因子编码、构造新变量,逐步建立多元线性回归模型以完成价格预测与走势判断。

1. 钻石价格分析:为什么“最完美”不等于“最贵”

钻石价格分析最容易掉进去的坑,是把“最完美”直接等同于“最贵”。用 R 语言对 ggplot2 自带的 diamonds 做回归分析,5.4 万条记录会先给一个反直觉结果:理想切割的钻石里仍有低价样本;更关键的是,数据里没有时间列,所谓价格走势只能理解为品质组合到价格的响应曲面。回归分析在这里不是算命,而是把 carat、cut、color、clarity 这些变量对价格的边际影响拆开。搞懂这件事,比背一条 lm() 公式有用得多。

这份数据适合做 R 数据分析,不是因为它干净,而是因为它把真实数据该有的麻烦都摆出来了:有序分类、右偏价格、零尺寸记录、异常值、交互项。机器学习里常说的“特征工程决定上限”,在这个数据集上体现得很直接。下面从数据底盘开始,一层层拆到模型和排查。

2. 数据底盘:diamonds 的 10 个变量如何决定回归模型的上限

2.1 十个变量不是并列的:连续、有序分类与名义分类的建模代价

diamonds 的 10 个变量看起来只是列名不同,实际在回归模型里的地位完全不同。carat、depth、table、x、y、z、price 是连续值,cut、color、clarity 是分类值。分类值里又有强弱顺序:cut 从 Fair 到 Ideal,color 从 J 到 D,clarity 从 I1 到 IF。R 默认把字符向量转成 factor 时按字母排序,参照组会变成“字母最小”的水平,而不是业务上最差或最好的水平。这个细节不处理,后面哑变量的系数解释会全部反过来。

属性名解释范围/水平类型建模注意
carat克拉重量0.2–5.01连续价格主驱动,但关系非线性
cut切割质量Fair、Good、Very Good、Premium、Ideal有序分类水平顺序要显式固定
color颜色J–I–H–G–F–E–D(最差到最好)有序分类D 最好,参照组别设错
clarity净度I1–SI2–SI1–VS2–VS1–VVS2–VVS1–IF有序分类稀有水平样本量小,系数方差大
depth总深度百分比43–79连续与 x、y、z 存在公式关系
table顶部宽度百分比43–95连续极端值要单独看
price美元价格326–18823(表口径)连续右偏,常考虑对数
x长度(mm)0–10.74连续0 值属尺寸异常
y宽度(mm)0–58.9连续存在极端大值
z深度(mm)0–31.8连续0 值不能直接进体积公式

表里 price 的上界写了 18823,正文描述里又出现最高 11886,这不是小事。两个口径混用,后面异常值阈值、预测区间、模型评价都会漂。写代码时以数据实际 summary 为准,文字描述只能当线索。另一个容易忽略的是 depth 的定义:总深度百分比约等于 2*z/(x+y),它和 x、y、z 不是独立信息。把 depth、x、y、z 同时塞进线性模型,会引入共线性,系数符号可能变得不可解释。

2.2 缺失值、重复值与零尺寸:数据清洗先于模型

这份数据的第一层检查不是画图,而是确认“有没有缺失、有没有重复、有没有结构性零值”。输入里提到用 mice 的 md.pattern 看缺失模式,结论是无缺失值。无缺失值不代表无问题:x、y、z 为 0 的记录通常意味着尺寸没有录进去,或者数据本身是残缺样本。它们不是价格异常,而是特征异常。直接拿去做体积衍生变量,会把 0 带进分母或乘积,后面残差图会出现莫名其妙的杠杆点。

# 读取内置数据:diamonds 随 ggplot2 发布,不需要外部文件 library(ggplot2) data("diamonds", package = "ggplot2") # 先看结构和量纲,避免把有序分类当连续值 str(diamonds) summary(diamonds) # 缺失值模式:md.pattern 返回矩阵,1 表示有值,0 表示缺失 library(mice) md.pattern(diamonds, rotate.names = TRUE) # 重复行与零尺寸记录:零尺寸通常是录入或结构性异常 sum(duplicated(diamonds)) sum(diamonds$x == 0 | diamonds$y == 0 | diamonds$z == 0)

str() 用来确认列类型,summary() 用来看连续变量的分位数和极端值,md.pattern() 用来确认缺失是不是成块出现。duplicated() 返回重复行数量,零尺寸那行是布尔计数。这里的关键不是记住函数名,而是理解清洗顺序:先确认类型,再确认缺失,再确认重复,最后才看异常值。顺序反了,异常值阈值会被错误类型带偏。

重复值也要分两种情况。完全重复的行可以直接去重;业务上重复但数值不完全一致的记录,不能一刀切。diamonds 里同一颗钻石的多次记录如果存在,应该先确认主键,而不是用 duplicated() 删完就当干净。输入里提到去掉重复行,这个动作可以做,但要在报告里写清楚删了多少行、删的是完全重复还是业务重复。

2.3 价格右偏与异常值:箱线图删掉的可能正是目标

price 的分布是典型右偏:大量钻石集中在几百到几千美元,少数高克拉、高净度钻石把尾部拉得很长。箱线图用 1.5 IQR 标异常点,在高价尾部会标出一大片。问题在于,这些点不一定是错误录入,很多就是目标里的“稀有高价钻石”。如果全局一刀切删除,模型会变得“好看”,但预测高价的能力被削掉。更稳的做法是按 carat 分层看价格离散,或者用模型残差判断异常,而不是只看原始 price。

# 用箱线图看 price 的全局分布;1.5 IQR 之外只是候选异常 p <- ggplot(diamonds, aes(y = price)) + geom_boxplot(fill = "grey85") + labs(title = "diamonds price 全局箱线图") p # 按 carat 分箱后看价格离散,避免全局阈值把大克拉高价误删 diamonds$carat_bin <- cut(diamonds$carat, breaks = c(0, 0.5, 1, 1.5, 2, 3, 5.1), include.lowest = TRUE) ggplot(diamonds, aes(x = carat_bin, y = price)) + geom_boxplot(outlier.alpha = 0.2)

cut() 的 breaks 决定分箱边界,include.lowest 保证最小值进入第一箱,outlier.alpha 控制异常点透明度。全局箱线图适合发现录入错误,不适合直接当删除规则。按 carat 分箱后,你会看到同样一克拉区间内价格仍有很大离散,这种离散来自 cut、color、clarity 的组合,不是单纯异常。把这种离散全删掉,等于把模型要解释的部分删了。

注意:异常值处理要先问“它是错误,还是稀有但真实的样本”。价格预测任务里,高价样本往往最重要,删之前要留证据。

2.4 描述性统计:峰值、断层和稀有性如何影响模型假设

价格直方图和核密度曲线会给出三个信息:峰值在 1000 美元左右,2000、4000、8000 附近有断层,8000 之后密度迅速贴近价格轴。峰值说明市场对这个价位接受度高,断层说明定价存在心理锚点或分级溢价。对回归模型来说,这意味着 price 的条件分布不是正态的,残差也不会天然满足同方差。直接做普通最小二乘,系数估计还能用,但置信区间和 p 值的解释要谨慎。

分布右偏还会影响损失函数。平方损失对高价样本非常敏感,模型会偏向拟合高价尾部;如果目标是给大多数消费者做价格参考,绝对误差或对数价格可能更稳。常见的实现思路是对 price 取 log,把乘法关系变成加法关系,同时压缩尾部。取对数后系数解释要改成“价格变化百分比”,不能再说“每克拉增加多少美元”。这一步不是必做,但要在建模前想清楚目标。

描述性统计还要看分类变量的样本量。cut 里 Ideal 约 21488 条,Premium 约 13748 条,Very Good 约 12069 条,Good 约 4891 条,Fair 约 1598 条。颜色 G 数量最高,D 和 J 数量最少。稀有水平在回归里会得到更大的系数标准误,交互项里更明显。样本量大不代表每个组合都稳,Ideal 与 D 色与 IF 净度的交叉格可能很稀,交互项一放进去就容易过拟合。

3. 从散点到模型:简单线性回归为什么一克拉系数会“看起来很大”

3.1 carat 与 price 的散点:线性模型为什么只能做基线

carat 和 price 的散点不是一条直线,而是一个喇叭口:克拉数越大,价格离散越大。小克拉区间价格挤在几百到几千美元,大克拉区间可以拉到上万甚至更高。线性回归假设条件均值随 carat 线性变化,实际关系更接近凸函数,价格随克拉增长的速度在加快。只用 carat 做简单线性回归,能得到一个显著为正的系数,但它只是基线,不是最终模型。

散点图还能看出异方差。小克拉样本的残差方差小,大克拉样本的残差方差大。普通最小二乘对异方差不是无偏性失效,而是标准误不可靠。样本量 5 万多时,p 值几乎总是很小,显著性不能证明模型设定正确。看残差图比看 p 值更有信息:如果残差随拟合值呈漏斗形,说明方差不是常数,后面要么变换,要么用稳健标准误。

简单线性回归的价值在于定锚。它告诉你 carat 单独能解释多少价格变化,后面加 cut、color、clarity 和交互项,才有对照。没有这个基线,多元模型的提升幅度说不清楚。很多分析一上来就堆变量,最后 R² 很高,但不知道是 carat 贡献的,还是分类变量把样本切碎了。

3.2 系数 7756 与 776063:单位错位比模型错更可怕

输入正文里同时出现“776063”和“每提高一克拉价格预期增加 7760.3 美元”。按 carat 的单位是克拉、price 的单位是美元,系数应该是每克拉多少美元。776063 这个数字明显是小数点或单位错位,7760.3 更接近可解释的边际价格。写代码时不要手抄文字里的系数,必须看 summary() 输出。系数抄错,后面预测、置信区间、业务解释全错,而且这种错很难被 R² 发现。

# 简单线性回归:只放 carat,作为基线模型 fit_carat <- lm(price ~ carat, data = diamonds) summary(fit_carat) # 系数、置信区间和残差诊断 coef(fit_carat) confint(fit_carat, level = 0.95) par(mfrow = c(2, 2)) plot(fit_carat)

lm() 的公式 price ~ carat 表示用 carat 解释 price,coef() 取系数,confint() 给 95% 置信区间,plot() 一次给四张残差诊断图。carat 的 Estimate 才是每克拉边际价格,单位是美元/克拉。截距表示 carat 为 0 时的外推价格,业务上不能直接当真实价格,因为数据里没有 0 克拉钻石。残差图里的 Residuals vs Fitted 看非线性,Normal Q-Q 看残差正态性,Scale-Location 看异方差,Residuals vs Leverage 看杠杆点。

系数显著不等于模型可用。5 万多条样本下,标准误很小,p 值很容易小于 0.001。真正要问的是:残差有没有结构,预测误差在业务可接受范围内吗,高价样本有没有系统性低估。简单模型最大的问题不是系数不显著,而是它把非线性关系压成一条直线,残差里还留着 cut、color、clarity 的信息。

3.3 factor 与衍生变量:让 cut/color/clarity 进入模型

分类变量进回归不能直接放字符串,要转成 factor。R 的 formula 会自动把 factor 展开成 k-1 个哑变量,参照组默认是第一个水平。cut 的合理顺序是 Fair < Good < Very Good < Premium < Ideal,color 是 J 最差到 D 最好,clarity 是 I1 最差到 IF 最好。顺序不固定,参照组就会变成字母顺序,系数解释跟着乱。比如 color 按字母排序,D 可能排在前面,参照组变成 D,其他颜色的系数全是负值,读起来容易误判。

# 显式固定水平顺序,避免 R 默认按字母排序改变参照组 diamonds$cut <- factor(diamonds$cut, levels = c("Fair", "Good", "Very Good", "Premium", "Ideal")) diamonds$color <- factor(diamonds$color, levels = c("J", "I", "H", "G", "F", "E", "D")) diamonds$clarity <- factor(diamonds$clarity, levels = c("I1", "SI2", "SI1", "VS2", "VS1", "VVS2", "VVS1", "IF")) # 检查每个水平的样本量,稀有水平会让系数方差变大 table(diamonds$cut) table(diamonds$color) table(diamonds$clarity)

factor() 的 levels 参数决定水平顺序,也决定参照组。这里没有设 ordered=TRUE,是为了让 lm() 按哑变量展开;如果设成有序因子,R 默认可能用多项式对比,系数不再是“相对参照组的差异”。table() 用来看每个水平的样本量,Fair 和 D 色样本少,交互项里要特别小心。衍生变量 total、depth percentage 这类指标,落地时要保证分母不为零,x、y、z 有 0 值的记录要先处理,否则新变量会出 Inf 或 NaN。

3.4 残差诊断:异方差和杠杆点比 R² 更早报警

残差诊断是回归的“黑匣子”检查。拟合值 vs 残差如果呈弯曲趋势,说明模型漏了非线性项;如果呈漏斗形,说明异方差;如果少数点杠杆很大,说明它们对系数影响过强。diamonds 里大克拉高价样本就是高杠杆点,删掉它们系数会明显变化。杠杆点不一定是错误,但要在报告里说明。 Cook 距离可以量化单点影响,距离大的点要逐个看,不要批量删。

残差正态性在 5 万条样本上不是核心问题。中心极限定理保证系数近似正态,但预测区间对残差分布更敏感。价格右偏时,残差也右偏,直接用正态假设算预测区间会偏窄。更稳的做法是看残差 QQ 图,或者用 bootstrap 给预测区间。R² 只说明样本内拟合,残差图才说明模型设定。R² 高但残差有结构的模型,预测新样本时容易翻车。

4. 多元回归:哑变量、交互项与 AIC 逐步回归到底在优化什么

4.1 无交互多元模型:R²=0.8798 的残差里还剩什么

把 carat、cut、color、clarity、depth、table、x、y、z 一起放进线性模型,得到无交互多元回归。输入里 R²=0.8798,说明模型解释了约 88% 的价格方差。这个数字不算低,但也不够好,尤其对价格预测。残差里通常还剩三类信息:carat 的非线性、分类变量之间的交互、以及 x/y/z 与 carat 的共线性。R² 没解释的部分,不一定是噪声,可能是模型没写进去的结构。

无交互模型假设 cut、color、clarity 对价格的影响是常数,不随 carat 变化。现实里不是这样:一克拉以内的钻石,切工溢价可能被克拉数掩盖;大克拉钻石,颜色和净度的溢价更明显。交互项就是用来放松这个假设的。加交互项之前,先看无交互模型的残差是否按 cut、color、clarity 分组有系统性差异。如果有,交互项不是装饰,是修正设定。

共线性也要看。depth 和 x、y、z 有公式关系,x、y、z 又和 carat 高度相关。方差膨胀因子高的变量,系数符号可能反直觉。比如 depth 的系数可能为负,不代表深度越大价格越低,而是因为 depth 和 carat、x、y、z 纠缠在一起。解释系数时要控制其他变量不变,这句话在共线性强的时候几乎做不到。更稳的做法是删掉冗余变量,或者用 carat 加上分类变量做主模型,尺寸变量只做辅助。

4.2 哑变量与参照组:k 个水平只进 k-1 列

cut 有 5 个水平,进模型时生成 4 个哑变量;color 有 7 个水平,生成 6 个;clarity 有 8 个,生成 7 个。参照组不进入设计矩阵,其他水平的系数表示“相对参照组的价格差异”。参照组选谁,系数符号就跟着变。选 Fair 做 cut 参照,Ideal 的系数通常是正的;选 Ideal 做参照,Fair 的系数就是负的。两种写法模型拟合完全等价,但业务解释不同。写报告时要明确参照组,否则读者会以为模型变了。

# 无交互多元模型:分类变量已转为 factor,R 自动生成 k-1 个哑变量 fit_add <- lm(price ~ carat + cut + color + clarity + depth + table + x + y + z, data = diamonds) summary(fit_add) # 加入三个交互项:让 carat 的斜率随切工、颜色、净度变化 fit_int <- lm(price ~ carat + cut + color + clarity + depth + table + x + y + z + carat:cut + carat:color + carat:clarity, data = diamonds) summary(fit_int) # 向后逐步回归,AIC 默认 k=2;不要把它当因果筛选 fit_step <- step(fit_int, direction = "backward", trace = 0) summary(fit_step)

formula 里的+表示主效应,:表示交互项。carat:cut只加交互,主效应已经在前面加了。step() 的 direction="backward" 表示从全模型开始逐个删变量,trace=0 关闭逐步过程输出。AIC 默认 k=2,惩罚项是 2 倍参数个数。逐步回归选的是预测导向的变量组合,不是因果结构。样本量大时,AIC 容易保留很多弱效应变量,模型复杂度上升,解释成本也跟着上升。

注意:哑变量参照组变了,系数符号反转是正常现象,不是模型坏了。解释前先看 contrasts() 或 levels()。

4.3 交互项 carat:cut/color/clarity:价格斜率随等级改变

交互项的核心是让 carat 的斜率随分类变量变化。无交互模型里,carat 的系数是一个全局平均斜率;加carat:cut后,不同切工等级可以有不同斜率。输入里加了 carat:cut、carat:color、carat:clarity 三个交互项,然后做向后逐步回归。这个顺序合理:先放业务上最可能改变价格弹性的交互,再用 AIC 筛。不要一上来放所有二阶、三阶交互,5 万条数据也扛不住组合爆炸。

交互项的解释要小心。carat 主效应表示参照组水平下的每克拉边际价格,交互项系数表示该水平相对参照组的斜率差异。比如 cut 参照组是 Fair,Ideal 的交互项系数为正,说明 Ideal 钻石每增加一克拉,价格增长比 Fair 更快。这个解释只在模型设定正确时成立。如果 carat 和 cut 还有非线性关系,线性交互项只是近似。

交互项还会放大共线性。carat 和 carat:cut 高度相关,VIF 会升高。系数标准误变大,显著性下降,但模型整体拟合可能提升。这时候不要只看单个系数的 p 值,要看模型整体预测误差和残差结构。交互项显著不等于业务上重要,5 万条样本下,很小的斜率差异也可能显著。更稳的评估是留出测试集,看交互模型在测试集上的 RMSE 或 MAE 有没有实质下降。

4.4 向后逐步回归与 AIC:选择变量不是因果推断

AIC 的公式是 -2logL + 2k,logL 是模型对数似然,k 是参数个数。AIC 越小,说明模型在拟合和复杂度之间平衡得越好。向后逐步回归从全模型开始,每次删一个变量,看 AIC 是否下降,直到删不动为止。它适合在候选变量多的时候做粗筛,不适合当最终模型。逐步回归的搜索路径不唯一,换一个方向或换一个起始模型,结果可能不同。样本量大时,AIC 倾向保留更多变量,模型会变复杂。

输入里说所有变量都被选入模型,这在 5 万多条样本上不奇怪。样本量大,弱效应的标准误小,AIC 惩罚相对弱,变量容易留下来。但“留下来”不等于“重要”。要看系数大小、置信区间、业务可解释性,以及测试集误差。逐步回归还会忽略变量之间的组结构,cut、color、clarity 的哑变量是一组,删一个水平可能破坏整体解释。更稳的做法是按组做模型比较,或者用正则化回归做变量选择。

AIC 不是唯一标准。BIC 对复杂度惩罚更重,样本量大时倾向更简模型。交叉验证的预测误差更直接,但计算成本高。如果目标是解释,选参数少、系数稳定的模型;如果目标是预测,选测试误差低的模型。两个目标冲突时,先明确业务问题。钻石价格分析里,消费者更关心“同样预算下哪些属性更值”,这需要可解释的系数,不是黑箱预测。

4.5 非参数检验与可视化:分组差异如何反过来约束模型

输入里对不同切割、颜色、净度的钻石价格做了非参数检验,结论是分组价格有显著差异。这个结论和回归模型是互相印证的:分类变量进模型有解释力,非参数检验说明分组分布不同。非参数检验不假设正态,适合右偏价格。Kruskal-Wallis 检验的是分组分布位置是否相同,不要求方差齐性。p 值小只说明分布不同,不说明差异有多大,也不说明哪一组更贵。要结合中位数、四分位距和箱线图看。

# 价格右偏、方差不齐时,Kruskal-Wallis 比 ANOVA 更稳 kruskal.test(price ~ cut, data = diamonds) kruskal.test(price ~ color, data = diamonds) kruskal.test(price ~ clarity, data = diamonds) # 正态性检验在 5 万条样本上非常敏感,配合 QQ 图看 set.seed(1) sample_price <- sample(diamonds$price, 5000) shapiro.test(sample_price) qqnorm(sample_price); qqline(sample_price)

kruskal.test() 的公式是数值变量 ~ 分组变量,输出统计量和 p 值。Shapiro-Wilk 检验样本量太大时会过度敏感,所以这里抽样到 5000 条再看。set.seed() 保证抽样可复现。QQ 图比 p 值更直观:点偏离直线说明尾部不符合正态。正态性检验的常见误用是把“p 值大”当成“数据正态”,其实只说明没有足够证据拒绝正态。价格这种右偏分布,尾部本来就不正态,不必强行追求正态。

可视化在这一步不是装饰,是模型设定的证据。小提琴图看分布形状,箱线图看中位数和四分位,直方图看峰值和断层。理想切割的钻石里仍有低价样本,说明 cut 不是单独定价因素,低价可能来自小克拉、低颜色、低净度的组合。这个现象提醒模型:分类变量的主效应必须和 carat 一起看,否则会得出“理想切割不值钱”的错误结论。

# 小提琴图看分布形状,箱线图看中位数和四分位 ggplot(diamonds, aes(x = cut, y = price, fill = cut)) + geom_violin(trim = FALSE, alpha = 0.6) + geom_boxplot(width = 0.12, outlier.alpha = 0.1) + scale_y_log10() + labs(x = "切工", y = "价格(对数刻度)")

geom_violin() 的 trim=FALSE 保留尾部,alpha 控制透明度,geom_boxplot() 的 width 控制箱体宽度,scale_y_log10() 压缩高价尾部。对数刻度下看分布,峰值和断层会更清楚。箱线图的中位数接近下四分位,说明分布右偏;小提琴图的宽度表示密度,低价区宽说明样本多。把这些图和回归残差放在一起看,才能判断模型有没有漏掉分组结构。

5. 避坑与排查:钻石价格回归里最容易翻车的五个细节

5.1 价格上界口径不一致:18823 与 11886 不要混

现象:变量表里 price 上界写 18823,正文描述里又说最高 11886,后面画直方图、定异常值阈值、写预测区间时两个数字都出现过。原因:不同描述段落可能来自不同统计口径,一个是原始数据范围,一个是清洗后或抽样后的范围。解决:以summary(diamonds$price)的输出为准,在报告里统一写“原始数据范围”和“清洗后范围”,不要在同一段里混用。异常值阈值、模型预测上限、业务解释都要跟着统一口径走。价格上界错了,高价样本的预测评估会失真。

5.2 系数抄错:776063 与 7760.3 的单位陷阱

现象:简单线性回归的 carat 系数在文字里写成 776063,解释里又说每克拉增加 7760.3 美元。原因:小数点错位或单位换算错误,手抄 summary 输出时漏了小数点。解决:所有系数从coef()或summary()直接取,不手抄;在代码里加单位注释,比如“美元/克拉”;写报告时用sprintf()或round()统一格式。系数错一位,预测价格会差两个数量级,R² 还看不出来。血泪经验是:模型可以复杂,数字不能手抄。

5.3 异常值剔除过度:高价钻石被删,R² 变好看

现象:用全局箱线图 1.5 IQR 删异常值后,R² 上升,残差图变干净,但模型对高价钻石的预测明显偏低。原因:高价钻石被当成异常值删掉,训练集里高价样本变少,模型自然学不会高价区间。解决:先判断异常是录入错误还是真实稀有样本;按 carat 分层看异常;用残差和 Cook 距离找影响点,不要只用原始 price 的箱线图。高价样本是价格预测的目标之一,删之前要留证据。模型变好看不等于变有用。

5.4 哑变量参照组变了:系数符号反转不是模型坏了

现象:重新运行代码后,cut 的系数符号全反了,Ideal 从正变负,以为模型出错。原因:factor 的 levels 顺序变了,参照组从 Fair 变成 Ideal,系数含义跟着变。解决:在建模前显式固定 levels,写清楚参照组;用contrasts(diamonds$cut)检查设计矩阵;报告里注明“相对 Fair”或“相对 Ideal”。参照组变化不影响拟合值,只影响系数解释。不检查 levels 就解释系数,是分类变量回归里最常见的翻车点。

5.5 R² 高不等于预测准:交互项与逐步回归的过拟合

现象:加了交互项、跑了逐步回归,训练集 R² 很高,但拿新数据预测误差很大。原因:逐步回归用全部数据选变量,选变量过程本身消耗了自由度;交互项在稀有组合上过拟合;没有留出测试集。解决:先划分训练集和测试集,变量选择只在训练集做;用交叉验证估计预测误差;交互项只保留业务上可解释、样本量足够的组合;对比无交互模型和交互模型的测试集 RMSE。R² 是样本内指标,预测准不准要看样本外。5 万多条数据也不够挥霍,稀有分类组合切完就没了。

6. 设计取舍:把钻石价格预测拆成可验证的三层

钻石价格分析最后要落到取舍:是解释系数,还是预测价格。这两个目标对模型的要求不一样。解释导向要参数少、系数稳定、参照组清楚;预测导向要测试误差低,可以接受交互项和正则化。把任务拆成数据层、模型层、评估层,比一上来调模型更稳。

层级要验证的问题可落地做法失败信号
数据层有没有时间列,零尺寸和重复值怎么处理先 summary、缺失模式、零值计数;异常值按 carat 分层把横截面数据当时间序列,或全局删高价样本
模型层线性主效应够不够,交互项是否必要无交互模型做基线,交互模型做对比,固定 factor levels系数符号频繁反转,VIF 过高,稀有组合过拟合
评估层样本外误差和分组误差是否可接受训练/测试拆分,按 carat 分层评估,报告 MAE/RMSE只看训练集 R²,不看高价样本误差

这份数据没有时间列,所以“预测钻石价格走势”只能理解为不同品质组合的价格响应曲面,不是时间序列外推。把这点写清楚,分析就不会越界。模型层我一般会保留一个无交互模型做解释基线,再用交互模型看提升;如果交互模型在测试集上没有实质改进,就退回简单模型。评估层按 carat 分层看误差,避免整体 MAE 被小克拉样本拉低,掩盖高价样本的系统偏差。

从那以后我每次拿到这种带分类变量的价格数据,都强制先做三件事:确认有没有时间列、固定 factor levels、留出测试集再谈 R²。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进