elcortegano
elcortegano

Reputation: 2694

Replace characters in R objects of class 'expression'

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

Answers (2)

G. Grothendieck
G. Grothendieck

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]))

UPDATES

Added rrapply solution.

Upvotes: 5

d.b
d.b

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

Related Questions