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.
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.
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.
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()
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"
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
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With