pikovayadama
pikovayadama

Reputation: 828

In R, How do I properly pass a function and a set of parameters to said function so that it executes properly?

I am working on an R project, and I have many different functions (I'm calculating RMSEs on various data sets with various requirements).

I am currently using the "do.call()" function to invoke the function name I'm passing in, but this causes my whole system to stall and nothing works. This has happened many times over, and I've had to restart R Studio (using version 4.0.2). I would like to pass in a function as an argument into my parent function (which is recursive but only to 2 passes), and I would like to be able to pass in the parameters from the parent function to the child functions, as well as the recursive function call.

I'm not sure of the correct execution of this.

Any help on where I'm going wrong is greatly appreciated.

Currently, my code is as follows:

#find_generic_lambda is the parent function that is called, and the FUN argument is the named function I would like to pass in to execute inside 

find_generic_lambda <- function(seq_start, seq_end, seq_increment, FUN, detailed_flag = FALSE, training_set, testing_set)
{
  lambdas <- seq(seq_start, seq_end, seq_increment)
  params = c(lambdas, train_set, test_set)
  #invoking the passed-in function here with the parameters I'm setting
  #this is where the code stumbles
  RMSE <- sapply(lambdas, do.call(FUN, params))
  #find the smallest lamdba
  qplot(lambdas, RMSE)  
  #saving the first round lambda 
  min_lambda_first_try <- lambdas[which.min(RMSE)]
  min_lambda_first_try
  
  if (detailed_flag)
  {
    #if this is the first iteration of the function, continue with taking a 10% lower and 10% higher lambda value to iterate through new lambdas that are much more granuluar, with increments at 10% of what they were previously.
    new_lambda_range = (seq_end + seq_start)/10
    new_lambda_range
    min_lambda_first_try <- find_generic_lambda(seq_start = min_lambda_first_try - new_lambda_range, seq_end = min_lambda_first_try + new_lambda_range, 
                                                seq_increment = seq_increment/10, FUN, detailed_flag = FALSE, training_set = training_set, testing_set = testing_set)
  }
  return (min_lambda_first_try)
  
}

#this is one of the functions that will be passed in as a parameter
regularized_rmse_3 <- function(l, train_set, test_set)
{
  mu <- mean(train_set$rating)
  just_the_sum <- train_set %>% 
    group_by(movieId) %>% 
    summarize(s = sum(rating - mu), n_i = n())
  
    predicted_ratings <- test_set %>% 
    left_join(just_the_sum, by='movieId') %>% 
    mutate(b_i = s/(n_i+l)) %>%
    mutate(pred = mu + b_i) %>%
    pull(pred)
    return(RMSE(predicted_ratings, test_set$rating))
}


rmse3_lambda <- find_generic_lambda(seq_start=0, seq_end=10, seq_increment=0.5, 
                                    FUN="regularized_rmse_3", 
                                    detailed_flag = TRUE, training_set=training_set, testing_set=testing_set)

Upvotes: 0

Views: 71

Answers (1)

Ben Toh
Ben Toh

Reputation: 782

Expanding on my comments:

Here's a simplified version of your functions (so I can make example dataset) -

f <- function (l_candidate, FUN) {
  RMSE <- sapply(l_candidate, FUN)
  l_min_RMSE <- l_candidate[which.min(RMSE)]
  return(l_min_RMSE)
}

g <- function (l, trainset, testset) {
  p <- mean(trainset + l)
  sqrt(mean((testset - p)^2))
}

trainset <- c(1, 1, 2, 1)
testset <- c(3, 4)

Then:

f(1:5, FUN = function (x) g(x, trainset, testset))
# [1] 2

So you pass the function g via a wrapper function into f and it will do the job for you.

Alternative R allows you to create a function out of another function:

g <- function (trainset, testset) function (l) {
  p <- mean(trainset + l)
  sqrt(mean((testset - p)^2))
}

g1 <- g(trainset, testset)
g1(1)
# [1] 1.346291

In this situation, g() takes two arguments, and return a function that takes 1 argument l. So you can create a new function g1() out of g().

Then you can pass it to your parent function giving you the same results in this example:

f(1:5, FUN = g1)
# [1] 2

Upvotes: 1

Related Questions