解释样条结果


20

我正在尝试使用R拟合GLM的样条曲线。一旦拟合出样条曲线,我希望能够获取生成的模型并在Excel工作簿中创建建模文件。

例如,假设我有一个数据集,其中y是x的随机函数,并且斜率在特定点处突然变化(在这种情况下,x = 500)。

set.seed(1066)
x<- 1:1000
y<- rep(0,1000)

y[1:500]<- pmax(x[1:500]+(runif(500)-.5)*67*500/pmax(x[1:500],100),0.01)
y[501:1000]<-500+x[501:1000]^1.05*(runif(500)-.5)/7.5

df<-as.data.frame(cbind(x,y))

plot(df)

我现在使用

library(splines)
spline1 <- glm(y~ns(x,knots=c(500)),data=df,family=Gamma(link="log"))

我的结果显示

summary(spline1)

Call:
glm(formula = y ~ ns(x, knots = c(500)), family = Gamma(link = "log"), 
    data = df)

Deviance Residuals: 
     Min       1Q   Median       3Q      Max  
-4.0849  -0.1124  -0.0111   0.0988   1.1346  

Coefficients:
                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)             4.17460    0.02994  139.43   <2e-16 ***
ns(x, knots = c(500))1  3.83042    0.06700   57.17   <2e-16 ***
ns(x, knots = c(500))2  0.71388    0.03644   19.59   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for Gamma family taken to be 0.1108924)

    Null deviance: 916.12  on 999  degrees of freedom
Residual deviance: 621.29  on 997  degrees of freedom
AIC: 13423

Number of Fisher Scoring iterations: 9

此时,我可以在r中使用预测函数并获得完全可接受的答案。问题是我想使用模型结果在Excel中构建工作簿。

我对预测函数的理解是,给定新的“ x”值,r将新的x插入适当的样条函数(值大于500的函数或值小于500的函数),然后取该结果并相乘通过适当的系数进行计算,从这一点来看,它像其他任何模型项一样对待。如何获得这些样条函数?

(注意:我意识到对数链接的伽马GLM可能不适用于所提供的数据集。我没有在询问如何或何时安装GLM。我将其作为示例,以实现可重复性。)


7
我建议,如果可能的话,请避免包括删除所有变量(rm(list=ls()))的代码,尤其是在没有任何警告的情况下。有人可能你的代码复制粘贴到R的公开会议,他们已经有些变量(但没有叫xydfspline1)和思念你的代码打掉他们的工作。他们这样做有点愚蠢吗?是。但是让他们决定何时删除自己的变量还是有礼貌的。
Glen_b-恢复莫妮卡2014年

Answers:


25

您可以对样条线公式进行反向工程,而无需进入R代码。 知道就足够了

  • 样条曲线是分段多项式函数。

  • dd+1

  • 多项式的系数可以通过线性回归获得。

d+1xxdd=34×4=16d+1=4x

64RR

此方法适用于任何统计软件,甚至包括源代码不可用的未记录专有软件。

200,500,800(1,1000)RR

R图

Excel图

(版本中的垂直灰色网格线R显示了内部结的位置。)


这是完整的R代码。这是一个简单的技巧,完全依赖于paste功能来完成字符串操作。(一种更好的方法是创建一个公式模板,并使用字符串匹配和替换命令将其填写。)

#
# Create and display a spline basis.
#
x <- 1:1000
n <- ns(x, knots=c(200, 500, 800))

colors <- c("Orange", "Gray", "tomato2", "deepskyblue3")
plot(range(x), range(n), type="n", main="R Version",
     xlab="x", ylab="Spline value")
for (k in attr(n, "knots")) abline(v=k, col="Gray", lty=2)
for (j in 1:ncol(n)) {
  lines(x, n[,j], col=colors[j], lwd=2)
}
#
# Export this basis in Excel-readable format.
#
ns.formula <- function(n, ref="A1") {
  ref.p <- paste("I(", ref, sep="")
  knots <- sort(c(attr(n, "Boundary.knots"), attr(n, "knots")))
  d <- attr(n, "degree")
  f <- sapply(2:length(knots), function(i) {
    s.pre <- paste("IF(AND(", knots[i-1], "<=", ref, ", ", ref, "<", knots[i], "), ", 
                   sep="")
    x <- seq(knots[i-1], knots[i], length.out=d+1)
    y <- predict(n, x)
    apply(y, 2, function(z) {
      s.f <- paste("z ~ x+", paste("I(x", 2:d, sep="^", collapse=")+"), ")", sep="")
      f <- as.formula(s.f)
      b.hat <- coef(lm(f))
      s <- paste(c(b.hat[1], 
            sapply(1:d, function(j) paste(b.hat[j+1], "*", ref, "^", j, sep=""))), 
            collapse=" + ")
      paste(s.pre, s, ", 0)", sep="")
    })
  })
  apply(f, 1, function(s) paste(s, collapse=" + "))
}
ns.formula(n) # Each line of this output is one basis formula: paste into Excel

第一个样条输出公式(此处产生的四个样条输出公式)为

"IF(AND(1<=A1, A1<200), -1.26037447288906e-08 + 3.78112341937071e-08*A1^1 + -3.78112341940948e-08*A1^2 + 1.26037447313669e-08*A1^3, 0) + IF(AND(200<=A1, A1<500), 0.278894459758071 + -0.00418337927419299*A1^1 + 2.08792741929417e-05*A1^2 + -2.22580643138594e-08*A1^3, 0) + IF(AND(500<=A1, A1<800), -5.28222778473101 + 0.0291833541927414*A1^1 + -4.58541927409268e-05*A1^2 + 2.22309136420529e-08*A1^3, 0) + IF(AND(800<=A1, A1<1000), 12.500000000002 + -0.0375000000000067*A1^1 + 3.75000000000076e-05*A1^2 + -1.25000000000028e-08*A1^3, 0)"

Rxx

Excel片段


2
ns.formula..你觉得 R吗?认真的说,尽管您的方法看起来非常有用,但是讽刺的是必须要破解才能获取这些参数。将输出表非常有用的..
geotheory

这可能是一个愚蠢的问题:但是您要绘制的是4个样条线,还是一个样条的4个基数?
Erosennin

@Erosennin我取决于您所说的“一个样条曲线”。这四个曲线是样条曲线的基础,该样条曲线在四个间隔中是分段三次的,并且在这些间隔相交的三个点处连续第二微分,如介绍我的答案的三个要点所描述。
Whuber

谢谢!我并不是故意挑剔,它只是看起来有四个样条曲线(从答案中得出),而没有四个曲线作为基础。同样,我只是在这里试图了解...
Erosennin

1
@Erosennin没问题。也许会有所帮助:“样条曲线”是这四个曲线的线性组合,由回归拟合过程确定。另一种表达方式:样条曲线由曲线的向量空间组成,可以通过对这四个曲线进行线性组合来创建曲线。
Whuber

4

您已经执行了以下操作:

> rm(list=ls())
> set.seed(1066)
> x<- 1:1000
> y<- rep(0,1000)
> y[1:500]<- pmax(x[1:500]+(runif(500)-.5)*67*500/pmax(x[1:500],100),0.01)
> y[501:1000]<-500+x[501:1000]^1.05*(runif(500)-.5)/7.5
> df<-as.data.frame(cbind(x,y))
> library(splines)
> spline1 <- glm(y~ns(x,knots=c(500)),data=df,family=Gamma(link="log"))
> 

现在,我将向您展示如何以两种不同的方式预测x = 12的(响应):首先使用预测函数(简单的方法!)

> new.dat=data.frame(x=12)
> predict(spline1,new.dat,type="response")
       1 
68.78721 

第二种方法直接基于模型矩阵。注意我使用,exp因为使用的链接函数是log。

> m=model.matrix( ~ ns(df$x,knots=c(500))) 
> prd=exp(coefficients(spline1) %*% t(m)) 
> prd[12]
[1] 68.78721

请注意,在上面我提取了第12个元素,因为它对应于x = 12。如果要预测训练集之外的x,则只需再次使用预测函数即可。假设我们要找到x = 1100的预测响应值,然后

> predict(spline1, newdata=data.frame(x=1100),type="response")
       1 
366.3483 

感谢您的答复!但是,我仍然很困惑:/。我不确定我知道如何处理此矩阵。例如,如果我有x = 12,则预测说y = 68.78721,但是从该矩阵中查找12则得到0.016816392。x <500的原始截距和系数分别为4.174603和3.830416。exp(4.174603 + 3.8304116 * 0.016816392)<> 68.78721 另外,如果x不在训练集中,我将如何获得x的值?
埃里克

我改变了答案。
Stat 2014年

我为x不在训练集中的情况添加了代码。
2014年

2
有没有办法在不使用预测函数的情况下获得x = 1100的366.3483?
埃里克

4

您可能会发现使用R rms包更容易将截断幂基础用于三次回归样条。拟合模型后,您可以使用中的Functionlatex函数检索拟合样条函数的代数表示rms


谢谢。在发布之前,我实际上在这里阅读了您的回复stats.stackexchange.com/questions/67607/…。我想我只需要更好地了解我可以使用rms做些什么。
埃里克

的文档Function()并未真正说明它的作用。在我的情况(见Rpubs细节rpubs.com/EmilOWK/rms_splines),我得到function(x = NA) {-2863.7787+245.72672* x-0.1391794*pmax(x-10.9,0)^3+0.27835881*pmax(x-50.5,0)^3-0.1391794*pmax(x-90.1,0)^3 } <environment: 0x556156e80db8>-2863.7787值是在模型中,第一COEF 245.72672第二,最后COEF -873.0223没有在任何地方公式可见。的输出也是如此latex()
德莱特(Deleet)

Function有工作Glm()时,您使用rcs的样条函数。输出将以最简单的形式来重述样条,就像我的RMS课程笔记中详细说明的那样,好像线性尾部限制不存在(但它们存在)。
弗兰克·哈雷尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.