0

I have a function in string format "dnorm(0,1)" which I want to evaluate but I need to make a change to string first. How do I subsequently run the function from character format?

function_char_format = str_replace("dnorm(0,1),.,"r") %>%
str_replace(".","r") %>% 
str_replace("\\(","(1e4, ") #this returns "rnorm(100,0,1)"
execute_function??(function_char_format) 

context: (I'm making a function where I'm extracting a distribution like "dnorm" from an r-object and need the corresponding "rnorm()" . The solution should also be applicable for "dbinom", "dpois" etc.)

Edit: added str_replace line

WiggyStardust
  • 182
  • 1
  • 10
  • What do you have and what do you expect to get? Your question is unclear. What do you want to change it to? What parameters do you need to give it? – Onyambu Apr 23 '18 at 19:06
  • I got `"dnorm(0,1)"` and I need output from `rnorm(100,0,1)` i.e. a list of number. (Just realized I missed a str_replace call where I add the 100) – WiggyStardust Apr 23 '18 at 19:09

2 Answers2

4

I think you need a combination of eval(parse(text=...)) and gsub(pattern, new_pattern, ...)

S <- "dnorm(0,1)"
set.seed(1)
eval(parse(text=S))
# 0.2419707
set.seed(1)
T <- gsub("norm", "pois", S)
eval(parse(text=T))
# 0.3678794
CPak
  • 13,260
  • 3
  • 30
  • 48
  • 1
    It's often a good idea to accompany any mention of `eval(parse(text = ...))` with a minimal note that it is generally considered a bad idea, and should be avoided if at all possible. (Even if it's what the OP actually needs, believe me, lots of unsuspecting beginners will land on this question via Google and use it unthinkingly.) – joran Apr 23 '18 at 19:12
  • Thanks @joran - I wasn't aware that this was generally frowned upon. – CPak Apr 23 '18 at 19:22
  • 1
    See `fortunes::fortune(106)` and `fortunes::fortune(181)` and https://stackoverflow.com/q/13649979/324364 – joran Apr 23 '18 at 19:25
2

I actually am a fan of parse(text = ...).

For example, if you have some named arguments:

old <- 'dnorm(x = 5, mean=1)'
cl <- parse(text = old)[[1L]]
eval(cl)
# [1] 0.0001338302

cl[[1L]] <- dlnorm
eval(cl)
# [1] 0.06626564

cl$mean <- 5
eval(cl)
# [1] 0.0002544689

But in your case, we can avoid parse(text = ...) with do.call or call since unnamed arguments can be passed by position:

old <- 'dnorm(0, 1)'
new <- 'rnorm'


args <- strsplit(old, '[(),]')[[1L]]
args <- lapply(args, type.convert, as.is = TRUE)

do.call(args[[1L]], args[-1L])
# [1] 0.2419707

## add more arguments like n for rnorm
set.seed(1L)
do.call(new, c(5L, args[-1L]))
# [1] -0.6264538  0.1836433 -0.8356286  1.5952808  0.3295078

## or use a named list to pass to a specific argument
# do.call(new, c(n = 5L, args[-1L]))

## or change to another function with similar use
args[[1L]] <- 'pnorm'
cl <- do.call(call, args)
eval(cl)
# [1] 0.1586553    

cl[[1L]] <- pnorm
eval(cl)
# [1] 0.1586553

Statistically speaking, there are almost 400 fortunes, and only 2 of them are anti-parse(text). That's pretty good..

rawr
  • 20,481
  • 4
  • 44
  • 78