TheEconomist
TheEconomist

Reputation: 63

Create multiple functions with changing names in a for loop in R

I have a dataframe "data", and want to calculate around 30 functions f(CS). The goal is to find CS for each function f(CS) with uniroot. I'm looking for a loop, that generates the functions f(CS) and the results of the functions (meaning the CS's).

Related questions: R - Naming multiple functions in a for loop and Multiple functions in for loop

The functions should have different names (function_1, function_2, function_3 etc.), and each function uses values from different rows from the dataframe "data". I tried my best to create a minimum reproducible example:

data <- data.frame(A=c(154,154,154,154,110154,102500,0,0,0,0,205200,0,0,0,0), 
                   B=c(-0.647, -0.738, -0.749, -0.691, -0.600, 2.455, 2.358, 2.289, 2.264, 2.267, 2.455, 2.358, 2.289, 2.264, 2.267))

function_1 <- function(CS){
          sum(data$A[1:5]/(1+data$B[1:5]/100+CS/10000)^(1:5) - 100000)
      }

function_2 <- function(CS){
      sum(data$A[6:10]/(1+data$B[6:10]/100+CS/10000)^(1:5) - 100000)
  }

function_3 <- function(CS){
      sum(data$A[11:15]/(1+data$B[11:15]/100+CS/10000)^(1:5) - 200000)
  }

Please note, that the constant in function_3 (200000) isnt a typo. As I mentioned, im calculating the roots with uniroot:

uniroot(function_1, c(-1000,1000))$root #267.3119
uniroot(function_2, c(-1000,1000))$root #4.500001
uniroot(function_3, c(-1000,1000))$root #14.5

I tried to use two for loops as below:

for (i in seq(from = 1, to = nrow(data)/5)){
noquote(paste0("function_",i,"")) <- function(CS){
           sum(data$A[1:5]/(1+data$B[1:5]/100+CS/10000)^(1:5))
   }
 }

for (i in seq(from = 1, to = nrow(data)/5)){
    uniroot(noquote(paste0("function_",i,")), c(-1000,1000))$root
  }

I don't know how to implement the changing constant and the changing function name, as noquote(paste0()) does not seem to work. Please let me know if you need any further information.

Upvotes: 1

Views: 1124

Answers (1)

dmp
dmp

Reputation: 825

You're close, but this isn't quite what noquote is for. Instead, use a combination of assign and get, like this:

for (i in seq(from = 1, to = nrow(data)/5)){
   assign(paste0("function_",i)), function(CS){
           sum(data$A[1:5]/(1+data$B[1:5]/100+CS/10000)^(1:5))
   }
 }

for (i in seq(from = 1, to = nrow(data)/5)){
    uniroot(get(paste0("function_",i)), c(-1000,1000))$root
}

However, I'm not sure the first loop in your example actually recreates the desired functions that you manually listed in the first code chunk in your question. Shouldn't the indexing of data change too? Like this?

for (i in seq(from = 1, to = nrow(data)/5)){
   indices <- (1+(i-1)*5):(5+(i-1)*5)
   assign(paste0("function_",i,"")), function(CS){
           sum(data$A[indices ]/(1+data$B[indices]/100+CS/10000)^(1:5))
   }
 }

Anyway, I'm sure others will be quick to say that you can probably find a way around having to loop over object names like this. Most would argue that best practices would be to manually assign the function and then make it more flexible. In the example you provided, you could construct the function so that it takes the indices as an argument, then you just have one function that you use in a variety of ways:

flexibleFunction <- function(CS, indices){
      sum(data$A[indices]/(1+data$B[indices]/100+CS/10000)^(1:5) - 100000)
  }

uniroot(flexibleFunction , c(-1000,1000), 1:5)$root
uniroot(flexibleFunction , c(-1000,1000), 6:10)$root

Upvotes: 1

Related Questions