假设我有以下公式:
myformula<-formula("depVar ~ Var1 + Var2")
如何从公式对象中可靠地获取因变量名?
我找不到任何用于此目的的内置函数。
我知道as.character(myformula)[[2]]
可以正常工作,
sub("^(\\w*)\\s~\\s.*$","\\1",deparse(myform))
在我看来,这些方法更像是一种hackery,而不是一种可靠而标准的方法。
有没有人知道具体的方法是什么? lm
使用?我已经看过它的代码了,但对我来说有点神秘......这里有一个方便的引用:
> lm
function (formula, data, subset, weights, na.action, method = "qr",
model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE,
contrasts = NULL, offset, ...)
{
ret.x <- x
ret.y <- y
cl <- match.call()
mf <- match.call(expand.dots = FALSE)
m <- match(c("formula", "data", "subset", "weights", "na.action",
"offset"), names(mf), 0L)
mf <- mf[c(1L, m)]
mf$drop.unused.levels <- TRUE
mf[[1L]] <- as.name("model.frame")
mf <- eval(mf, parent.frame())
if (method == "model.frame")
return(mf)
else if (method != "qr")
warning(gettextf("method = '%s' is not supported. Using 'qr'",
method), domain = NA)
mt <- attr(mf, "terms")
y <- model.response(mf, "numeric")
w <- as.vector(model.weights(mf))
if (!is.null(w) && !is.numeric(w))
stop("'weights' must be a numeric vector")
offset <- as.vector(model.offset(mf))
if (!is.null(offset)) {
if (length(offset) != NROW(y))
stop(gettextf("number of offsets is %d, should equal %d (number of observations)",
length(offset), NROW(y)), domain = NA)
}
if (is.empty.model(mt)) {
x <- NULL
z <- list(coefficients = if (is.matrix(y)) matrix(, 0,
3) else numeric(), residuals = y, fitted.values = 0 *
y, weights = w, rank = 0L, df.residual = if (!is.null(w)) sum(w !=
0) else if (is.matrix(y)) nrow(y) else length(y))
if (!is.null(offset)) {
z$fitted.values <- offset
z$residuals <- y - offset
}
}
else {
x <- model.matrix(mt, mf, contrasts)
z <- if (is.null(w))
lm.fit(x, y, offset = offset, singular.ok = singular.ok,
...)
else lm.wfit(x, y, w, offset = offset, singular.ok = singular.ok,
...)
}
class(z) <- c(if (is.matrix(y)) "mlm", "lm")
z$na.action <- attr(mf, "na.action")
z$offset <- offset
z$contrasts <- attr(x, "contrasts")
z$xlevels <- .getXlevels(mt, mf)
z$call <- cl
z$terms <- mt
if (model)
z$model <- mf
if (ret.x)
z$x <- x
if (ret.y)
z$y <- y
if (!qr)
z$qr <- NULL
z
}
答案 0 :(得分:32)
尝试使用all.vars
:
all.vars(myformula)[1]
答案 1 :(得分:13)
我找到了一个有用的包'formula.tools',它适合你的任务。
代码示例:
f&lt; - as.formula(a1 + a2~a3 + a4)
lhs.vars(f)#get dependent variables
[1]“a1”“a2”
rhs.vars(f)#get independent variables
[1]“a3”“a4”
答案 2 :(得分:11)
我想你也可以自己做功能来使用terms()
:
getResponse <- function(formula) {
tt <- terms(formula)
vars <- as.character(attr(tt, "variables"))[-1] ## [1] is the list call
response <- attr(tt, "response") # index of response var
vars[response]
}
R> myformula <- formula("depVar ~ Var1 + Var2")
R> getResponse(myformula)
[1] "depVar"
它和as.character(myformyula)[[2]]
一样hacky,但你可以保证得到正确的变量,因为调用解析树的顺序不会很快改变。
多个因变量不太好:
R> myformula <- formula("depVar1 + depVar2 ~ Var1 + Var2")
R> getResponse(myformula)
[1] "depVar1 + depVar2"
因为他们需要进一步处理。
答案 3 :(得分:6)
根据您的编辑获取实际响应,而不仅仅是它的名称,我们可以使用lm()
使用的非标准评估惯用法和大多数其他建模函数与基础R中的公式接口
form <- formula("depVar ~ Var1 + Var2")
dat <- data.frame(depVar = rnorm(10), Var1 = rnorm(10), Var2 = rnorm(10))
getResponse <- function(form, data) {
mf <- match.call(expand.dots = FALSE)
m <- match(c("formula", "data"), names(mf), 0L)
mf <- mf[c(1L, m)]
mf$drop.unused.levels <- TRUE
mf[[1L]] <- as.name("model.frame")
mf <- eval(mf, parent.frame())
y <- model.response(mf, "numeric")
y
}
> getResponse(form, dat)
1 2 3 4 5
-0.02828573 -0.41157817 2.45489291 1.39035938 -0.31267835
6 7 8 9 10
-0.39945771 -0.09141438 0.81826105 0.37448482 -0.55732976
如您所见,这将从提供的数据框中获取实际的响应变量数据。
这是如何工作的,该函数首先捕获函数调用而不扩展...
参数,因为它包含评估公式数据所不需要的东西。
接下来,"formula"
和"data"
参数与调用匹配。行mf[c(1L, m)]
从调用(1L
)中选择函数名称以及两个匹配参数的位置。 drop.unused.levels
的{{1}}参数在下一行中设置为model.frame()
,然后更新呼叫以将呼叫中的函数名称从TRUE
切换为{{1 }}。以上所有代码都会调用lm
并将调用的进程调用model.frame
函数。
然后在函数的父环境中评估此修改后的调用 - 在本例中是全局环境。
最后一行使用lm()
提取器函数从模型框架中获取响应变量。
答案 4 :(得分:2)
这应该总是给你所有依赖的变种:
myformula<-formula("depVar1 + depVar2 ~ Var1 + Var2")
as.character(myformula[[2]])[-1]
#[1] "depVar1" "depVar2"
我不认为这特别“hacky”。
修改强>
3个家属发生了奇怪的事情:
myformula<-formula("depVar1 + depVar2 + depVar3 ~ Var1 + Var2")
as.character(myformula[[2]])
#[1] "+" "depVar1 + depVar2" "depVar3"
所以这可能不如我想象的那么可靠。
<强> EDIT2:强>
好的,myformula[[2]]
是一个语言对象,而as.character
似乎与languageEl
类似。
length(myformula[[2]])
#[1] 3
languageEl(myformula[[2]],which=1)
#`+`
languageEl(myformula[[2]],which=2)
#depVar1 + depVar2
languageEl(myformula[[2]],which=3)
#depVar3
languageEl(languageEl(myformula[[2]],which=2),which=2)
#depVar1
如果检查每个元素的长度,可以创建自己的提取功能。但这可能太过分了。
<强> EDIT3:强>
基于@seancarmody all.vars(myformula[[2]])
的答案是可行的方法。
答案 5 :(得分:0)
我知道这个问题已经很老了,但是我想我会添加一个基本的R答案,该答案不需要索引,也不依赖于对all.vars
的调用中列出的变量的顺序,并在一个以上时将响应变量作为单独的元素提供:
myformula <- formula("depVar1 + depVar2 ~ Var1 + Var2")
all_vars <- all.vars(myformula)
response <- all_vars[!(all_vars %in% labels(terms(myformula)))]
> response
[1] "depVar1" "depVar2"
答案 6 :(得分:0)
使用all.vars
非常棘手,因为它无法检测到单边公式的响应。例如
all.vars(~x+1)
[1] "x"
那是错的。
这是获得响应的最可靠方法:
getResponseFromFormula = function(formula) {
if (attr(terms(as.formula(formula)) , which = 'response'))
all.vars(formula)[1]
else
NULL
}
getResponseFromFormula(~x+1)
NULL
getResponseFromFormula(y~x+1)
[1] "y"
请注意,如果公式包含多个响应变量,则可以用all.vars(formula)[1]
替换函数中的formula[2]
。