Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you construct a flexible function input when creating a function in R?

Tags:

r

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.

like image 210
Scott Hunter Avatar asked Jan 24 '18 23:01

Scott Hunter


People also ask

What type of arguments can a function take in R?

Arguments are the parameters provided to a function to perform operations in a programming language. In R programming, we can use as many arguments as we want and are separated by a comma. There is no limit on the number of arguments in a function in R.

What are the different parts of a function in R?

All R functions have three parts: the body() , the code inside the function. the formals() , the list of arguments which controls how you can call the function. the environment() , the “map” of the location of the function's variables.


3 Answers

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
like image 191
Sathish Avatar answered Sep 22 '22 15:09

Sathish


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
like image 36
twedl Avatar answered Sep 21 '22 15:09

twedl


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.

like image 35
Freguglia Avatar answered Sep 18 '22 15:09

Freguglia