Home > OS >  Convert variables in data.table to formula
Convert variables in data.table to formula

Time:02-21

I have a sample data.table data as below:

   VarName Formulae
1:       A      1 1
2:       B      A 3
3:       C     B*10
4:       D      A C
5:       E      D/2

I want to convert the Formulae column into formulas, so that the output can become like:

  VarName Result
1:       A      2
2:       B      5
3:       C      50
4:       D      52
5:       E      26

Basically the VarName column is the variable name and the Formulae column is the corresponding formula.

A = 1 1
B = A 3
C = B*10
D = A C
E = D/2

I have tried using the eval and parse functions like data$VarName = eval(parse(text = "data$Formulae")), however I could not get the desired output.

CodePudding user response:

Loop through VarName replace them with Formulae within brackets, then evaluate:

res <- setNames(x$Formulae, x$VarName)

while(any(grepl("[A-Z]", res))) {
  for(i in names(res)){
    res <- gsub(i, paste0("(", res[ i ], ")"), res, fixed = TRUE)
  }
}

#res, after replacements:
#                          A                          B 
#                      "1 1"                  "(1 1) 3" 
#                          C                          D 
#             "((1 1) 3)*10"     "(1 1) (((1 1) 3)*10)" 
#                          E 
# "((1 1) (((1 1) 3)*10))/2" 

# evaluate
sapply(res, function(i) eval(parse(text = i)))
#A  B  C  D  E 
#2  5 50 52 26 

CodePudding user response:

One way to do this is to convert Formulae to actual one-sided formulae and then functions which are in turn evaluated inside of lst() which allows for the sequential building of objects. This relies on the metaprogramming functionality of the tidyverse framework rather than data.table.

library(dplyr)
library(purrr)

df <- data.frame(VarName = LETTERS[1:5],
                 Formulae = c("1 1", "A 3", "B*10", "A C", "D/2"))

lst(!!!map(set_names(df$Formulae, df$VarName),
           ~ quo(
             as_mapper(as.formula(reformulate(.x)))()
           )))
$A
[1] 2

$B
[1] 5

$C
[1] 50

$D
[1] 52

$E
[1] 26

CodePudding user response:

Using apply :

df <- data.frame("VarName"=c("X","Y"),"Formulae"=c("1 1","X 1"))
df$formulas <- apply(df,1,function(x)eval(parse(text = paste0(x["VarName"]," ~ ",x["Formulae"]))))

Using eval(parse(...)) structure was correct, but this should work properly. However, maybe someone will answer a cleaner proposition.

Take note that column "formulas" can not be a vector, so it is a list.

str(df)
'data.frame':   2 obs. of  3 variables:
 $ VarName : chr  "X" "Y"
 $ Formulae: chr  "1 1" "X 1"
 $ formulas:List of 2
  ..$ :Class 'formula'  language X ~ 1   1
  .. .. ..- attr(*, ".Environment")=<environment: 0x000002933f8904a8> 
  ..$ :Class 'formula'  language Y ~ X   1
  .. .. ..- attr(*, ".Environment")=<environment: 0x000002933fb6f3b8> 

This can cause some headaches in dataframes usage. I suggest using mapping tools like purrr instead of concatening everything into a dataframe in this case.

  • Related