如何将复杂的方程放入R公式?

Posted

技术标签:

【中文标题】如何将复杂的方程放入R公式?【英文标题】:How to put a complicated equation into a R formula? 【发布时间】:2013-02-10 23:12:42 【问题描述】:

我们将树木的直径作为预测变量,将树木高度作为因变量。此类数据存在许多不同的方程,我们尝试对其中的一些进行建模并比较结果。

但是,我们无法弄清楚如何正确地将一个方程式转换为相应的Rformula 格式。

可以以R中的trees数据集为例。

data(trees)
df <- trees
df$h <- df$Height * 0.3048   #transform to metric system
df$dbh <- (trees$Girth * 0.3048) / pi   #transform tree girth to diameter

首先,一个似乎运行良好的方程示例:

form1 <- h ~ I(dbh ^ -1) + I( dbh ^ 2)  
m1 <- lm(form1, data = df)
m1

Call:
lm(formula = form1, data = df)

Coefficients:
(Intercept)    I(dbh^-1)     I(dbh^2)  
27.1147      -5.0553       0.1124  

估计系数abc,这是我们感兴趣的。

现在有问题的方程式:

试着像这样适应它:

form2 <- h ~ I(dbh ^ 2) / dbh + I(dbh ^ 2) + 1.3

给出一个错误:

m1 <- lm(form2, data = df)
Error in terms.formula(formula, data = data) 
invalid model formula in ExtractVars

我猜这是因为/ 被解释为嵌套模型而不是算术运算符?

这不会出错:

form2 <- h ~ I(I(dbh ^ 2) / dbh + I(dbh ^ 2) + 1.3)
m1 <- lm(form2, data = df)

但结果不是我们想要的:

m1
Call:
lm(formula = form2, data = df)

Coefficients:
(Intercept)  I(I(dbh^2)/dbh + I(dbh^2) + 1.3)  
19.3883                            0.8727  

外层I()内的整项只给出一个系数,这似乎是合乎逻辑的。

我们如何将第二个方程拟合到我们的数据中?

【问题讨论】:

【参考方案1】:

你有几个问题。 (1) 您缺少form2 分母的括号(并且 R 无法知道您想在分母中添加常量 a,或者在哪里放置任何参数,真的),并且更多问题:(2) 你的第二个模型不是线性的,所以lm 不会工作。

修复 (1) 很容易:

form2 <- h ~ 1.3 + I(dbh^2) / (a + b * dbh + c * I(dbh^2))

修正 (2),虽然有很多方法可以估计非线性模型的参数,但 nls(非线性最小二乘法)是一个很好的起点:

m2 <- nls(form2, data = df, start = list(a = 1, b = 1, c = 1))

您需要为nls 中的参数提供起始猜测值。我刚刚选择了 1,但您应该更好地猜测参数可能是什么。

【讨论】:

感谢您的回答!我们需要很长时间才能发现这些问题,甚至更长时间才能找到解决方案。【参考方案2】:

编辑已修复,不再错误地使用偏移量...

补充@shujaa 的答案:

你可以改变你的问题

H = 1.3 + D^2/(a+b*D+c*D^2)

1/(H-1.3) = a/D^2+b/D+c

这通常会打乱模型的假设(即,如果 H 是正态分布且方差不变,那么 1/(H-1.3) 就不会是这样。不过,我们还是试试吧:

data(trees)
df <- transform(trees,
            h=Height * 0.3048,   #transform to metric system
            dbh=Girth * 0.3048 / pi   #transform tree girth to diameter
            )
lm(1/(h-1.3) ~ poly(I(1/dbh),2,raw=TRUE),data=df)

## Coefficients:
##                    (Intercept)  poly(I(1/dbh), 2, raw = TRUE)1  
##                       0.043502                       -0.006136  
## poly(I(1/dbh), 2, raw = TRUE)2  
##                       0.010792  

这些结果通常足以为nls 拟合获得良好的起始值。但是,您可以通过glm 做得更好,它使用链接函数来允许某些形式的非线性。具体来说,

(fit2 <- glm(h-1.3 ~ poly(I(1/dbh),2,raw=TRUE),
             family=gaussian(link="inverse"),data=df))

## Coefficients:
##                    (Intercept)  poly(I(1/dbh), 2, raw = TRUE)1  
##                       0.041795                       -0.002119  
## poly(I(1/dbh), 2, raw = TRUE)2  
##                       0.008175  
## 
## Degrees of Freedom: 30 Total (i.e. Null);  28 Residual
## Null Deviance:       113.2 
## Residual Deviance: 80.05     AIC: 125.4 
## 

您可以看到结果大约与线性拟合相同,但不完全一样。

pframe <- data.frame(dbh=seq(0.8,2,length=51))

我们使用predict,但需要更正预测以说明我们从 LHS 中减去了一个常数:

pframe$h <- predict(fit2,newdata=pframe,type="response")+1.3
p2 <- predict(fit2,newdata=pframe,se.fit=TRUE) ## predict on link scale
pframe$h_lwr <- with(p2,1/(fit+1.96*se.fit))+1.3
pframe$h_upr <- with(p2,1/(fit-1.96*se.fit))+1.3
png("dbh_tmp1.png",height=4,width=6,units="in",res=150)
par(las=1,bty="l")
plot(h~dbh,data=df)
with(pframe,lines(dbh,h,col=2))
with(pframe,polygon(c(dbh,rev(dbh)),c(h_lwr,rev(h_upr)),
      border=NA,col=adjustcolor("black",alpha=0.3)))
dev.off()

因为我们在 LHS 上使用了常量(这几乎但不完全适合使用 offset 的框架——如果我们的公式为 @,我们只能使用偏移量987654334@,即如果不断调整是在链接(反向)比例而不是原始比例上),这不完全适合ggplotgeom_smooth框架

library("ggplot2")
ggplot(df,aes(dbh,h))+geom_point()+theme_bw()+
   geom_line(data=pframe,colour="red")+
   geom_ribbon(data=pframe,colour=NA,alpha=0.3,
             aes(ymin=h_lwr,ymax=h_upr))

ggsave("dbh_tmp2.png",height=4,width=6)

【讨论】:

【参考方案3】:

假设您使用的是nls,R 公式可以使用普通的 R 函数 H(a, b, c, D),因此公式可以只是 h ~ H(a, b, c, dbh),这样可以:

# use lm to get startingf values
lm1 <- lm(1/(h - 1.3) ~ I(1/dbh) + I(1/dbh^2), df)
start <- rev(setNames(coef(lm1), c("c", "b", "a")))

# run nls
H <- function(a, b, c, D) 1.3 + D^2 / (a + b * D + c * D^2)
nls1 <- nls(h ~ H(a, b, c, dbh), df, start = start)

nls1 # display result

绘制输出:

plot(h ~ dbh, df)
lines(fitted(nls1) ~ dbh, df)

【讨论】:

我会将这个答案标记为正确答案,因为 a) 它包括如何估计起始值,b) 使用普通的 R 函数允许我们非常容易地拟合其他非线性函数 c) 它绘制结果。谢谢!

以上是关于如何将复杂的方程放入R公式?的主要内容,如果未能解决你的问题,请参考以下文章

多元线性回归的计算公式是怎样的?

如何将方程转换为单个变量的公式?

lm方程的公式

牛顿迭代法的牛顿迭代公式

euler公式

如何在简书Markdown中输入数学公式