如何在R公式中用两个术语替换一个术语?

8

我有一个类似于以下内容的东西:

y ~ x + z

我希望你能将其转换为:

我希望你能翻译它成

y ~ x_part1 + x_part2 + z

更普遍地说,我想要一个函数,它可以接受一个公式,并返回所有匹配 "^x$" 的项被替换为 "x_part1" 和 "x_part2" 的公式。这是我目前的解决方案,但感觉很不自然...
my.formula <- fruit ~ apple + banana
var.to.replace <- 'apple'
my.terms <- labels(terms(my.formula))
new.terms <- paste0('(', 
                    paste0(var.to.replace, 
                           c('_part1', '_part2'),
                           collapse = '+'),
                    ')')
new.formula <- reformulate(termlabels = gsub(pattern = var.to.replace,
                                             replacement = new.terms,
                                             x = my.terms),                                 
                           response = my.formula[[2]])

需要注意的是,输入公式可能会包含相互作用。

y ~ b*x + z

应该输出以下公式之一(等效)
y ~ b*(x_part1 + x_part2) + z
y ~ b + (x_part1 + x_part2) + b:(x_part1 + x_part2) + z
y ~ b + x_part1 + x_part2 + b:x_part1 + b:x_part2 + z

MrFlick主张使用

substitute(y ~ b*x + z,list(x=quote(x_part1 + x_part2)))

但是当我将想要修改的公式存储在变量中时,例如

my.formula <- fruit ~ x + banana

这种方法似乎需要进行一些微调:

substitute(my.formula, list(x=quote(apple_part1 + apple_part2)))
# my.formula

那种方法需要进行必要的改变:
do.call(what = 'substitute',
        args = list(apple, list(x=quote(x_part1 + x_part2))))

但是当'x'和c('x_part', 'x_part2')都以变量形式存储,例如上面的var.to.replacenew.terms时,我无法弄清如何使用这种方法。

5个回答

9
您可以使用substitute函数来实现此功能。
substitute(y ~ b*x + z, list(x=quote(x_part1 + x_part2)))
# y ~ b * (x_part1 + x_part2) + z

在这里,我们使用命名列表来告诉R将变量x替换为表达式x_part1 + x_part2


2
这看起来非常有前途。如果我想要使用的公式存储在变量中,我该如何处理?似乎替换函数作用于我放在第一个参数中的表达式,而不是该表达式的值? - rcorty
我不清楚你是如何“存储”变量的,这一点在你的问题中并不明确。我也不确定你对替换的哪个部分不清楚。 - MrFlick
我在问题的末尾添加了一些文本,可以更好地进行语法高亮。谢谢你的帮助。 - rcorty
2
啊,那么你只需要使用 do.call 来传递变量的值。do.call("substitute", list(my.formula, list(x=quote(apple_part1 + apple_part2)))) - MrFlick
啊,我又遇到了一个问题。如果要替换的术语和要替换成的术语也存储在变量中怎么办? - rcorty
1
对于前一种情况,请使用setNames,而对于后一种情况,则不要使用quote()。在未来,尝试使您的初始问题更完整,或者创建一个新问题以解决出现的新问题。在评论中更改问题是没有帮助的。 - MrFlick

4

您可以编写一个递归函数来修改公式的表达式树:

replace_term <- function(f, old, new){
  n <- length(f)
  if(n > 1) {
    for(i in 1:n) f[[i]] <- Recall(f[[i]], old, new)

    return(f)
  }

  if(f == old) new else f
}

您可以使用它来修改例如交互:

> replace_term(y~x*a+z - x, quote(x), quote(x1 + x2))
y ~ (x1 + x2) * a + z - (x1 + x2)

1
内置的substitute函数基本上是这样实现的。 - MrFlick

4

那么,将公式作为字符串来使用如何?许多基础R模型(如lm())接受字符串公式(否则您可以始终使用formula())。在这种情况下,您可以使用类似于gsub()的东西:

f1 <- "y ~ x + z"
f2 <- "y ~ b*x + z"

gsub("x", "(x_part1 + x_part2)", f1)
#> [1] "y ~ (x_part1 + x_part2) + z"

gsub("x", "(x_part1 + x_part2)", f2)
#> [1] "y ~ b*(x_part1 + x_part2) + z"

例如,假设我们有一个名为mtcars的数据集,并且我们想要用disp + hp(x_part1+x_part2)替换mpg(x):
f1 <- "qsec ~ mpg + cyl"
f2 <- "qsec ~ wt*mpg + cyl"

f1 <- gsub("mpg", "(disp + hp)", f1)
f2 <- gsub("mpg", "(disp + hp)", f2)

lm(f1, data = mtcars)
#> 
#> Call:
#> lm(formula = f1, data = mtcars)
#> 
#> Coefficients:
#> (Intercept)         disp           hp          cyl  
#>    22.04376      0.01017     -0.02074     -0.56571

lm(f2, data = mtcars)
#> 
#> Call:
#> lm(formula = f2, data = mtcars)
#> 
#> Coefficients:
#> (Intercept)           wt         disp           hp          cyl  
#>   20.421318     1.554904     0.026837    -0.056141    -0.876182  
#>     wt:disp        wt:hp  
#>   -0.006895     0.011126

2
如果您只想修改主要影响,您可以减去x,并添加两个新变量。
> f <- y ~ x + z
> update(f, .~.-x+x_part1 + x_part2)
y ~ z + x_part1 + x_part2

你如何从x生成x_part1? - rcorty
我使用字符(paste)完成,然后应用 as.formula 并将其传递给 update 的 'new' 参数。 - rcorty
啊,这个答案的问题在于,如果原始公式有交互作用,我相信它们会在新公式中丢失。我会更新问题以澄清这种情况。 - rcorty

1

根据 rcorty 的要求,将 'x' 和 c('x_part', 'x_part2') 存储在 var.to.replacenew.terms 中,并采用 MrFlick 建议使用 setNames,我们可以尝试以下操作:

my.formula <- fruit ~ x + banana
var.to.replace <- "x"
new.terms <-  c('x_part', 'x_part2') 
new.terms1 <- paste(new.terms, collapse="+")
do.call("substitute", list(my.formula, setNames(list(str2lang(new.terms1)), var.to.replace))) 

> fruit ~ x_part + x_part2 + banana

顺便提一下,我发现保罗·约翰逊的Rchaeology(第2.1节)相关、有教育意义且富有娱乐性。


这真是太棒了。 - undefined

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