使用 mcmapply 从父函数调用的嵌套函数内部将 R 对象保存到全局环境
Saving R objects to global environment from inside a nested function called by a parent function using mcmapply
我正在尝试编写一个 R 脚本,它使用嵌套函数将多个 data.frames(并行)保存到全局环境。下面的示例代码在 Windows 中运行良好。但是当我将相同的代码移动到 Linux 服务器时,函数 - prepare_output() 保存到全局环境的对象不会被函数 - get_output( ).
我是否遗漏了一些关于 mcmapply 如何影响 Linux 与 Windows 中的范围界定的根本不同的东西?
library(data.table)
library(parallel)
#Function definitions
default_case <- function(flag){
if(flag == 1){
create_input()
get_output()
}else{
Print("select a proper flag!")
}
}
create_input <- function(){
dt_initial <<- data.table('col1' = c(1:20), 'col2' = c(21:40)) #Assignment to global envir
}
get_output<- function(){
list1 <- c(5,6,7,8)
dt1 <- data.table(dt_initial[1:15,])
prepare_output<- function(cnt){
dt_new <- data.table(dt1)
dt_new <- dt_new[col1 <= cnt, ]
assign(paste0('dt_final_',cnt), dt_new, envir = .GlobalEnv )
#eval(call("<<-",paste0('dt_final_',cnt), dt_new))
print('contents in global envir inside:')
print(ls(name = .GlobalEnv)) # This print all object names dt_final_5 through dt_final_8 correctly
}
mcmapply(FUN = prepare_output,list1,mc.cores = globalenv()$numCores)
print('contents in global envir outside:')
print(ls(name = .GlobalEnv)) #this does NOT print dataframes generated and assigned to global in function prepare_output
save( list = ls(name = .GlobalEnv)[ls(name = .GlobalEnv) %like% 'dt_final_' ], file = 'dt_final.Rdata')
}
if(Sys.info()['sysname'] == "Windows"){numCores <- 1}else{numCores <- parallel::detectCores()}
print('numCores:')
print(numCores)
#Function call
default_case(1)
我使用嵌套结构的原因是因为 dt1 的准备很花时间,我不想通过在应用调用中执行每个循环来增加执行时间.
(对不起,我会把它写成'Answer',因为评论框太简短了)
您问题的最佳解决方案是确保您 return 您生产的对象,而不是尝试 分配 它们从函数内部到外部环境 [edit 2020-01-26] 这在并行处理中永远不起作用,因为并行工作人员无法访问主 R 进程的环境。
R 中的一个非常好的经验法则可以帮助您实现这一点:永远不要在代码中使用 assign()
或 <<-
- 无论是顺序处理还是并行处理。充其量,您可以让这样的代码在顺序模式下工作,但通常,您最终会得到难以维护且容易出错的代码。
通过关注 returning 值(在您的示例中为 y <- mclapply(...)
),您会做对的。它也更适合 R 的整体功能设计并且更自然地并行化。
我在 2019 年 1 月 11 日写了一篇博客 post 'Parallelize a For-Loop by Rewriting it as an Lapply Call',可能会帮助您过渡到这种功能性风格。
我正在尝试编写一个 R 脚本,它使用嵌套函数将多个 data.frames(并行)保存到全局环境。下面的示例代码在 Windows 中运行良好。但是当我将相同的代码移动到 Linux 服务器时,函数 - prepare_output() 保存到全局环境的对象不会被函数 - get_output( ).
我是否遗漏了一些关于 mcmapply 如何影响 Linux 与 Windows 中的范围界定的根本不同的东西?
library(data.table)
library(parallel)
#Function definitions
default_case <- function(flag){
if(flag == 1){
create_input()
get_output()
}else{
Print("select a proper flag!")
}
}
create_input <- function(){
dt_initial <<- data.table('col1' = c(1:20), 'col2' = c(21:40)) #Assignment to global envir
}
get_output<- function(){
list1 <- c(5,6,7,8)
dt1 <- data.table(dt_initial[1:15,])
prepare_output<- function(cnt){
dt_new <- data.table(dt1)
dt_new <- dt_new[col1 <= cnt, ]
assign(paste0('dt_final_',cnt), dt_new, envir = .GlobalEnv )
#eval(call("<<-",paste0('dt_final_',cnt), dt_new))
print('contents in global envir inside:')
print(ls(name = .GlobalEnv)) # This print all object names dt_final_5 through dt_final_8 correctly
}
mcmapply(FUN = prepare_output,list1,mc.cores = globalenv()$numCores)
print('contents in global envir outside:')
print(ls(name = .GlobalEnv)) #this does NOT print dataframes generated and assigned to global in function prepare_output
save( list = ls(name = .GlobalEnv)[ls(name = .GlobalEnv) %like% 'dt_final_' ], file = 'dt_final.Rdata')
}
if(Sys.info()['sysname'] == "Windows"){numCores <- 1}else{numCores <- parallel::detectCores()}
print('numCores:')
print(numCores)
#Function call
default_case(1)
我使用嵌套结构的原因是因为 dt1 的准备很花时间,我不想通过在应用调用中执行每个循环来增加执行时间.
(对不起,我会把它写成'Answer',因为评论框太简短了)
您问题的最佳解决方案是确保您 return 您生产的对象,而不是尝试 分配 它们从函数内部到外部环境 [edit 2020-01-26] 这在并行处理中永远不起作用,因为并行工作人员无法访问主 R 进程的环境。
R 中的一个非常好的经验法则可以帮助您实现这一点:永远不要在代码中使用 assign()
或 <<-
- 无论是顺序处理还是并行处理。充其量,您可以让这样的代码在顺序模式下工作,但通常,您最终会得到难以维护且容易出错的代码。
通过关注 returning 值(在您的示例中为 y <- mclapply(...)
),您会做对的。它也更适合 R 的整体功能设计并且更自然地并行化。
我在 2019 年 1 月 11 日写了一篇博客 post 'Parallelize a For-Loop by Rewriting it as an Lapply Call',可能会帮助您过渡到这种功能性风格。