用多个左手变量拟合线性模型

8

我刚开始学习R语言,希望通过使用*apply函数来改进下面的脚本(我已经阅读了关于apply的内容,但是我无法使用它)。我想在多个自变量上(这些自变量是数据框中的列)使用lm函数。

for (i in (1:3) {
  assign(paste0('lm.',names(data[i])), lm(formula=formula(i),data=data))
  } 

Formula(i)的定义如下:

formula=function(x)
{
  as.formula ( paste(names(data[x]),'~', paste0(names(data[-1:-3]), collapse = '+')), env=parent.frame() )
}

谢谢。

1个回答

19
如果我没理解错的话,您正在处理类似于这样的数据集:
set.seed(0)
dat <- data.frame(y1 = rnorm(30), y2 = rnorm(30), y3 = rnorm(30),
                  x1 = rnorm(30), x2 = rnorm(30), x3 = rnorm(30))

x1x2x3是协变量,y1y2y3是三个独立的响应。您正在尝试拟合三个线性模型:

y1 ~ x1 + x2 + x3
y2 ~ x1 + x2 + x3
y3 ~ x1 + x2 + x3

目前您正在使用一个循环通过y1, y2, y3,每次拟合一个模型。您希望通过用lapply替换for循环来加速此过程。

您走错了方向。 lm()是一项昂贵的操作。只要您的数据集不小,for循环的成本可以忽略不计。用lapply替换for循环没有性能上的提升。

由于所有三个模型的RHS(~右侧)相同,因此三个模型的模型矩阵相同。因此,所有模型的QR分解只需要进行一次。lm允许这样做,您可以使用:

fit <- lm(cbind(y1, y2, y3) ~ x1 + x2 + x3, data = dat)
#Coefficients:
#             y1         y2         y3       
#(Intercept)  -0.081155   0.042049   0.007261
#x1           -0.037556   0.181407  -0.070109
#x2           -0.334067   0.223742   0.015100
#x3            0.057861  -0.075975  -0.099762

如果您检查str(fit),您会发现这不是三个线性模型的列表;相反,它是一个具有单个$qr对象但具有多个左侧变量的单个线性模型。因此,$coefficients$residuals$fitted.values都是矩阵。除了通常的“lm”类别外,得到的线性模型还具有额外的“mlm”类别。我创建了一个特殊的标签来收集关于该主题的一些问题,并在其tag wiki中进行了总结。
如果您有更多的协变量,您可以通过使用.来避免输入或粘贴公式:
fit <- lm(cbind(y1, y2, y3) ~ ., data = dat)
#Coefficients:
#             y1         y2         y3       
#(Intercept)  -0.081155   0.042049   0.007261
#x1           -0.037556   0.181407  -0.070109
#x2           -0.334067   0.223742   0.015100
#x3            0.057861  -0.075975  -0.099762

注意:不要编写

y1 + y2 + y3 ~ x1 + x2 + x3

这将把y = y1 + y2 + y3视为单个响应。使用cbind()

跟进:

我对一般化很感兴趣。我有一个数据框 df,其中前 n 列是因变量 (y1,y2,y3,....),接下来的 m 列是自变量 (x1+x2+x3+....)。 对于 n = 3m = 3,它是 fit <- lm(cbind(y1, y2, y3) ~ ., data = dat))。但如何通过使用 df 的结构自动执行此操作。我的意思是类似于 (for i in (1:n)) fit <- lm(cbind(df[something] ~ df[something], data = dat))。我使用 pastepaste0 创建了“something”。谢谢。

所以你正在编写公式,或者想在循环中动态生成/构建模型公式。有许多方法可以做到这一点,并且许多 Stack Overflow 问题都与此有关。通常有两种方法:

  1. 使用reformulate重新表述;
  2. 使用paste / paste0formula / as.formula

我更喜欢reformulate的简洁性,但它不支持公式中有多个LHS。 如果您想转换LHS,则还需要进行一些特殊处理。因此,在下面我将使用paste解决方案。

对于您的数据框df,您可以执行以下操作:

paste0("cbind(", paste(names(df)[1:n], collapse = ", "), ")", " ~ .")

更好的方式是使用 sprintftoString 来构建左侧值(LHS):
sprintf("cbind(%s) ~ .", toString(names(df)[1:n]))

这是一个使用iris数据集的示例:
string_formula <- sprintf("cbind(%s) ~ .", toString(names(iris)[1:2]))
# "cbind(Sepal.Length, Sepal.Width) ~ ."

你可以将这个字符串公式传递给lm,因为lm会自动将其转换为公式类。或者你可以使用formula(或as.formula)自己进行转换:
formula(string_formula)
# cbind(Sepal.Length, Sepal.Width) ~ .

备注:

在R核心中,也支持多个LHS公式的使用:


我应该强调 glm 不能处理多个左侧变量。GLM 迭代地拟合重新加权的线性回归模型,其中权重取决于响应变量。显然,两个不同的左侧变量会给出两组不同的权重,因此会产生两个不同的加权模型矩阵。因此,无法进行常规的 QR 因式分解。 使用多个响应和权重运行 lm 呈现了非常类似的情境。 - Zheyuan Li

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接