与R

时间:2018-02-16 17:29:55

标签: r for-loop matrix parallel-processing permutation

我试图使用R来按列来置换矩阵。 然而,它需要很长时间(矩阵是68k x 32k整数)。

我想并行执行(因为每列都是独立置换的)。我怎样才能实现它?它应该与R中令人尴尬的并行相关,但我没有找到解决方案。

目前,我的功能如下:

permMTX <- function(x) {
    nr <- nrow(x)
    nc <- ncol(x)
    # I'd like to parallelize this for, since each
    # column can be permuted independently
    for (i in 1:nc) {
        x[,i] <- x[sample(nr),i]
    }
    x
} 

2 个答案:

答案 0 :(得分:4)

解决方案

首先,我会使用矢量化,这样可以提高效率。

permMTX = function(x) apply(x, 2L, sample)

然后我们可以使用库parallel来并行化该函数:

library(parallel)

parPermMTX = function(x, cluster) parApply(cl = cluster, X = x, MARGIN = 2L, FUN = sample)

用法

使用parallel,您必须在使用前注册群集。这是一个例子:

cl = makeCluster(detectCores(logical = FALSE))
parPermMTX(diag(10), cl)
#     [,1] [,2] [,3] [,4] [,5]
#[1,]    0    1    0    0    0
#[2,]    0    0    0    0    0
#[3,]    0    0    0    0    0
#[4,]    1    0    0    1    1
#[5,]    0    0    1    0    0

parallel的工作方式(产生多个R进程),你必须确保你有足够的内存来容纳你的数据的多个副本。

我认为建议将数据导出到流程中,只需调用

即可
clusterExport(cl, varlist = "exampleData")

虽然它确实并行运行,但它并不比简单地使用apply更快,但我无法测试与您的数据相同的数据,所以我不能确保它能够正常工作。

这是因为sample已经大量优化,因此产生过程的开销大于简单地调用sample。见Why is the parallel package slower than just using apply?

在我的系统上,采样68E3整数32E3次大约需要40秒:

microbenchmark(sample(68E3), times = 32E3)
#Unit: milliseconds
#          expr      min       lq     mean   median       uq      max neval
# sample(68000) 1.132273 1.192923 1.290838 1.227912 1.286229 7.880191 32000

也许你的内存不足,并且使用硬盘缓存,这非常慢。

第二解决方案

那么,如果我们尝试按顺序将sample的调用分配给单个进程,该怎么办?这是我在这里尝试的:

parPermMTX2 = function(x, cluster) do.call(cbind, parLapply(cl = cluster, X = list(x[,seq(floor(ncol(x)/2))], x[,floor(ncol(x)/2)+seq(ceiling(ncol(x)/2))]), fun = permMTX))

我们将x分为两部分,然后在每部分中调用permMTX,然后与cbind重新合并。

可悲的是,这种方式我都无法获得更好的表现。所以,当我回答你的问题时,我不确定它是否有任何帮助。

答案 1 :(得分:3)

免责声明:我是 bigstatsr 包的作者。

您可以使用共享内存(存储在磁盘上的矩阵)并执行此操作:

# devtools::install_github("privefl/bigstatsr")
library(bigstatsr)

# matrix on disk
mat <- FBM(68e3, 32e2, backingfile = "test")
# inialize with 1:nrow(mat) for each column
system.time(
  big_apply(mat, a.FUN = function(X, ind) {
    print(min(ind))
    X[, ind] <- rep(rows_along(X), length(ind))
    NULL
  }, a.combine = 'c')
) # 15 sec

# permute each column, in parallel
system.time(
  big_apply(mat, a.FUN = function(X, ind) {
    print(min(ind))
    X[, ind] <- apply(X[, ind], 2, sample)
    NULL
  }, a.combine = 'c', ncores = nb_cores())
) # 27 sec

在十分之一的数据上花费27秒,在整个数据集上花费378秒(在只有2个物理核心和8GB RAM的笔记本电脑上)。