Предположим, у меня есть формула или другой объект, похожий на выражение.
ee <- a ~ b + c
и хотите заменить все экземпляры (например) символа b на B. Хакерский способ сделать это с заменой строки:
(ee
|> deparse()
|> gsub(pattern = "\\<b\\>", replacement = "B")
|> as.formula()
)
Это работает (результат ee <- a ~ B + c, как и хотелось)
Возможно, лучший способ сделать это - написать рекурсивную функцию, которая проверяет, идентичен ли конкретный элемент целевому, и заменяет его заменой, если это так, в противном случае вызывает себя для элементов... это огромная боль. Может ли кто-нибудь предложить наименее болезненный способ реализовать это (например, заклинание с участием rapply()) ...?
(В связи с этим вопросом об удалении членов из формулы...)





Рекурсивная функция уже существует: substitute.
> do.call(substitute, list(ee, list(b = quote(B))))
a ~ B + c
Обратите внимание, что
> substitute(ee, list(b = quote(B)))
ee
не работает, потому что substitute является специальным и не оценивает свой первый аргумент:
> typeof(substitute)
[1] "special"
@BenBolker Связанный: bugs.r-project.org/show_bug.cgi?id=18568
Если вам нужен более удобный интерфейс, следующая рекурсивная функция сделает свое дело, не использует синтаксический анализ строк и не особенно утомительна.
replace_formula <- function(f, from, to, level = 1) {
mc <- match.call()
if (level > 1) f <- mc$f
if (is.symbol(f) || is.function(f)) {
if (identical(f, mc$from)) return(mc$to) else return(f)
}
as.call(lapply(as.list(f), function(x) {
do.call(replace_formula, list(f = x, from = mc$from, to = mc$to, level + 1))
}))
}
Например:
replace_formula(a ~ b + c, from = b, to = B)
#> a ~ B + c
replace_formula(x ~ y + x|d, y, Y)
#> x ~ Y + x | d
my_formula <- y ~ cos(sin(a)) + a
replace_formula(my_formula, a, A)
#> y ~ cos(sin(A)) + A
Created on 2023-07-15 with reprex v2.0.2
Спасибо! Я думаю, что аналогичные проблемы, которые я решал раньше («удалить термины формы XXX»; «заменить термины формы
(f|g)наfoo(f|g)» и т. д.), были более сложной/требуемой рекурсией, но это отлично работает для того, что мне нужно сделать прямо сейчас...