如何在公式中使用字符串引用变量?

55
在下面的简单示例中,我试图在回归公式中使用字符字符串vars的值。然而,我只能将变量名的字符串("v2+v3+v4")传递给公式,而不能传递该字符串的真实含义(例如, "v2"是dat $v2)。
我知道有更好的运行回归的方法(例如,lm(v1〜v2 + v3 + v4,data = dat))。但我的情况更加复杂,我正在尝试找出如何在公式中使用字符字符串。有什么想法吗?
下面是更新后的代码:
# minimal example 
# create data frame
v1 <- rnorm(10)
v2 <- sample(c(0,1), 10, replace=TRUE)
v3 <- rnorm(10)
v4 <- rnorm(10)
dat <- cbind(v1, v2, v3, v4)
dat <- as.data.frame(dat)

# create objects of column names
c.2 <- colnames(dat)[2]
c.3 <- colnames(dat)[3]
c.4 <- colnames(dat)[4]

# shortcut to get to the type of object my full code produces
vars <- paste(c.2, c.3, c.4, sep="+")

### TRYING TO SOLVE FROM THIS POINT:
print(vars)
# [1] "v2+v3+v4"

# use vars in regression
regression <- paste0("v1", " ~ ", vars)
m1 <- lm(as.formula(regression), data=dat)

更新: 在第一个示例中,@Arun对v1缺少""的指出是正确的。这纠正了我的示例,但我在我的真实代码中仍然遇到问题。在下面的代码块中,我调整了我的示例以更好地反映我的实际代码。起初,我选择创建一个更简单的示例,认为问题是字符串vars

下面是一个不起作用的示例 :) 使用上面创建的相同数据框dat

dv <- colnames(dat)[1]
r2 <- colnames(dat)[2]
# the following loop creates objects r3, r4, r5, and r6
# r5 and r6 are interaction terms
for (v in 3:4) {
  r <- colnames(dat)[v]
  assign(paste("r",v,sep=""),r)
  r <- paste(colnames(dat)[2], colnames(dat)[v], sep="*")
  assign(paste("r",v+2,sep=""),r)
}

# combine r3, r4, r5, and r6 then collapse and remove trailing +
vars2 <- sapply(3:6, function(i) { 
                paste0("r", i, "+")
                })
vars2 <- paste(vars2, collapse = '')
vars2 <- substr(vars2, 1, nchar(vars2)-1)

# concatenate dv, r2 (as a factor), and vars into `eq`
eq <- paste0(dv, " ~ factor(",r2,") +", vars2)

以下是问题:

print(eq)
# [1] "v1 ~ factor(v2) +r3+r4+r5+r6"

与第一个示例中的 regression 不同,eq 不会引入列名(例如 v3)。对象名称(例如 r3)被保留。因此,以下 lm() 命令不起作用。

m2 <- lm(as.formula(eq), data=dat)

5
我理解您的意思是:paste0("v1", " ~ ", vars) - Arun
谢谢,@Arun。你是对的。现在我的示例运行了,但我仍然在我的实际脚本中遇到错误。看起来一切都一样,但我知道我一定犯了一个错误。我会继续检查,如果我能找出差异,我会再次发布。 - Eric Green
1
在您发布的示例中,找不到“regressors”。如果您能编辑您的帖子提供一个现在会出错的示例,那就太好了。 - Arun
lm(v1 ~ factor(v2) +r3+r4+r5+r6, data=dat) 也不起作用。我认为你的意思是将 r3 等的内容粘在一起,而不是它们的名称。 - Aaron left Stack Overflow
@AaronпјҡжҳҜзҡ„пјҢжҲ‘ж— жі•еј„жё…жҘҡеҰӮдҪ•и®©eqиҝ”еӣһr3зҡ„еҶ…е®№пјҲеҚі v3пјүзӯүгҖӮ eqеҸӘдјҡз»ҷеҮәеӯ—з¬ҰдёІr3+r4+r5+r6гҖӮ - Eric Green
显示剩余2条评论
2个回答

71
我看到这里有一些问题。首先,虽然我不认为这会引起任何麻烦,但让我们一步完成您的数据框,这样您就不必在全局环境和数据框中同时使用到了。其次,让我们将在此处设为因子,这样我们就不必以后再处理它了。
dat <- data.frame(v1 = rnorm(10),
                  v2 = factor(sample(c(0,1), 10, replace=TRUE)),
                  v3 = rnorm(10),
                  v4 = rnorm(10) )

第一部分 现在,对于您的第一部分,看起来这就是您想要的:

lm(v1 ~ v2 + v3 + v4, data=dat)

这里有一个更简单的方法来完成这个操作,不过你仍需要指定响应变量。

lm(v1 ~ ., data=dat)

或者,您可以使用 paste 和调用 lm来构建函数。

f <- paste(names(dat)[1], "~", paste(names(dat)[-1], collapse=" + "))
# "v1 ~ v2 + v3 + v4"
lm(f, data=dat)

然而,在这些情况下,我更喜欢使用do.call,它在传递给函数之前评估表达式;这使得生成的对象更适合调用update等函数。请比较输出中的call部分。
do.call("lm", list(as.formula(f), data=as.name("dat")))

第二部分 关于你的第二部分,看起来你想要的是这样的内容:

lm(factor(v2) + v3 + v4 + v2*v3 + v2*v4, data=dat)

首先,因为v2是数据框中的一个因子,所以我们不需要该部分。其次,更好地利用R的算术操作来创建交互作用可以进一步简化代码,代码如下。

lm(v1 ~ v2*(v3 + v4), data=dat)

我会使用paste函数来创建函数;对于循环,即使在较大的情况下,使用assign也可能不是一个好主意。

f <- paste(names(dat)[1], "~", names(dat)[2], "* (", 
           paste(names(dat)[-c(1:2)], collapse=" + "), ")")
# "v1 ~ v2 * ( v3 + v4 )"

然后可以直接使用lm或者使用do.call进行调用。

lm(f, data=dat)
do.call("lm", list(as.formula(f), data=as.name("dat")))

关于你的代码 你在尝试使用r3等内容时遇到的问题是你想要变量r3的内容,而不是值r3。要获取值,你需要使用get,像这样,并使用paste将值合并在一起。

vars <- sapply(paste0("r", 3:6), get)
paste(vars, collapse=" + ")

然而,更好的方法是避免使用assign,直接构建您想要的术语向量,如下所示。

vars <- NULL
for (v in 3:4) {
  vars <- c(vars, colnames(dat)[v], paste(colnames(dat)[2], 
                                          colnames(dat)[v], sep="*"))
}
paste(vars, collapse=" + ")

更符合R语言风格的解决方案是使用lapply函数:
vars <- unlist(lapply(colnames(dat)[3:4], 
                      function(x) c(x, paste(colnames(dat)[2], x, sep="*"))))

非常有帮助和富有教育意义。谢谢!我仍然不确定如何使我的字符串在公式中起作用,但你给了我一个解决问题的方法。感谢你抽出时间回复。你真的帮助我整理了我的代码。 - Eric Green
这非常有用 - 它帮助我更好地理解lm函数。遗憾的是,do.call语法如此冗长。 - metakermit
参数必须是一个列表吗?也许是的,但这样做可以使 do.call(rbind, lapply(foo, fun)) 等操作变得更加简洁。 - Aaron left Stack Overflow
1
偶然发现了这个问答... do.call 是我一直在寻找的函数!谢谢! - theforestecologist

6
TL;DR: 使用paste命令。
create_ctree <- function(col){
    myFormula <- paste(col, "~.", collapse="")
    ctree(myFormula, data)
}
create_ctree("class")

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