在R中使用含分类变量的GAM模型预测的边际效应问题
问题根源与解决方案
你的问题出在模型结构设定错误:
model_1是针对John的单独数据拟合,所有平滑项s(speed)、s(length)、s(price)都是完全基于John的样本生成的专属曲线。- 而你写的
model_2只是给每个用户加了一个固定截距user_id,但三个平滑项是基于所有用户数据的全局统一曲线,相当于让John的预测结果是全局平滑曲线加上他的个体截距,这和model_1里John专属的平滑曲线完全不是一回事,结果自然不一样。
正确的模型设定(二选一)
要让全数据模型能输出和单独建模一致的John边际效应,必须让平滑项随user_id单独变化,mgcv里有两种常用方式:
1. 完全独立的用户专属平滑项
这种方式和给每个用户单独建模的逻辑最接近,只是把所有用户的模型放在一起拟合:
model_2 = mgcv::gam( y ~ user_id + s(speed, by = user_id) + s(length, by = user_id) + s(price, by = user_id), data = data2 )
这里的by = user_id会为每个用户生成独立的s(speed)、s(length)、s(price)平滑曲线,和model_1的拟合逻辑几乎一致。
2. 共享复杂度的因子平滑交互项(更稳健)
如果部分用户的数据量很少,完全独立的平滑项容易过拟合,可以用因子平滑交互项,让所有用户的平滑项共享相同的"平滑程度"(即平滑参数),但曲线的位置和形状可以不同:
model_2 = mgcv::gam( y ~ s(speed, user_id, bs = "fs") + s(length, user_id, bs = "fs") + s(price, user_id, bs = "fs"), data = data2 )
bs = "fs"表示Factor Smooth,这种设定会在保证平滑项稳健性的同时,保留用户间的个体差异。
预测步骤
用上述正确的model_2后,构造测试集的逻辑和之前一致:
# 先计算John的length和price均值 mean_john_length = mean(data2$length[data2$user_id == "John"], na.rm = TRUE) mean_john_price = mean(data2$price[data2$user_id == "John"], na.rm = TRUE) # 构造测试集 test_set2 = data.frame( user_id = rep("John", length(seq(0,100,by=5))), speed = seq(0,100,by=5), length = mean_john_length, price = mean_john_price ) # 获取预测结果 predictions_2 = mgcv::predict.gam(model_2, test_set2, type="response")
此时predictions_2就会和prediction_1的结果一致(或非常接近,取决于拟合的微小差异)。
用ggeffects验证
如果用ggeffects画图,直接指定用户和控制变量即可:
plot(ggeffects::ggpredict( model_2, terms = c("speed [0:100 by=5]", "user_id [John]"), condition = c(length = mean_john_length, price = mean_john_price) ), facets = TRUE)
内容的提问来源于stack exchange,提问作者Telis
相关产品推荐
相关产品推荐

