8

I want to write an R function that takes a mathematical function in x and returns a new function in x as an output. For example:

The input should be passed in as a mathematical function (or relation) in x:

g <- x^2 + 9*x + log(x)

And the resulting output should be:

function(x) (exp(g))

i.e. I want to return the symbolic exponential expression of the original function in x i.e. exp(x^2 + 9*x + log(x)) in this illustrative example

So ideally it would return the function object:

function(x) (exp(x^2 + 9*x + log(x)))

I tried as follows:

test <- function(g){
h <- function(x){exp(g)}
return(h)
}
m <- test(x^2 + 9*x + log(x))
m(10)

So m(10) should return:

exp(10^2 + 9*10 + log(10))

which is exp(192.3026) in this case.

Could anyone show how to do this please?

user4687531
  • 1,021
  • 15
  • 30
  • this is similar to symbolic differentiation, isn't it? `deriv(expr, ...)` – jogo Dec 09 '15 at 09:22
  • No - no differentiation involved here. Just want to create/ return a new function object (i.e. exp(10^2 + 9*10 + log(10))) from the function that is passed in as an input (i.e. x^2 + 9*x + log(x)). – user4687531 Dec 09 '15 at 09:30
  • because of that I wrote "similar". I know that you want another transformation of the symbolic expression. – jogo Dec 09 '15 at 09:32
  • Sure - in the sense of a functional transformation it is similar - yes – user4687531 Dec 09 '15 at 09:36
  • Yes @JoshO'Brien - you are correct. It is "exp(192.3026)" that I meant. It is now edited thanks! – user4687531 Dec 09 '15 at 09:39

3 Answers3

10

You could use package functional:

library(functional)
fun <- Compose(function(x) x^2 + 9*x + log(x), exp)
fun(1)
#[1] 22026.47
Roland
  • 127,288
  • 10
  • 191
  • 288
  • Please consider editing your post to add more explanation about what your code does and why it will solve the problem. An answer that mostly just contains code (even if it's working) usually wont help the OP to understand their problem. – SuperBiasedMan Dec 09 '15 at 14:56
  • 8
    @SuperBiasedMan Please cease posting these useless comments. – Roland Dec 09 '15 at 15:31
  • 5
    @SuperBiasedMan Please consider using more restraint with review auto-comments. While oftentimes generic comments can be a constructive addition to a Q&A, comments that fail to recognize the difference between code that requires lengthy explanation and code that does not usually won't help improve the content on StackOverflow. ;) – joran Dec 09 '15 at 18:57
7

Here is one approach:

test <- function(e) {
    ee <- substitute(e)
    eee <- substitute(exp(X), list(X=ee))
    f <- function(x) {}
    body(f) <- eee
    environment(f) <- parent.frame()
    f
}

## Check that it works
m <- test(x^2 + 9*x + log(x))
m
# function (x) 
# exp(x^2 + 9 * x + log(x))
m(1)
# [1] 22026.47
m(1) == exp(10)
# [1] TRUE
Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
  • 1
    For more on building functions programmatically, [see here](http://stackoverflow.com/questions/12982528/how-to-create-an-r-function-programmatically) – Josh O'Brien Dec 09 '15 at 10:01
  • 1
    Please consider editing your post to add more explanation about what your code does and why it will solve the problem. An answer that mostly just contains code (even if it's working) usually wont help the OP to understand their problem. – SuperBiasedMan Dec 09 '15 at 14:56
  • 1
    @SuperBiasedMan -- Please consider pointing specifically to what it is about the code you don't understand and would like help with. Or do you happen to know the OP, and know that answers that contain just code (using common and well-documented functions) won't help them? (In which case, my bad -- could you give me some pointers about how experienced they are as an R user, and perhaps ask them to contact me with their own questions?) – Josh O'Brien Dec 10 '15 at 17:15
3

edit - for functionality in question

f <- function(...) {
  l <- eval(substitute(alist(x = x, ...)))
  l[[2]] <- substitute(exp(X), list(X = l[[2]]))
  as.function(`names<-`(l, l[sapply(l, is.symbol)]))
}

g <- f(x^2 + 2*x + 5)
# function (x = x) 
#   exp(x^2 + 2 * x + 5)

g(1)
# [1] 2980.958

Here is another way for a general case:

f <- function(...) {
  l <- eval(substitute(alist(...)))
  as.function(`names<-`(l, l[sapply(l, is.symbol)]))
}

g <- f(x, x^2 + 9*x + log(x))

# function (x = x) 
#   x^2 + 9 * x + log(x)

g(10)
# [1] 192.3026

This version will also work for any number of variables, just define them followed by the function:

g <- f(x, y, z, x + 2 * y + z ** 3)

# function (x = x, y = y, z = z) 
#   x + 2 * y + z^3

g(1, 2, 0)
# [1] 5

There may be a better way to add ... to functions, but here is how you can do that

f <- function(..., use_dots = FALSE) {
  l <- eval(substitute(alist(...)))
  if (use_dots)
    l <- c(head(l, -1), list('...' = as.symbol('...')), tail(l, 1))
  as.function(`names<-`(l, l[sapply(l, is.symbol)]))
}

So now you don't have to name all the variables/arguments

g <- f(x, y, plot(x, y, ...), use_dots = TRUE)
g(1:5, 1:5, main = 'main title', pch = 16, col = 3, cex = 3, xpd = NA)

enter image description here

rawr
  • 20,481
  • 4
  • 44
  • 78
  • Thanks. I tried the following for my application but didn't quite work: f <- function(...) { l <- eval(substitute(alist(...))) as.function(`names<-`(l, l[sapply(l, is.symbol)])) } test2 <- function(g){ # Create the function h(x) which is a function # of the g(x) input function passed in as a string h <- f(x, exp(g)) return(h) } y <- test2(x^2 + 2*x) y(10) y_prime <- D(body(y), 'x') y_prime – user4687531 Dec 09 '15 at 10:53
  • I wanted to return the exp(function) and then take its derivative. Could you please assist as this method seems concise and useful if I can get it to work – user4687531 Dec 09 '15 at 10:55
  • @user4687531 change the g to x and it will work. the variables need to match, otherwise the function doesn't know what to use, for example `test2 <- function(x) f(x, exp(x))` – rawr Dec 09 '15 at 10:58
  • I don't believe that works as intended. If I do y <- test2(x^2 + 2*x + 5). I expect the y to equal exp(x^2 + 2*x + 5). It currently evaluates to exp(x). Could you pls show how to adapt your code to do this by adapting my code in the comment above? – user4687531 Dec 09 '15 at 11:12
  • @user4687531 see edit. it was intended that you do that in the first version via `g <- f(x, exp(x^2 + 9*x + log(x)))` but I left of the `exp`. since your desired function is always to use `exp`, this is a little less general and needs a special case to handle as in the edit, so now using that version of `f`, you can simply do `g <- f(x^2 + 9*x + log(x))` as expected. I mean less general because this function will only have one variable, x, and will only be of the form `exp(...)` where `...` is whatever you pass to `f` – rawr Dec 09 '15 at 14:57
  • Thanks @rawr. This was most helpful for my purpose – user4687531 Dec 10 '15 at 22:31