R中函数与参数传递执行问题求助(含递归场景)
解决R项目中RMSE计算卡顿崩溃问题与递归函数参数传递优化
咱们先揪出导致卡顿崩溃的核心问题,再一步步修正代码逻辑,实现正确的函数参数传递和递归细化:
核心问题分析
sapply与do.call用法完全错误
你当前写的RMSE <- sapply(lambdas, do.call(FUN, params))是致命问题:sapply需要的是一个能接收单个lambda的函数,但你直接执行了do.call(FUN, params),相当于把整个lambdas向量、拆分后的训练/测试集一股脑传给了子函数,这会让子函数处理完全超出预期的超大输入,直接撑爆内存导致崩溃。参数构造逻辑错误
params = c(lambdas, train_set, test_set)里的c()会把数据框拆成单个向量元素,完全破坏了数据集的结构,子函数拿到的根本不是完整的训练/测试集。函数传递方式冗余
传入FUN="regularized_rmse_3"字符串完全没必要,直接传递函数对象更高效,还能避免环境查找的潜在问题。
修正后的完整代码
父函数 find_generic_lambda
library(ggplot2) library(dplyr) 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) # 正确遍历每个lambda,调用子函数计算RMSE RMSE <- sapply(lambdas, function(current_lambda) { # 直接传递当前lambda、完整的训练集和测试集给子函数 FUN(current_lambda, training_set, testing_set) }) # 绘制RMSE曲线(如果不需要递归时重复绘图,可加判断只在第一次绘制) print(qplot(lambdas, RMSE, xlab = "Lambda", ylab = "RMSE", main = "RMSE vs Lambda")) # 找到当前区间的最优lambda min_lambda <- lambdas[which.min(RMSE)] # 开启细化模式时,进行一次递归缩小范围 if (detailed_flag) { # 计算原区间的10%作为新的搜索范围,中心在当前最优lambda original_total_range <- seq_end - seq_start new_search_range <- original_total_range / 10 min_lambda <- find_generic_lambda( seq_start = min_lambda - new_search_range, seq_end = min_lambda + new_search_range, seq_increment = seq_increment / 10, FUN = FUN, detailed_flag = FALSE, training_set = training_set, testing_set = testing_set ) } return(min_lambda) }
子函数 regularized_rmse_3
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(), .groups = "drop" # 显式取消分组,避免后续join的分组问题 ) predicted_ratings <- test_set %>% left_join(just_the_sum, by = "movieId") %>% mutate( b_i = s/(n_i + l), 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 )
关键优化点说明
- 修复
RMSE计算逻辑:用匿名函数包裹子函数调用,确保每个lambda单独传入,同时完整传递训练/测试集,彻底解决内存过载问题。 - 修正递归范围计算:原代码的
new_lambda_range逻辑错误,现在用原区间的10%作为新搜索范围,确保递归时聚焦在最优lambda附近。 - 显式取消分组:在
summarize后添加.groups = "drop",避免分组状态传递到后续操作引发警告或错误。 - 函数传递优化:直接传递函数对象,比字符串更高效,也避免了
do.call查找函数时的环境问题。
额外性能建议
如果你的数据集非常大,可以考虑用purrr::map_dbl代替sapply,语法更清晰且性能相当;另外如果不需要递归时重复绘制图表,可以在绘图前加个判断,比如只在detailed_flag为TRUE时绘制第一次的图。
内容的提问来源于stack exchange,提问作者pikovayadama
相关产品推荐
相关产品推荐

