优化/并行化R - 处理计算R中SPI的大数据集

时间:2016-12-12 11:29:50

标签: r matrix parallel-processing

我正在处理一个大型全球降水数据集(具有非常精细的空间分辨率),以使用R中的SPEI package来计算标准化降水指数。

我的一般问题是指使用非常大的数据优化数据处理。我在其他帖子(hereherehere实例)中找到了一些讨论,但似乎与我的情况类似。

我的输入是具有超过20年的每月观察(> 20 * 12行)的降水时间序列的矩阵。 1,000,000点(列)。 SPI的计算为每个时间序列执行一系列步骤,并将指数计算为与中值的标准偏差。 输出是一个列表,结果矩阵($ fit)具有相同的输入矩阵大小。

这是代码示例:

require(SPEI)

#generating a random values matrix 
data<-replicate(200, rnorm(240))
# erasing negative values
data[data<=0]=0

spi6 <- spi(data, 6, kernel = list(type = 'rectangular', shift = 0), distribution = 'PearsonIII', fit = 'ub-pwm', na.rm = FALSE, ref.start=NULL, ref.end=NULL, x=FALSE, params=NULL)

#testing the results
plot(spi6$fitted[,67])
#taking my results out
results <- t(spi6$fitted)

此脚本运行正常,但如果我增加点数(在这种情况下为列),则处理时间会呈指数增长。直到内存不足问题:

Warning messages:
1: In std[ff, s] <- qnorm(cdfpe3(acu.pred[ff], p3par)) :
  Reached total allocation of 16253Mb: see help(memory.size)
2: In std[ff, s] <- qnorm(cdfpe3(acu.pred[ff], p3par)) :
  Reached total allocation of 16253Mb: see help(memory.size)
3: In NextMethod("[<-") :
  Reached total allocation of 16253Mb: see help(memory.size)
4: In NextMethod("[<-") :
  Reached total allocation of 16253Mb: see help(memory.size)
5: In std[ff, s] <- qnorm(pze + (1 - pze) * pnorm(std[ff, s])) :
  Reached total allocation of 16253Mb: see help(memory.size)
6: In std[ff, s] <- qnorm(pze + (1 - pze) * pnorm(std[ff, s])) :
  Reached total allocation of 16253Mb: see help(memory.size)
7: In NextMethod("[<-") :
  Reached total allocation of 16253Mb: see help(memory.size)
8: In NextMethod("[<-") :
  Reached total allocation of 16253Mb: see help(memory.size)

如何拆分输入矩阵(或将过程拆分到输入矩阵上),以便并行处理列向量组(每个列向量都是特定点的全时序列),而不会丢失信息(或混乱)我的数据)? 感谢。

1 个答案:

答案 0 :(得分:3)

我发现一个非常线性的,而不是指数的处理时间:

system.time(spi(data[,1], 6))
system.time(spi(data[,1:10], 6))
system.time(spi(data[,1:100], 6))

如果您看到指数增长,则可能是由于RAM分配过多的问题造成的。

一个简单的解决方案是将计算分解为矩阵:

spi6 <- data*NA
system.time(
  for (i in 1:100) spi6[,i] <- spi(data[,i], 6)$fitted
)

或者,具有类似的效率:

system.time(
  spi6 <- apply(data[,1:100], 2, function(x) spi(x, 6)$fitted)
)

如您所见,计算时间与原始选项非常相似,在该选项中您将整个矩阵作为spi()函数的输入提供。 ,如果您遇到内存问题,可能会解决这些问题。

另一方面,如果您可以访问多核计算机(现在很可能就是这种情况),您可以通过并行计算看到计算时间的改进:

library(snowfall)
sfInit(parallel=TRUE, cpus=2)
sfLibrary(SPEI)

system.time(
  spi6 <- sfApply(data[,1:100], 2, function(x) spi(x, 6)$fitted)
)

sfStop()

您可能需要将更高的值设置为ncpu以获得更高的速度增益,具体取决于您的计算机支持的线程数。 然而sfApply,无法解决大型数据集的内存问题。这是因为该函数在分配的cpus数之间拆分数据集。由于系统的总内存不会发生变化,因此内存耗尽也是如此。

解决方案是合并两种方法:拆分数据集然后并行化。像这样:

data <- replicate(10000, rnorm(240))

sfInit(parallel=TRUE, cpus=10)
sfLibrary(SPEI)

spi6 <- data*NA
for (i in 0:9) {
    chunk <- (i*1000)+(1:1000)
    spi6[,chunk] <- sfApply(data[,chunk], 2, function(x) spi(x, 6)$fitted)
} 

sfStop()

现在,您只需要找到块的最大大小,即传递给sfApply的数据原始数量,以便不会溢出可用的RAM。一些尝试和错误,这很容易。