如果我没理解错的话,您正在处理类似于这样的数据集:
set.seed(0)
dat <- data.frame(y1 = rnorm(30), y2 = rnorm(30), y3 = rnorm(30),
x1 = rnorm(30), x2 = rnorm(30), x3 = rnorm(30))
x1
、x2
和x3
是协变量,y1
、y2
和y3
是三个独立的响应。您正在尝试拟合三个线性模型:
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)
如果您检查
str(fit)
,您会发现这不是三个线性模型的列表;相反,它是一个具有单个
$qr
对象但具有多个左侧变量的单个线性模型。因此,
$coefficients
、
$residuals
和
$fitted.values
都是矩阵。除了通常的“lm”类别外,得到的线性模型还具有额外的“mlm”类别。我创建了一个特殊的
mlm标签来收集关于该主题的一些问题,并在其
tag wiki中进行了总结。
如果您有更多的协变量,您可以通过使用
.
来避免输入或粘贴公式:
fit <- lm(cbind(y1, y2, y3) ~ ., data = dat)
注意:不要编写
y1 + y2 + y3 ~ x1 + x2 + x3
这将把
y = y1 + y2 + y3
视为单个响应。使用
cbind()
。
跟进:
我对一般化很感兴趣。我有一个数据框 df
,其中前 n
列是因变量 (y1,y2,y3,....)
,接下来的 m
列是自变量 (x1+x2+x3+....)
。 对于 n = 3
和 m = 3
,它是 fit <- lm(cbind(y1, y2, y3) ~ ., data = dat))
。但如何通过使用 df
的结构自动执行此操作。我的意思是类似于 (for i in (1:n)) fit <- lm(cbind(df[something] ~ df[something], data = dat))
。我使用 paste
和 paste0
创建了“something”。谢谢。
所以你正在编写公式,或者想在循环中动态生成/构建模型公式。有许多方法可以做到这一点,并且许多 Stack Overflow 问题都与此有关。通常有两种方法:
- 使用
reformulate
重新表述;
- 使用
paste
/ paste0
和formula
/ as.formula
。
我更喜欢reformulate
的简洁性,但它不支持公式中有多个LHS。 如果您想转换LHS,则还需要进行一些特殊处理。因此,在下面我将使用paste
解决方案。
对于您的数据框df
,您可以执行以下操作:
paste0("cbind(", paste(names(df)[1:n], collapse = ", "), ")", " ~ .")
更好的方式是使用
sprintf
和
toString
来构建左侧值(LHS):
sprintf("cbind(%s) ~ .", toString(names(df)[1:n]))
这是一个使用
iris
数据集的示例:
string_formula <- sprintf("cbind(%s) ~ .", toString(names(iris)[1:2]))
你可以将这个字符串公式传递给
lm
,因为
lm
会自动将其转换为公式类。或者你可以使用
formula
(或
as.formula
)自己进行转换:
formula(string_formula)
备注:
在R核心中,也支持多个LHS公式的使用:
glm
不能处理多个左侧变量。GLM 迭代地拟合重新加权的线性回归模型,其中权重取决于响应变量。显然,两个不同的左侧变量会给出两组不同的权重,因此会产生两个不同的加权模型矩阵。因此,无法进行常规的 QR 因式分解。 使用多个响应和权重运行 lm 呈现了非常类似的情境。 - Zheyuan Li