Как я могу удалить два последовательных плюса (+) из формулы / строки? - PullRequest
0 голосов
/ 23 марта 2020

Например, у меня есть формула, подобная этой:

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

Как я могу удалить два последовательных плюса (+) между var3 и var4 (и оставить только один)?

Ответы [ 2 ]

2 голосов
/ 23 марта 2020

Можно редактировать составные части формулы без привязки к строке. Формулы состоят из двух частей: выражения (часть, которую вы пишете) и среды (где вы ее пишете, возможно, с переменными в ней, указанными в выражении). Среда, в которой мы хотим держаться; выражение, которое мы хотим изменить.

Выражения (под которыми здесь я подразумеваю языковые объекты, такие как символы и вызовы, а не узко определенный класс 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)

и, следовательно, повторяться. Поскольку они представляют собой древовидные структуры, для итерации по всему дереву нам необходимо выполнить рекурсию. Большая часть функции довольно типична для рекурсии (возвращать атомы c листовые узлы; рекурсивно перебирать узлы с потомками), но сложная часть - это условие для идентификации части, которую мы хотим изменить. Если вы посмотрите на рассматриваемый узел, он содержит унарный (с одним аргументом) + вызов:

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>

Очевидно, что это немного болезненно для повседневных манипуляций с формулами, но, ну, это возможно, может быть полезно для программирования c использование, и (для меня, по крайней мере) интересно.

1 голос
/ 23 марта 2020

Что-то вроде

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

, где f - ваша формула.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...