R-如何迭代4D矩阵的每个切片

时间:2018-10-09 21:19:21

标签: r netcdf parallel-foreach dompi

我正在R中使用doMPI来并行保存netCDF气候数据。该数据以4维矩阵m的形式存储在R中,其中在经度和纬度网格上具有20000个时间点的6个变量的数据。 m因此被索引为m[lon,lat,time,variable]。根据netCDF如何将数据存储在磁盘上,将数据写入磁盘的最有效方法是使用时间片。因此,对于每个变量,我想一次遍历m一个时间片。目前,我的代码如下:

    ntime <- 20000
    output.vars <- list("rainfall", "snowfallwateq", "snowmelt", "newsnow", "snowdepth", "swe") 
    for (var.index in seq_along(output.vars)) {
        ncout <- nc_open(output.files[var.index], write=TRUE)

        val <- foreach(time.index=1:ntime, .packages=c("ncdf4")) %dopar%
        {
            ncvar_put(ncout, output.vars[[var.index]], 
                      vals=m[,,time.index,var.index],
                      start=c(1, 1, time.index),
                      count=c(nlon, nlat, 1))
        }

        nc_close(ncout)
    }

这不必要地将整个m矩阵复制到每个工作人员。那占用了过多的内存,我需要减少复制的数据量。我对from this answer的想法是,我可以遍历矩阵的每个时间片,因此在每次迭代时仅将时间片的数据复制到每个工人。 foreach构造允许多个对象同时进行迭代,因此我什至可以将时间索引与矩阵时间片并排使用,而不会出现问题。不幸的是,我不知道通过时间片对矩阵进行迭代的任何方法。有没有办法做到这一点,使得在变量t的{​​{1}}循环的每次迭代foreach上,我可以拥有一个变量var,该变量保存二维矩阵data

我已经在下面尝试了直观的方法,但是它遍历每个单独的元素,而不是一次遍历整个时间片。

m[,,t,var]

1 个答案:

答案 0 :(得分:0)

如果您可以在主R流程中处理数据, 您可以尝试将每个二维切片从big.matrix包转换成bigmemory, 并在您的并行工作者中使用它。 仅在处理从属进程中的每个切片所需的时间很长时,这才有用。

参见此示例,请注意,您可以使用foreach嵌套2个%:%循环

m <- as.numeric(1:16)
dim(m) <- rep(2L, 4L)

# use %do% for sequential processing, without copying the data to parallel workers
big_m <- foreach(i=1L:2L, .combine=c) %:% foreach(j=1L:2L, .combine=list) %do% {
  as.big.matrix(m[,,i,j], type="double")
}

descriptors <- lapply(big_m, describe)

# specify .noexport to avoid copying the data to each worker
foreach(m_slice_desc=descriptors, .packages=c("bigmemory"), .noexport=ls(all.names=TRUE)) %dopar% {
  # you could even modify the slices in parallel if you wanted
  m_slice <- attach.big.matrix(m_slice_desc)
  for (i in 1L:2L) {
    for (j in 1L:2L) {
      m_slice[i,j] <- m_slice[i,j] * 2
    }
  }
  # return nothing
  NULL
}

# just to show that the values were modified in place
for (bm in big_m) { print(bm[,]) }
     [,1] [,2]
[1,]    2    6
[2,]    4    8
     [,1] [,2]
[1,]   18   22
[2,]   20   24
     [,1] [,2]
[1,]   10   14
[2,]   12   16
     [,1] [,2]
[1,]   26   30
[2,]   28   32

如果您不能/不会使用bigmemory, 或者处理每个二维切片的速度过快 (是的,这对于多处理可能是有问题的, 参见this answer), 也许您可以从数据中提取3维切片,然后使用.noexport一次仅复制一个切片, 像这样:

slices_3d <- lapply(1L:2L, function(i) { m[,,,i] })
foreach(slice_3d=slices_3d, .noexport=ls(all.names=TRUE)) %dopar% {
  for (j in 1L:2L) {
    slice_2d <- slice_3d[,,j]
    # do something
  }
  # return nothing
  NULL
}

我实际上不是100%确信上述内容会阻止复制整个slices_3d, 如果不是,您可能必须在主R流程中手动提取块中的子集 (例如每次slices_3d[1L:num_parallel_workers]等等), 并确保每次调用foreach时只导出一个块。