7

My question is not about an R package specifically (I don't think. If so I apologize), rather how to construct a flexible function input when creating a function. I will use an example using a function called multiroot() in the R package rootSolve (which performs Newton Raphson method to finding roots).

For multiroot() to work you must first create a function which is a vector of some system of equations. You then pass this function as an input to the function multiroot().

For example:

require(rootSolve)
model <- function(x) c(F1 = x[1]^2+ x[2]^2 -1, F2 = x[1]^2- x[2]^2 +0.5)
ss <- multiroot(f = model, start = c(1, 1))

Everything works great. However, suppose that F1 is dynamic, meaning my equation can change depending on how many data points I've collected somewhere else. I have code that spits out an equation. Currently the equation is a character string. For example, eq1 <- "x[1]^2 + x[2]^2 - 1"

So now if I try and substitute the equation made elsewhere it becomes (Notice F1):

 model <- function(x) c(F1 = eq1, F2 = x[1]^2- x[2]^2 +0.5)
 ss <- multiroot(f = model, start = c(1, 1))

I get the error: Error in stode(y, times, func, parms = parms, ...) : REAL() can only be applied to a 'numeric', not a 'character'

I can see from the error it wants numeric, but eq1 <- as.numeric(eq1) returns "NA", so that obviously won't work. R won't understand all the characters as numeric

If I try eq1 <- as.symbol(eq1) I get the same error with "list" instead of "character".

If I try eq1 <- expression(eq1) I get the same error with "expression" instead of "list".

Maybe my ultimate question becomes, "How do I make this equation numeric?", but I don't think so. I don't see why R thinks the original input is numeric anyway.

Other things I have tried out of desperation include just making a simple function to test outside of the package.

#Test with generic function
model2 <- function(x) 2*x[1]- x[2]^2+5
model2(c(2,3))

Works great!

Now for substitution:

eq2 <- noquote("2*x[1]- x[2]^2+5")
eq2 <- substitute(2*x[1]- x[2]^2+5)
eq2 <- quote(2*x[1]- x[2]^2+5)
eq2 <- expression(2*x[1]- x[2]^2+5)
eq2 <- as.symbol(2*x[1]- x[2]^2+5)

#Test with generic function
model2 <- function(x) eq2
model2(c(2,3))

While there are no errors, the output does not provide the expected result. Rather, it returns 2*x[1]- x[2]^2+5 most of the time. I know each has a different structure, class, etc.

So, my question is how can I make the function input dynamic by inputting a variable that can change elsewhere in the code? I would like it to work with the package I used in the example, but hopefully my question isn't package dependent. My specific question may be how do I turn my variable that is a character string to numeric, but that doesn't quite make sense to me.

Thanks for any help provided.

Scott Hunter
  • 254
  • 2
  • 14
  • You can also construct a function from components, but I'm not sure it is what you want: `f <- function() {}; body(f) <- expression(x[1]^2 + x[2]^2 - 1); formals(f) <- alist(x=)` or all in one go: `as.function(alist(x=, x[1]^2 + x[2]^2 - 1))` – thelatemail Jan 25 '18 at 00:41
  • I think this might be a duplicate: https://stackoverflow.com/questions/29421816/turning-a-string-into-a-function-line-in-r/29421965?s=6|38.7397#29421965 – IRTFM Jan 25 '18 at 01:56
  • @42 Yes, it seems very similar. Good catch! I see you are the one who answered. I did search quite a bit before I posted. – Scott Hunter Jan 25 '18 at 21:30

4 Answers4

2

Instead of thinking of your input as a string to be converted, you can think of the input as a function:

eq1 <- "x[1]^2 + x[2]^2 - 1" # change this line to:
eq1 <- function(x) { x[1]^2 + x[2]^2 - 1 }

Now eq1 is a function you can pass to model, evaluated at the same argument:

model <- function(x) c(F1 = eq1(x), F2 = x[1]^2- x[2]^2 +0.5)
ss <- multiroot(f = model, start = c(1, 1))

> ss
$root
[1] 0.5000000 0.8660254

$f.root
          F1           F2 
2.323138e-08 2.323308e-08 

$iter
[1] 5

$estim.precis
[1] 2.323223e-08
twedl
  • 1,588
  • 1
  • 17
  • 28
  • @JT Thanks for the quick response. It is almost what I need. I will try and mess around later. But in your answer where you say "change this line to:", are you saying change it by hand? This is what I want to avoid. Take me from line 1 to line 2 automatically and I'm golden. – Scott Hunter Jan 25 '18 at 00:20
  • Right, I mean start off by writing it as a function (i.e., don't write line 1 at all). I suppose it depends how this bit of your code works: "I have code that spits out an equation." If you can get it to spit out a function, then you're done. If not... (but the other answers are good for that!) – twedl Jan 25 '18 at 00:23
  • Yes, the answer below is more inline with what I was thinking. However, I'm extremely grateful for your efforts. – Scott Hunter Jan 25 '18 at 21:31
2

To illustrate the problem in your code, I will use myfunc() a user defined function instead of multiroot(), which requires me to install a new package.

simple math

2*2-3^2+5
# [1] 0

math inside a function

model2 <- function(x) 2*x[1]- x[2]^2+5  
model2(c(2,3))
# [1] 0

math inside a function using equation

eq2 <- '2*x[1]- x[2]^2+5'      
model2 <- function(x) eval(parse(text = eq2))
model2(c(2,3))    
# [1] 0

math inside a function - another example using myfunc() instead of multiroot()

myfunc <- function(f , start )
{
  do.call(f, args = list(start))
}
model <- function(x) c(F1 = x[1]^2+ x[2]^2 -1, F2 = x[1]^2- x[2]^2 +0.5)
myfunc(f = model, start = c(1, 1))
# F1  F2 
# 1.0 0.5

math inside a function using equation and myfunc()

  • returns equation
eq1 <- 'x[1]^2+ x[2]^2 -1'
model <- function(x) c(F1 = eq1, F2 = x[1]^2- x[2]^2 +0.5)
myfunc(f = model, start = c(1, 1))
#                  F1                  F2 
# "x[1]^2+ x[2]^2 -1"               "0.5"
  • returns value after evaluating the equation
eq1 <- 'x[1]^2+ x[2]^2 -1'
model <- function(x) c(F1 = eval(parse(text = eq1)), F2 = x[1]^2- x[2]^2 +0.5)
myfunc(f = model, start = c(1, 1))
#  F1  F2 
# 1.0 0.5
Sathish
  • 12,453
  • 3
  • 41
  • 59
  • I'm not exactly sure what all eval and parse do, or why I need text =, but I'll try to read up on that. Thanks again! – Scott Hunter Jan 25 '18 at 21:41
2

I think eval is what you need. Particularly, for your example, you could use:

eq2 <- substitute(2*x[1]- x[2]^2+5)

#Test with generic function
model2 <- function(x) eval(eq2)
model2(c(2,3))

In a more general sense, if you want you want to pass an expression as a string, you could use

expre = "2*x[1]- x[2]^2+5"

model2 <- function(expr,x) eval(parse(text=expr)) 
model2(expre,c(2,3)) 

The trick is to evaluate inside the function so you get the result from the appropriate namespace.

VFreguglia
  • 2,129
  • 4
  • 14
  • 35
1

You don't need to use eval(parse(.)) but only parse. It returns an unevaluated language object suitable for passing the the body<- function:

f1 <- function(x){}
body(f1) <- parse( text=eq1)
 require(rootSolve)
Loading required package: rootSolve

model <- function(x) c(F1 = f1(x), F2 = x[1]^2- x[2]^2 +0.5)
ss <- multiroot(f = model, start = c(1, 1))
ss
#--------------
$root
[1] 0.5000000 0.8660254

$f.root
          F1           F2 
2.323138e-08 2.323308e-08 

$iter
[1] 5

$estim.precis
[1] 2.323223e-08

The parse-step creates the same tree structure that would have occurred during a "full" function construction but leaves it open for further evaluation with arguments to be supplied later.

IRTFM
  • 258,963
  • 21
  • 364
  • 487