有没有比字符串操作更好的选择来以编程方式构建公式?

Posted

技术标签:

【中文标题】有没有比字符串操作更好的选择来以编程方式构建公式?【英文标题】:Is there a better alternative than string manipulation to programmatically build formulas? 【发布时间】:2012-10-09 16:29:33 【问题描述】:

其他人的函数似乎都采用公式对象,然后在内心深处的某个地方对它们施以黑魔法,我很嫉妒。

我正在编写一个适合多个模型的函数。这些模型的部分公式保持不变,而从一个模型到另一个模型的部分变化。笨拙的方法是让用户将公式部分作为字符串输入,对其进行一些字符操作,然后使用as.formula

但在我走这条路之前,我只是想确保我没有忽略一些更简洁的方法,它允许函数接受标准 R 格式的公式(例如从其他使用公式的对象中提取)。

我想要...

> LHS <- y~1; RHS <- ~a+b; c(LHS,RHS);
y ~ a + b
> RHS2 <- ~c;
> c(LHS, RHS, RHS2);
y ~ a + b + c

或者...

> LHS + RHS;
y ~ a + b
> LHS + RHS + RHS2;
y ~ a + b + c

...但不幸的是,这两种语法都不起作用。有人知道是否有什么可以做的吗?谢谢。

【问题讨论】:

尽管我最终意识到我并不需要那种通用性,而是更好地利用了update 函数,但下面的 mnel 的答案是一个很好且有用的答案,并且可能有做了我最初尝试的事情。不过,总的来说,我赞成好的答案,但在我真正尝试它们并能保证它们之前不接受它们。在许多情况下,我自己找到了更好的答案,我真的应该在有时间的时候提交自己的答案。我接受答案的标准是否过于严格? 【参考方案1】:

reformulate 会做你想做的事。

reformulate(termlabels = c('x','z'), response = 'y')
## y ~ x + z

或者没有拦截

reformulate(termlabels = c('x','z'), response = 'y', intercept = FALSE)
## y ~ x + z - 1

请注意,您不能构造包含多个reponses 的公式,例如x+y ~z+b

reformulate(termlabels = c('x','y'), response = c('z','b'))
z ~ x + y

从现有的formula 中提取术语(以您的示例为例)

attr(terms(RHS), 'term.labels')
## [1] "a" "b"

要得到响应稍有不同,一个简单的方法(对于单变量响应)。

as.character(LHS)[2]
## [1] 'y'


combine_formula <- function(LHS, RHS)
  .terms <- lapply(RHS, terms)
  new_terms <- unique(unlist(lapply(.terms, attr, which = 'term.labels')))
  response <- as.character(LHS)[2]

  reformulate(new_terms, response)





combine_formula(LHS, list(RHS, RHS2))

## y ~ a + b + c
## <environment: 0x577fb908>

我认为将响应指定为字符向量会更明智,例如

combine_formula2 <- function(response, RHS, intercept = TRUE)
  .terms <- lapply(RHS, terms)
  new_terms <- unique(unlist(lapply(.terms, attr, which = 'term.labels')))
  response <- as.character(LHS)[2]

  reformulate(new_terms, response, intercept)



combine_formula2('y', list(RHS, RHS2))

您还可以定义 + 运算符来处理公式(更新为公式对象设置新方法)

`+.formula` <- function(e1,e2)
  .terms <- lapply(c(e1,e2), terms)
  reformulate(unique(unlist(lapply(.terms, attr, which = 'term.labels'))))


RHS + RHS2
## ~a + b + c

你也可以明智地使用update.formula.

 update(~a+b, y ~ .)
 ##  y~a+b

【讨论】:

+1 为您的精彩总结。但是让我们承认这一点:它只是表明,如果您想要易于阅读的代码,则可以通过字符串使用该方法。我怀疑,在整个生命周期中,速度的提升是否会为你赢得喝咖啡的时间。 @DieterMenne 速度的提升并不重要——安全的提升很重要。当有人第一次尝试将您的代码与非语法变量名(即“a b”)一起使用时,它会因一个奇怪的错误而中断,您需要花费数小时才能找到。 除了update.formula,还有其他方法可以使用.吗?我最终使用了setdiff() 语句,我本来希望在reformulate() 中使用~.-var1-var2,而帮助没有提到那个用例。

以上是关于有没有比字符串操作更好的选择来以编程方式构建公式?的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式重命名 AWS Glue 目录中的列名

UITextField 初始化缓慢?

以编程方式创建视图和添加子视图比使用情节提要更好/更快[重复]

如何以编程方式比较magento版本?

以编程方式创建 plist

在excel中编辑长条件格式公式