并行plyr中的奇异环境行为
最近,我在我的工作区中创建了一个对象并行plyr中的奇异环境行为,r,parallel-processing,plyr,R,Parallel Processing,Plyr,最近,我在我的工作区中创建了一个对象factor=1,不知道base包中有一个函数factor 我打算在并行循环中使用变量factor,例如 library(plyr) library(foreach) library(doParallel) workers <- makeCluster(2) registerDoParallel(workers,cores=2) factor=1 llply( as.list(1:2), function(x) factor*x, .p
factor=1
,不知道base
包中有一个函数factor
我打算在并行循环中使用变量factor
,例如
library(plyr)
library(foreach)
library(doParallel)
workers <- makeCluster(2)
registerDoParallel(workers,cores=2)
factor=1
llply(
as.list(1:2),
function(x) factor*x,
.parallel = TRUE,
.paropts=list(.export=c("factor"))
)
如果我们检查llply
的输出,我们会看到base::factor
中的行factor\u=get(“factor”,envir=export\u env)
不返回1
(对应于用户提供的对象),而是返回base::factor
的函数定义
问题1)我如何理解这种行为?我希望输出是1
问题2)如果我给另一个包中已经定义的对象分配了一个新值(例如在我的情况下,factor
),是否有办法从R
获得警告?llply函数调用引擎盖下的“foreach”。Foreach使用“parant.frame()”确定要评估的环境。在llply的案例中,parant.frame是什么?它是llply的函数环境,没有定义因子
与其使用llply,为什么不直接使用foreach呢
library(plyr)
library(foreach)
library(doParallel)
workers <- makeCluster(2)
registerDoParallel(workers,cores=2)
factor=1
foreach(x=1:2) %dopar% {factor*x}
库(plyr)
图书馆(foreach)
图书馆(双平行)
工人首先,我应该注意,如果使用另一个未在base
中使用的变量名,则错误会消失——例如,如果我们使用a
而不是factor
。这清楚地表明llply
沿着其搜索路径在factor
(值为1的变量)之前查找base::factor
(函数)。我试图用简化版的llply
复制此问题,即
library(plyr)
library(foreach)
library(doParallel)
workers <- makeCluster(2)
registerDoParallel(workers,cores=2)
factor=1
llply_simple=function(.x,.fun,.paropts) {
#give current environment a name
tmpEnv=environment()
attr(tmpEnv,"name")="llply_simple_body"
#print all enclosing envirs of llply_simple_body (see def of allEnv below)
print(allEnv(tmpEnv))
cat("------\nResults:\n")
do.ply=function(i) {
.fun(i)
}
fe_call <- as.call(c(list(quote(foreach::foreach), i = .x), .paropts))
fe <- eval(fe_call)
foreach::`%dopar%`(fe, do.ply(i))
}
有趣的是,简化的函数实际上按预期工作(即,给出1
和2
作为结果)
因此,llply_simple
与完整的plyr::llply
函数的唯一显著区别在于后者属于一个包。让我们尝试将llply_simple
移动到一个包中
package.skeleton(list=c("llply_simple","allEnv"),name="llplyTest")
unlink("./llplyTest/DESCRIPTION")
devtools::create_description("./llplyTest",
extra=list("devtools.desc.author"='"T <t@t.com>"'))
tmp=readLines("./llplyTest/man/llply_simple.Rd")
tmp[which(grepl("\\\\title",tmp))+1]="Test1"
writeLines(tmp,"./llplyTest/man/llply_simple.Rd")
tmp=readLines("./llplyTest/man/allEnv.Rd")
tmp[which(grepl("\\\\title",tmp))+1]="Test2"
writeLines(tmp,"./llplyTest/man/allEnv.Rd")
devtools::install("./llplyTest")
突然之间,我们得到了与我2013年的原始问题相同的错误。因此,问题显然与从包中调用函数有关。让我们看看allEnv
的输出:它基本上为我们提供了llpy_simple
和llplyTest::llpy_simple
用于查找应该导出的变量的环境序列。实际上是foreach
进行导出,如果您有兴趣了解为什么foreach
真正从我们命名为llply\u simple\u body
的环境开始,请查看foreach::%dopar%
的源代码,foreach:::getDoPar
和foreach:::.foreachGlobals$fun
并遵循envir
参数的路径
我们现在可以清楚地看到,非包版本的搜索顺序与llplyTest::llpy_simple
不同,包版本将首先在base
中找到因子
谢谢你的意见。这里的问题不在于评估环境,而在于foreach
使用什么环境来查找应该导出的变量。如果使用的变量名未在base
中使用,那么问题就会消失,比如说a
而不是factor
。我知道我可以直接使用foreach
(就像你和我现在做的那样),然后错误就消失了。但回到2013年,我是一个重度plyr
用户,这个错误让我非常困惑。所以,出于好奇,我想解决这个问题。
allEnv=function(x) {
if (environmentName(x)=="R_EmptyEnv") {
return(environmentName(x))
} else {
c(environmentName(x),allEnv(parent.env(x)))
}
}
llply_simple(1:2,function(x) x*factor,list(.export="factor"))
#[1] "llply_simple_body" "R_GlobalEnv" "package:doParallel" "package:parallel"
#[5] "package:iterators" "package:foreach" "package:plyr" "tools:rstudio"
#[9] "package:stats" "package:graphics" "package:grDevices" "package:utils"
#[13] "package:datasets" "package:methods" "Autoloads" "base"
#[17] "R_EmptyEnv"
#--------
#Results:
#[[1]]
#[1] 1
#
#[[2]]
#[1] 2
package.skeleton(list=c("llply_simple","allEnv"),name="llplyTest")
unlink("./llplyTest/DESCRIPTION")
devtools::create_description("./llplyTest",
extra=list("devtools.desc.author"='"T <t@t.com>"'))
tmp=readLines("./llplyTest/man/llply_simple.Rd")
tmp[which(grepl("\\\\title",tmp))+1]="Test1"
writeLines(tmp,"./llplyTest/man/llply_simple.Rd")
tmp=readLines("./llplyTest/man/allEnv.Rd")
tmp[which(grepl("\\\\title",tmp))+1]="Test2"
writeLines(tmp,"./llplyTest/man/allEnv.Rd")
devtools::install("./llplyTest")
library(llplyTest)
llplyTest::llply_simple(1:2,function(x) x*factor,list(.export="factor"))
#[1] "llply_simple_body" "llplyTest" "imports:llplyTest" "base"
#[5] "R_GlobalEnv" "package:doParallel" "package:parallel" "package:iterators"
#[9] "package:foreach" "package:plyr" "tools:rstudio" "package:stats"
#[13] "package:graphics" "package:grDevices" "package:utils" "package:datasets"
#[17] "package:methods" "Autoloads" "base" "R_EmptyEnv"
#------
#Results:
#Error in do.ply(i) :
# task 1 failed - "non-numeric argument to binary operator"