38

假设我有以下公式:

myformula<-formula("depVar ~ Var1 + Var2")

如何可靠地从公式对象中获取因变量名称?

我没有找到任何用于此目的的内置函数。我知道这as.character(myformula)[[2]]很有效

sub("^(\\w*)\\s~\\s.*$","\\1",deparse(myform))

在我看来,这些方法更像是一种骇客,而不是一种可靠和标准的方法。


有谁知道例如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
}
4

7 回答 7

41

尝试使用all.vars

all.vars(myformula)[1]
于 2012-11-04T09:38:18.657 回答
13

我想您也可以编写自己的函数来使用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"

它就像 hacky 一样,as.character(myformyula)[[2]]但是您可以确保获得正确的变量,因为调用解析树的顺序不会很快改变。

对于多个因变量,这不是很好:

R> myformula <- formula("depVar1 + depVar2 ~ Var1 + Var2")
R> getResponse(myformula)
[1] "depVar1 + depVar2"

因为他们需要进一步处理。

于 2012-11-04T10:03:00.540 回答
13

我找到了一个有用的包“formula.tools”,它适合您的任务。

代码示例:

f <- as.formula(a1 + a2~a3 + a4)

lhs.vars(f) #获取因变量

[1] “a1” “a2”

rhs.vars(f) #获取自变量

[1] “a3” “a4”

于 2013-07-24T08:06:24.020 回答
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"and"data"参数与调用匹配。该行mf[c(1L, m)]从调用 ( 1L) 中选择函数名称以及两个匹配参数的位置。的drop.unused.levels参数在下一行model.frame()中设置为TRUE,然后更新调用以将调用中的函数名从 切换lmmodel.frame。上面的代码所做的就是将调用lm()和处理调用到model.frame()函数的调用中。

然后在函数的父环境中评估这个修改后的调用 - 在这种情况下是全局环境。

最后一行使用model.response()提取器函数从模型框架中获取响应变量。

于 2012-11-04T11:16:25.297 回答
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" 

所以这可能不像我想象的那么可靠。

编辑2:

好的,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]])的路。

于 2012-11-04T09:54:13.583 回答
1

我知道这个问题很老了,但我想我会添加一个不需要索引的基本 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"
于 2018-09-07T00:45:03.727 回答
1

使用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]

于 2019-03-24T10:58:58.973 回答