我有一个data.frame的单元格,值和坐标。它驻留在全球环境中。
> head(cont.values)
cell value x y
1 11117 NA -34 322
2 11118 NA -30 322
3 11119 NA -26 322
4 11120 NA -22 322
5 11121 NA -18 322
6 11122 NA -14 322
因为我的自定义函数需要几秒钟来计算单个单元格(我需要计算数万个单元格),我不想复制已经有值的单元格的计算。以下解决方案试图避免这种情况。每个单元格可以独立计算,尖叫以便并行执行。
我的函数实际上做的是检查指定的单元格编号是否有值,如果是NA,则计算它并插入它代替NA。
我可以使用应用函数系列运行我的魔术函数(结果为value
)对应cell
}并且在apply
内,我可以读写cont.values
没有问题(它在全球环境中)。
现在,我希望并行运行(使用snowfall
)并且我无法从单个核心读取或写入此变量。
问题:当并行执行函数时,什么解决方案能够从工作者(核心)内部驻留在全局环境中的动态变量读取/写入。有没有更好的方法来做到这一点?
答案 0 :(得分:4)
这取决于所讨论的功能是什么,当然,但我担心snowfall
在那里不会有太多帮助。事实上,您必须将整个数据框导出到不同的核心(请参阅?sfExport
)并仍然找到一种方法来组合它。这种方式超越了改变全球环境价值的全部目的,因为您可能希望尽可能降低内存使用率。
您可以深入了解snow
的低级功能,让它变得有效。请参阅以下示例:
#Some data
Data <- data.frame(
cell = 1:10,
value = sample(c(100,NA),10,TRUE),
x = 1:10,
y = 1:10
)
# A sample function
sample.func <- function(){
id <- which(is.na(Data$value)) # get the NA values
# this splits up the values from the dataframe in a list
# which will be passed to clusterApply later on.
parts <- lapply(clusterSplit(cl,id),function(i)Data[i,c("x","y")])
# Here happens the magic
Data$value[id] <<-
unlist(clusterApply(cl,parts,function(x){
x$x+x$y
}
))
}
#now we run it
require(snow)
cl <- makeCluster(c("localhost","localhost"), type = "SOCK")
sample.func()
stopCluster(cl)
> Data
cell value x y
1 1 100 1 1
2 2 100 2 2
3 3 6 3 3
4 4 8 4 4
5 5 10 5 5
6 6 12 6 6
7 7 100 7 7
8 8 100 8 8
9 9 18 9 9
10 10 20 10 10
您仍然需要复制(部分)数据才能将其发送到核心。但是,当您在数据框上调用snowfall
高级函数时,无论如何都会发生这种情况,因为snowfall
无论如何都会使用snow
的低级函数。
另外,不应忘记,如果更改数据帧中的一个值,整个数据帧也会复制到内存中。因此,当他们从群集中返回时,您将不会通过逐个添加值来赢得那么多。您可能想尝试一些不同的方法并进行一些内存分析。
答案 1 :(得分:4)
工作人员咨询价值观的中央商店模式在CRAN的rredis包中实施。我们的想法是Redis服务器维护一个键值对的存储(您的全局数据框,重新实现)。工作人员查询服务器以查看是否已计算该值(redisGet
),如果不进行计算并将其存储(redisSet
),以便其他工作人员可以重复使用它。工人可以是R脚本,因此很容易扩展劳动力。这是一个非常好的替代并行范例。这是一个使用“memoizing”每个结果概念的例子。我们有一个缓慢的功能(睡眠一秒钟)
fun <- function(x) { Sys.sleep(1); x }
我们写了一个'memoizer',它返回fun
的变体,首先检查x
的值是否已经计算过,如果是,则使用
memoize <-
function(FUN)
{
force(FUN) # circumvent lazy evaluation
require(rredis)
redisConnect()
function(x)
{
key <- as.character(x)
val <- redisGet(key)
if (is.null(val)) {
val <- FUN(x)
redisSet(key, val)
}
val
}
}
然后我们记住我们的功能
funmem <- memoize(fun)
然后去
> system.time(res <- funmem(10)); res
user system elapsed
0.003 0.000 1.082
[1] 10
> system.time(res <- funmem(10)); res
user system elapsed
0.001 0.001 0.040
[1] 10
这确实需要在R外部运行的redis服务器,但非常容易安装;请参阅rredis软件包附带的文档。
内部-R并行版本可能
library(snow)
cl <- makeCluster(c("localhost","localhost"), type = "SOCK")
clusterEvalQ(cl, { require(rredis); redisConnect() })
tasks <- sample(1:5, 100, TRUE)
system.time(res <- parSapply(cl, tasks, funmem))
答案 2 :(得分:1)
我同意Joris您需要将数据复制到其他核心。
从积极的方面来说,您不必担心核心内部NA
是否在数据中。
如果您的原始data.frame
被称为cont.values
:
nnaidx<-is.na(cont.values$value) #where is missing data originally
dfrnna<-cont.values[nnaidx,] #subset for copying to other cores
calcValForDfrRow<-function(dfrRow){return(dfrRow$x+dfrRow$y)}#or whatever pleases you
sfExport(dfrnna, calcValForDfrRow) #export what is needed to other cores
cont.values$value[nnaidx]<-sfSapply(seq(dim(dfrnna)[1]), function(i){calcValForDfrRow(dfrnna[i,])}) #sfSapply handles 'reordering', so works exactly as if you had called sapply
应该很好地工作(除非错别字)