lm()和predict.lm()的奇怪行为取决于显式命名空间访问器的使用

mb7*_*744 7 r spline predict lm

我感兴趣的是lm函数的一些令人不安的行为以及predict.lmR中的相关函数.splines基础包提供了bs生成b样条展开的函数,然后可以使用lm多功能线性模型拟合函数来拟合样条模型.

这些lmpredict.lm函数具有很多内置的便利性,可以利用公式和术语.如果调用bs()嵌套在lm调用内,则用户可以提供单变量数据predict,并且此数据将自动扩展为适当的b样条基础.然后将照常预测这种扩展的数据矩阵.

library(splines)

x <- sort(runif(50, 0, 10))
y <- x^2

splineModel <- lm(y ~ bs(x, y, degree = 3, knots = c(3, 6)))

newData <- data.frame(x = 4)
prediction <- predict(splineModel, newData) # 16

plot(x, y)
lines(x, splineModel$fitted.values, col = 'blue3')
points(newData$x, prediction, pch = 3, cex = 3, col = 'red3')
legend("topleft", legend = c("Data", "Fitted Values", "Predicted Value"),
       pch = c(1, NA, 3), col = c('black', 'blue3', 'red3'), lty = c(NA, 1, NA))
Run Code Online (Sandbox Code Playgroud)

如我们所见,这完美地运作:

在此输入图像描述

当使用::运算符明确指示bs函数是从splines包的命名空间导出时,会发生异常.以下代码段除了该更改外完全相同:

library(splines)

x <- sort(runif(50, 0, 10))
y <- x^2

splineModel <- lm(y ~ splines::bs(x, y, degree = 3, knots = c(3, 6)))

newData <- data.frame(x = 4) 
prediction <- predict(splineModel, newData) # 6.40171

plot(x, y)
lines(x, splineModel$fitted.values, col = 'blue3')
points(newData$x, prediction, pch = 3, cex = 3, col = 'red3')
legend("topleft", legend = c("Data", "Fitted Values", "Predicted Value"),
       pch = c(1, NA, 3), col = c('black', 'blue3', 'red3'), lty = c(NA, 1, NA))
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

如果splines从不library首先使用包附加在第二个片段中,则会产生完全相同的结果.我想不出另一种情况,即在::已经加载的包上使用运算符会改变程序行为.

使用其他函数(splines如自然样条基础实现)会产生相同的行为ns.有趣的是,在这两种情况下,"y hat"或拟合值都是合理的并且相互匹配.据我所知,拟合的模型对象除属性名称外是相同的.

我无法确定此行为的来源.虽然这可能看起来像一个错误报告,但我的问题

  1. 为什么会这样?我一直试图贯彻predict.lm但不能确定分歧发生的地方.
  2. 这是某种预期的行为,如果是这样,我可以在哪里了解更多信息?

MrF*_*ick 6

所以问题是模型需要跟踪用原始数据计算的结,并在预测新数据时使用这些值.这通常发生在model.frame()呼叫内部的lm()呼叫中.该bs()函数返回一个类,"bs"在创建model.frame时,将调度该列splines:::makepredictcall.bs以尝试捕获边界结.(你可以看到函数makepredictcall中的model.frame.default调用.)

但是,如果我们比较结果

splineModel1 <- lm(y ~ bs(x, y, degree = 3, knots = c(3, 6)))
attr(terms(splineModel1), "predvar")
# list(y, bs(x, degree = 3L, knots = c(3, 6), Boundary.knots =  c(0.275912734214216, 
# 9.14309860439971), intercept = FALSE))

splineModel2 <- lm(y ~ splines::bs(x, y, degree = 3, knots = c(3, 6)))
attr(terms(splineModel2), "predvar")
# list(y, splines::bs(x, y, degree = 3, knots = c(3, 6)))
Run Code Online (Sandbox Code Playgroud)

请注意第二个如何捕获Boundary.knots.这是因为splines:::makepredictcall.bs实际查看调用名称的功能

function (var, call) {
    if (as.character(call)[1L] != "bs") 
        return(call)
    ...
}
Run Code Online (Sandbox Code Playgroud)

当您splines::bs在公式中使用时,则as.character(call)[1L]返回"splines::bs"不匹配的内容,"bs"以便不会发生任何事情.我不清楚为什么这个检查在那里.似乎方法调度应该足以假设它是一个bs对象.

在我看来,这似乎不是想要的行为,可能应该修复.但是,bs()如果不加载包,则不应该真正调用该函数,因为makepredictcall.bs不会导入类似函数,因此会破坏对这些对象的自定义调度.