如何在R中使用公式对象(formula objects)

9

我正在尝试学习如何使用 formula 对象创建自己的函数。我最大的困惑在于如何解析它们。

假设我有以下内容:

gigl <- function(formula, data, family = gaussian()) 

使用 R 数据集 BOD

> BOD
  Time demand
1    1    8.3
2    2   10.3
3    3   19.0
4    4   16.0
5    5   15.6
6    7   19.8

使用lm很容易拟合线性模型。

>lm(Time~demand, data=BOD)
Call:
lm(formula = Time ~ demand)

Coefficients:
(Intercept)       demand  
    -1.8905       0.3746

如何通过解析公式来创建自己的函数?

例如,如果我有以下公式:

>gigl(Time~demand, data=BOD)

如何解析这些组件?我并不在乎函数 gigl 的作用。我只想知道如何处理 formula

编辑

由于有关具体示例的问题,让我们尝试以下内容:

假设我要使用公式中的输入来构建一个 cor() 矩阵。因此,从上面的内容中,我将看到 cor(Time, demand) 的结果,如果添加了更多变量,则会看到所有输入的完整 cor()


1
你能详细说明一下你想用这个函数做什么吗?为什么你特别想使用一个公式? - Colin FAY
公式实际上只是符号的集合。您可以使用列表样式提取器遍历它们,例如(a~b)[[3]]返回b。如果您能给出更具体的示例,那将更有帮助。 - MrFlick
如果有更多的变量,公式会是什么样子呢?a ~ b+c+d?结果是否会是a、b、c、d的完整相关矩阵?如果是这样,公式左侧的变量和右侧的变量之间是否有区别? - Ben Bolker
在这个例子中没有区别。这可能不是最好的例子。如果您愿意,可以自己创建示例。我只想学习如何操作“formula”对象。 - Alex
4个回答

5

这里有一个函数,它接受一个公式,并将其转换为对cor()函数的调用,然后在由数据和外部环境组成的环境中评估该调用...

f <- function(form,data) {
    form[[1]] <- quote(cor)
    eval(form,data)
}
f(demand~Time,BOD)
## [1] 0.8030693

3

rlang包可以在tidyeval范式中更轻松地处理公式。例如,您可以执行以下操作:

library(rlang)

mycor <- function(form, data) {
  v1 <- f_lhs(form)  
  v2 <- f_rhs(form)
  d <- enquo(data)
  qq <- expr(with(!!d, cor(!!v1, !!v2)))
  eval_tidy(qq)

}

mycor(disp~drat, mtcars)
# [1] -0.7102139

with(mtcars, cor(disp, drat))
# [1] -0.7102139

f_lhs/f_rhs函数分别帮助提取左侧和右侧。然后,我们可以使用quo()!!运算符将这些部分重新组合成一个新的函数调用。然后我们使用eval_tidy来评估这个新的函数调用。


对我来说,使用 !! 看起来很奇怪。这有什么作用呢? - Alex
2
当这种类型的扩展通常不发生时,它会用调用中变量的值替换变量名。请参见:https://cran.r-project.org/web/packages/rlang/vignettes/tidy-evaluation.html - MrFlick
这里的代码在父级中查找自由变量,但通常使用这种风格的函数时,自由变量应在公式环境中查找。例如,在 X <- 0; f <- function() { X <- 1; mycor(disp ~ drat^X + X, mtcars) }; f() 中,它将使用 drat^0 返回 NA,但应该使用 drat^1,因为公式在 f 中定义,因此它的环境是在 f 中。 - G. Grothendieck
好观点@G.Grothendieck。也许更好的翻译是使用qq <- expr(...)而不是quo()eval_tidy(qq, env=environment(form))。这将使您的示例正常工作。我认为公式捕获环境的事实有时会让人感到惊讶,而不是做他们期望的事情。 - MrFlick

1

我不确定您想做什么,但是您可以查看公式的术语

fm <- formula(Time ~ demand);
tms <- terms(fm);
tms;
#Time ~ demand
#attr(,"variables")
#list(Time, demand)
#attr(,"factors")
#       demand
#Time        0
#demand      1
#attr(,"term.labels")
#[1] "demand"
#attr(,"order")
#[1] 1
#attr(,"intercept")
#[1] 1
#attr(,"response")
#[1] 1
#attr(,".Environment")
#<environment: R_GlobalEnv>

tms 中,您可以提取相关的条目和属性。例如,
attr(tms, "variables");
#list(Time, demand)

1
假设使用了两个变量(不允许使用表达式)。假设这两个变量在公式中出现且可能出现在左侧、右侧或两侧,可以使用all.vars获取变量名和get_all_vars获取内容。
gig1 <- function(formula, data) cor(data[all.vars(formula)])

gig1(demand ~ Time, BOD)

提供:

          demand      Time
demand 1.0000000 0.8030693
Time   0.8030693 1.0000000

或者

gig2 <- function(formula, data) cor(get_all_vars(formula, data))

gig2(demand ~ Time, BOD)

提供:

          demand      Time
demand 1.0000000 0.8030693
Time   0.8030693 1.0000000

您可能希望查看lm的源代码和公式包以获取更多想法。


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