Reputation: 2694
I'm trying to replace characters in an R expression (not in a string), but it seems that the usual functions for replacing characters won't work in this case. For example, given the following:
exp <- expression(italic(N[T]))
class(exp) # "expression"
Both string::str_replace
and base::gsub
will fail to convert characters in this expression object. The first returning a warning message, and in both cases returning an object of class "character": the expression object is lost.
exp %>% stringr::str_replace(pattern = "T", replacement = "i")
[1] "italic(N[i])"
Warning message: In stri_replace_first_regex(string, pattern, fix_replacement(replacement), : argument is not an atomic vector; coercing
exp %>% base::gsub(pattern = "T", replacement = "i")
[1] "italic(N[i])"
Is it possible to convert characters in expressions without loosing the class type?
A trick like the following won't work:
exp %>% base::gsub(pattern = "N", replacement = "i") %>% base::gsub(pattern = "^", replacement ="expression(") %>% base::gsub(pattern = "$", replacement = ")")
Upvotes: 3
Views: 966
Reputation: 269895
1) substitute Convert to a call object, use substitute
on that and then convert back (or maybe a call object is sufficient for your needs and you don't need to convert back to expression). This works entirely at the R language level as opposed to string manipulation.
as.expression(do.call("substitute", list(exp[[1]], list(T = as.name("I")))))
## expression(italic(N[I]))
2) recurse An alternative is to recusively walk through the call object and replace T with I. Note that this inputs and outputs a call object so convert from expression to call object and from call object to expression if you need to work with expression objects rather than call objects.
T2I <- function(e) {
if (identical(e, as.name("T"))) e <- as.name("I")
else if (length(e) > 1) for(i in 1:length(e)) e[[i]] <- Recall(e[[i]])
e
}
as.expression(T2I(exp[[1]]))
## expression(italic(N[I]))
3) assignment If you know the structure of exp then this assignment will work:
exp.orig <- exp # in case you need to save the original
exp[[1]][[2]][[3]] <- as.name("I")
exp
## expression(italic(N[I]))
4) string manipulation This one converts to a character string, performs replacement and converts back. It is similar to another answer but not exactly the same:
parse(text = gsub("\\bT\\b", "I", format(exp[[1]])))
## expression(italic(N[I]))
5) rrapply The rrapply
function in the rrapply package can walk an expression and perform replacements.
library(rrapply)
condition <- function(x) x == as.name("T")
f <- function(x) as.name("I")
rrapply(exp, condition = condition, f = f, how = "replace")
## expression(italic(N[I]))
Added rrapply
solution.
Upvotes: 5
Reputation: 32548
str2expression(gsub("N[T]", "N[i]", exp, fixed = TRUE))
#OR
parse(text = gsub("N[T]", "N[i]", exp, fixed = TRUE))
#expression(italic(N[i]))
Upvotes: 4