如何从 formula/string 中删除两个连续的加号 (+)?
how can I remove two consecutive pluses (+) from a formula/string?
例如,我有这样一个公式:
main_var ~ 0 + var1:x + var2:y + var3 + + var4 + (0 + main_var|x_y) + (0 + add_var|x_y) + (1|x_y)
如何删除 var3
和 var4
之间的两个连续加号 (+)(只保留一个)?
类似
as.formula( gsub( ""\+s*\+", "+", deparse(f)))
其中 f
是您的公式。
可以在不强制转换为字符串的情况下编辑公式的组成部分。公式包含两个部分,一个表达式(您编写的部分)和一个环境(您编写它的地方,可能包含表达式中引用的变量)。我们想要坚持的环境;我们要更改的表达式。
表达式(在这里我指的是符号和调用等语言对象,而不是狭义定义的 expression
class)是语法树,其行为有点像列表。它们可以是子集:
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)
因此进行了迭代。因为它们是树状结构,要遍历整个树,我们需要递归。大多数函数对于递归来说是非常典型的(return 原子叶节点;在有子节点的节点上递归),但棘手的部分是识别我们想要更改的部分的条件。如果您查看有问题的节点,它包含一个一元(带有一个参数)+
调用:
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>
显然,这对于日常的公式操作来说有点痛苦,但是,嗯,这是可能的,可能对编程使用有用,并且(至少对我来说)很有趣。
例如,我有这样一个公式:
main_var ~ 0 + var1:x + var2:y + var3 + + var4 + (0 + main_var|x_y) + (0 + add_var|x_y) + (1|x_y)
如何删除 var3
和 var4
之间的两个连续加号 (+)(只保留一个)?
类似
as.formula( gsub( ""\+s*\+", "+", deparse(f)))
其中 f
是您的公式。
可以在不强制转换为字符串的情况下编辑公式的组成部分。公式包含两个部分,一个表达式(您编写的部分)和一个环境(您编写它的地方,可能包含表达式中引用的变量)。我们想要坚持的环境;我们要更改的表达式。
表达式(在这里我指的是符号和调用等语言对象,而不是狭义定义的 expression
class)是语法树,其行为有点像列表。它们可以是子集:
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)
因此进行了迭代。因为它们是树状结构,要遍历整个树,我们需要递归。大多数函数对于递归来说是非常典型的(return 原子叶节点;在有子节点的节点上递归),但棘手的部分是识别我们想要更改的部分的条件。如果您查看有问题的节点,它包含一个一元(带有一个参数)+
调用:
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>
显然,这对于日常的公式操作来说有点痛苦,但是,嗯,这是可能的,可能对编程使用有用,并且(至少对我来说)很有趣。