Identify and replace parameters in a formula like string in R

Viewed 74

I want to do some manipulation on parameters which are part of a string formatted as a mathematical function. For this goal I need to identify parameters and dynamically replace them.

More concrete I have a data.table with two columns:

> before
    target                             formula
1: targetA glarbA_scale * (fooA + zorbAoffset)
2: targetB          scale_of_B* (barA+offsetB)
...

This needs to be formatted in the form:

> after
    target                                  formula
1: targetA param1_targetA * (fooA + param2_targetA)
2: targetB    param1_targetB* (barA+param2_targetB)
...

My plan is to go line-by-line through the before-table and for each line:

  • Split entry of the second column (formula) at all mathematical operators : (, +, -, /, ) including the parentheses.
  • Identify the element including the phrase scale at arbitrary position in this string
  • Replace this by a new string of the form param1_<corresponding element of column1> e.g. glarbA_scale => param1_targetA
  • Analogue for the second parameter (offset), but with increased counter. E.g.: zorbAoffset => param2_targetA
  • paste the formula back to one string

This must be robust against different notations for the parameters, but all include scale or offset, respectively.

I have problems with both stages: Splitting a string on a number of different delimiters while conserving these and including them in the resulting list. For that I look for a function(call) which does:

> magical_function("glarbA_scale * (fooA + zorbAoffset)")
"glarbA_scale" "*" "(" "fooA" "+" "zorbAoffset" ")"

The resulting vector of strings should then be subject of the replacement function, which replaces the elements including scale or offset in the above mentioned form.

An alternative idea was to parse the string into an expression and utilize the substitute function.

I also thought about splitting this into two questions, but I guess this is one problem, and there might also be a nice possibility to do the splitting and replacement in one command similar to substitute...

In any case: Thank you in advance!

1 Answers

1) strings Using the input shown in the Note at the end and assuming that the variable names contain only word characters use sub, vectorized, twice (once for scale and once for offset) with the indicated regular expressions.

library(data.table)

vsub <- Vectorize(sub)
DT[, formula := vsub("\\b\\w*scale\\w*", paste0(target, "_param1"), formula)][
   , formula := vsub("\\b\\w*offset\\w*", paste0(target, "_param2"), formula)]

DT
##    target                                  formula
## 1: targetA targetA_param1 * (fooA + targetA_param2)
## 2: targetB    targetB_param1* (barA+targetB_param2)

2) expressions Alternately we can use expressions. Parse each formula, get the variables in it, extract the scale and offset variables, set up the arguments for substitute, run it and deparse the result back to a string.

subst <- function(x, target) {
  e <- parse(text = x)
  v <- all.vars(e)
  scale <- grep("scale", v, value = TRUE)
  offset <- grep("offset", v, value = TRUE)
  newv <- lapply(paste0("parm", 1:2, "_", target), as.name)
  oldv <- c(scale, offset)
  L <- setNames(newv, oldv) 
  deparse1(do.call("substitute", list(e[[1]], L)))
}

DT[, formula := subst(formula, target), by = 1:nrow(DT)]

DT
##     target                                formula
## 1: targetA parm1_targetA * (fooA + parm2_targetA)
## 2: targetB parm1_targetB * (barA + parm2_targetB)

Note

Lines <- "target,formula
targetA,glarbA_scale * (fooA + zorbAoffset)
targetB,scale_of_B* (barA+offsetB)"

library(data.table)
DT <- fread(Lines)
Related