R语言lm()回归斜率截距与绘图结果不符问题咨询
问题根源
斜率和截距的认知偏差完全来自zoo包yearmon类型的内部编码规则,和建模、绘图本身的计算逻辑无关:
as.yearmon()生成的日期对象,内部以距离公元0年的年数存储数值,相邻两个月份的实际数值差为1/12 ≈ 0.083,而非你目视x轴刻度默认认为的“相邻月份数值差为1”。可以执行as.numeric(df$a)直接查看内部值,你所用的5个时间点对应数值为2022.000、2022.083、2022.167、2022.250、2022.333。lm()输出的斜率-1.2,含义是x每增加1个内部单位(即时间过去1整年)时,y的平均变化量。换算为月度变化量为-1.2 * (1/12) = -0.1,这个数值就是你看ggplot图时,x轴每走1个月份刻度对应的拟合线斜率,和图上平缓的拟合趋势完全匹配。- 截距的逻辑同理:
lm()输出的截距是x=0时的y预测值,对应内部数值0也就是公元0年的y估计值,和你图上显示的2022年区间的拟合线位置自然差距极大。 - ggplot的
stat_smooth调用的是和你完全相同的lm()计算逻辑,不存在绘图和建模结果不一致的问题,偏差纯粹来自视觉判断时对x轴数值单位的误判。
验证方法
执行以下代码可以直接核对计算逻辑和可视化结果的一致性:
# 查看yearmon的内部存储值 as.numeric(df$a) # 提取lm拟合结果 lm_fit <- lm(b ~ a, data = df) # 换算为月度变化斜率,和目视结果一致 unname(coef(lm_fit)["a"] / 12) # 输出为-0.1 # 计算2022年1月(拟合线最左端)的y预测值,和图上位置匹配 predict(lm_fit, newdata = data.frame(a = as.yearmon("2022-01"))) # 输出约1.6
调整方法
如果需要lm()直接输出对应“每1个月变化量”的斜率、以及和观测区间匹配的截距,可以提前将x转换为以月份为单位的数值变量:
# 生成以首个观测月为0点的月份计数变量 df$month_gap <- as.numeric(df$a - min(df$a)) * 12 # 此时建模输出的斜率直接对应月度变化,截距为首个观测月的y预测值 lm(b ~ month_gap, data = df) # 绘图时同步使用转换后的变量即可让轴刻度、建模系数、视觉判断完全统一 ggplot(df, aes(x = month_gap, y = b)) + geom_point() + stat_smooth(method = lm) + labs(x = "距离2022年1月的月份数")
内容的提问来源于stack exchange,提问作者Katharina Böhm
相关产品推荐
相关产品推荐

