【问题标题】:how can I remove two consecutive pluses (+) from a formula/string?如何从公式/字符串中删除两个连续的加号 (+)?
【发布时间】:2020-03-23 03:13:22
【问题描述】:

例如,我有一个这样的公式:

main_var ~ 0 + var1:x + var2:y + var3 + + var4 + (0 + main_var|x_y) + (0 + add_var|x_y) + (1|x_y)

如何删除var3var4 之间的两个连续加号(+)(只留下一个)?

【问题讨论】:

  • 如果您的公式是f,这可行:f[[3]][[2]][[2]][[2]][[3]] <- quote(var4) ... 但太疯狂了。用deparse()转成字符串,然后gsub()用合适的正则表达式,再转成公式
  • 好的,谢谢!从字符串中删除两个重复的加号的正则表达式是什么?
  • 如果加号由零个或多个空格分隔:"\\+\\s*\\+" 并执行:gsub("\\+\\s*\\+", "+", your_string)
  • 更具体地说是gsub("\\+s*\\+","+",z)(您想将“+ +”转换为“+”
  • 您需要更准确的是您对问题的描述。显然不是“如何删除 var3 和 var4 之间的两个连续加号 (+)”,而是“如何删除两个连续的加号 (+)”,您实际上并不想删除它们,但您想替换它们一加号。此外,它们不是连续的,因为它们之间有字符。似乎您可能想用加号替换每个以加号开头的字符串,后跟零个或多个空格,然后是另一个加号...

标签: r regex gsub


【解决方案1】:

可以在不强制转换为字符串的情况下编辑公式的组成部分。公式包含两部分,一个表达式(您编写的部分)和一个环境(您编写它的地方,其中可能包含表达式中引用的变量)。我们想要坚持的环境;我们要更改的表达式。

表达式(这里我指的是语言对象,如符号和调用,而不是狭义的expression 类)是语法树,其行为有点像列表。它们可以是子集:

f <- main_var ~ 0 + var1:x + var2:y + var3 + + var4 + (0 + main_var|x_y) + (0 + add_var|x_y) + (1|x_y)

f[[1]]
#> `~`
f[[2]]
#> main_var
f[[3]]
#> 0 + var1:x + var2:y + var3 + +var4 + (0 + main_var | x_y) + (0 + 
#>     add_var | x_y) + (1 | x_y)
f[[3]][[3]]
#> (1 | x_y)

因此迭代。因为它们是树状结构,所以要遍历整个树,我们需要递归。大多数函数对于递归来说是非常典型的(返回原子叶节点;在具有子节点的节点上递归),但棘手的部分是识别我们想要更改的部分的条件。如果您查看有问题的节点,它包含一个一元(带有一个参数)+ 调用:

f <- main_var ~ 0 + var1:x + var2:y + var3 + + var4 + (0 + main_var|x_y) + (0 + add_var|x_y) + (1|x_y)
f[[3]][[2]][[2]][[2]][[3]]
#> +var4
f[[3]][[2]][[2]][[2]][[3]][[1]]
#> `+`
f[[3]][[2]][[2]][[2]][[3]][[2]]
#> var4

所有其他 + 调用都是二进制的。因此,我们可以检查第一个节点是+ 的长度为2 的节点。事实证明,获得+ 表达式也有点棘手;最简单的是experssion(+)[[1]]quote(+1)[[1]],但是一旦你有了它,平等检查就会照常工作。

将各个部分放在一起,并通过将部分强制转换为表达式和公式来进行清理,

remove_unary_plus <- function(expr){
    if (length(expr) == 1) {
        # return atomic elements
        return(expr) 
    } else if (length(expr) == 2 && expr[[1]] == expression(`+`)[[1]]) {
        # for unary plus calls, return the argument without the plus
        return(expr[[2]]) 
    } else {
        # otherwise recurse, simplifying the results back to a language object
        clean_expr <- as.call(lapply(expr, remove_unary_plus))

        # if it's a formula, hold on to the environment
        if (inherits(expr, "formula")) {
            clean_expr <- as.formula(clean_expr, env = environment(expr))
        }

        return(clean_expr)
    }
}

f_clean <- remove_unary_plus(f)
f_clean
#> main_var ~ 0 + var1:x + var2:y + var3 + var4 + (0 + main_var | 
#>     x_y) + (0 + add_var | x_y) + (1 | x_y)

你看,它保持了它的环境:

str(f)
#> Class 'formula'  language main_var ~ 0 + var1:x + var2:y + var3 + +var4 + (0 + main_var | x_y) +      (0 + add_var | x_y) + (1 | x_y)
#>   ..- attr(*, ".Environment")=<environment: R_GlobalEnv>
str(f_clean)
#> Class 'formula'  language main_var ~ 0 + var1:x + var2:y + var3 + var4 + (0 + main_var | x_y) + (0 +      add_var | x_y) + (1 | x_y)
#>   ..- attr(*, ".Environment")=<environment: R_GlobalEnv>

显然,这对于日常的公式操作来说有点痛苦,但是,它是可能的,也许对程序化使用有用,并且(至少对我而言)很有趣。

【讨论】:

  • 我同意这是“正确”的可靠答案,但是我只在必要时才构建这样的系统实在是太让人头疼了……
  • 是的,我希望在 R 中使用递归的工具稍微好一点。 Recallrapplypurrr::modify_depthpryr::modify_lang 等只是不够灵活或不够强大,所以使用正则表达式更容易。也许我应该再写一个包。
【解决方案2】:

类似

as.formula( gsub( ""\\+s*\\+", "+", deparse(f)))

f 是你的公式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-11
    • 1970-01-01
    • 1970-01-01
    • 2011-05-21
    • 1970-01-01
    • 1970-01-01
    • 2023-02-16
    • 1970-01-01
    相关资源
    最近更新 更多