3

I'm writing some code to calculate derivatives of functions. I managed to generate 1st and 2nd derivatives for 1 variable and store in the environment as functions, so I can plot them later. Now I'm trying to calculate the partial derivates for equations with 2 or more variables and I can't find the code that can generate as much functions in the environment as partial derivatives of the function.

To clarify, for 1 variable I used:

    f <- function(x) cos(20*x)*exp(-1*x)
    F.<- function (x) eval(D(as.expression(body(f)), "x"))
    F..<- function (x) eval(D(as.expression(D(as.expression(body(f)), "x")),"x"))

and it worked perfect, I get the 3 functions in the environment: functions

But for more than 1 variable I have to loop through this functions and generate as many funtions as partial derivatives of the equation.

My question is: how can I generate a loop that calculates the partial derivatives of a function and stores them as functions, each one with a custom name?

I tried the derivative function inside a for loop but couldn't manage to define different names for each calculation of the derivatives:

    for (i in 1:nro_variables) {
      var_D = vector_variables[i]  
      F.<- function (x) eval(D(as.expression(body(f)), var_D))
    }
Heikki
  • 2,214
  • 19
  • 34
y154707
  • 93
  • 5
  • Can you give an example of a function that has more than one variable and the output that you would expect to get for that input? That way possible solutions can be tested and verified. – MrFlick Oct 13 '20 at 21:10
  • @MrFlick, yes. Say I define function "f" as: f= -log(1-x-y)-log(x)-log(y). its partial derivatives are: F.= 1/(1-x-y)-1/x with respect to "x", and F.= 1/(1-x-y)-1/y with respect to "y". I'm trying to get both equations stored in the environment with different name (eg: "F.x" for the partial derivative with respect to "x" and "F.y" for the partial with respect to "y") – y154707 Oct 14 '20 at 11:08

2 Answers2

2

Similar to avoiding the use of assigning many separate similar objects to flood your global environment, consider using a single list that can index elements for better serial organization. See @GregorThomas's best practices answer advocating:

Don't ever create d1 d2 d3, ..., dn in the first place. Create a list d with n elements.

Specifically, if vector_variables is a character vector, sapply using simplify=FALSE will return a named list of functions. And yes, functions within lists can still be called.

partial_deriv_funcs <- sapply(vector_variables, function(var_D) {
    f <- function(x) cos(20*x)*exp(-1*x)
    return(function(x) eval(D(as.expression(body(f)), var_D)))
}, simplify = FALSE)

To call function elements singularly or iteratively:

# CALL SINGLE FUNCTION WITH SINGLE PARAM
result <- partial_deriv_funcs[['var1']](param)

# CALL SAME FUNCTION WITH VECTOR OF PARAMS
results <- lapply(param_vector, partial_deriv_funcs[['var1']])

# CALL ALL FUNCTIONS USING SAME PARAM
results <- lapply(partial_deriv_funcs, function(f) f(param))

# CALL ALL FUNCTIONS EACH WITH DIFFERENT PARAM
results <- mapply(function(f,p) f(p), partial_deriv_funcs, param_per_func_vector, SIMPLIFY= FALSE)
results <- Map(function(f,p) f(p), partial_deriv_funcs, param_per_func_vector) # EQUIVALENT
Parfait
  • 104,375
  • 17
  • 94
  • 125
  • Thanks @Parfait, it worked great!. I added a For...loop to add the 'Environment' value of each element on the list and changed the default value (vector_variables) by the variable selected for the partial derivative. Now I can print the derivative equation and use it to evaluate values and plot them. – y154707 Oct 19 '20 at 20:09
  • Excellent! Great to hear and glad to help. – Parfait Oct 19 '20 at 20:12
1

eval and parse can be used to define the function and assign can give it the correct name.

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

fns <- c("f", "g", "h")

for (fn in fns) {
  for (variable in formalArgs(fn)) {
    function_name <- glue::glue("{toupper(fn)}.{variable}")
    fn_definition <- eval(parse(text = glue::glue("function({paste0(formalArgs(fn), collapse = ',')}) eval(D(as.expression(body({fn})), '{variable}'))")))

    assign(
      function_name,
      fn_definition
    )
  }
}

ls.str(mode = "function")
#> f : function (x)  
#> F.x : function (x)  
#> fn_definition : function (x, y, z)  
#> g : function (x, y)  
#> G.x : function (x, y)  
#> G.y : function (x, y)  
#> h : function (x, y, z)  
#> H.x : function (x, y, z)  
#> H.y : function (x, y, z)  
#> H.z : function (x, y, z)  
Paul
  • 8,734
  • 1
  • 26
  • 36