向量矢量矩阵运算分组的高效策略

时间:2019-02-07 15:39:10

标签: r matrix parallel-processing

我必须在一个大矩阵上计算一系列统计数据,我想以向量作为分组因子以最有效的方式做到这一点。

行是我要分组的变量,而列是示例。

例如:

mat = matrix(seq(1,10000), ncol  = 100)
vect_group = c(1,1,1,1,1,2,2,2,3,3,3, ...)

我想计算索引为1、2、3等的所有行的平均列。因此,在这种情况下,请获得一个新矩阵,该矩阵的行数与vect_group的级别以及匹配列中的相应统计信息一样。

到目前为止,我已经通过索引获得了这种循环,并且每次都对这些子矩阵使用apply,但是我想加快该过程。我尝试了doParallelforeach,但没有成功。

我苦苦挣扎的关键部分是拆分/聚合过程以生成较小的矩阵。另外,我不知道这些开销是否会影响多线程计算的选择。

2 个答案:

答案 0 :(得分:1)

我不知道您是否需要多线程。

我已经测试了两种解决方案,一种使用基数R,另一种使用dplyr。两者在基准测试中都非常快。

mat <- matrix(seq(1,10000), ncol  = 100)
vect_group <- rep(1:10, each = 10)

#--
library(dplyr)

#-- Base R
splitData <- split(as.data.frame(mat), vect_group)
meansPerGroup <- sapply(splitData, colMeans)

#-- Dplyr
df <- data.frame(mat, vect_group)
meansPerGroup <- df %>%
    group_by(vect_group) %>%
    summarize_at(vars(colnames(mat)), mean)

然后,我针对这两种解决方案进行了基准测试:

rbenchmark::benchmark(replications = 5000,
    baseR = function(mat = mat, vect_group = vect_group) {
        splitData <- split(as.data.frame(mat), vect_group)
        meansPerGroup <- sapply(splitData, colMeans)
    },
    dplyr = function(df = df, vect_group = vect_group) {
        meansPerGroup <- df %>%
            group_by(vect_group) %>%
            summarize_at(vars(colnames(mat)), mean)
    })

基准测试结果

   test replications elapsed relative user.self sys.self user.child sys.child
1 baseR         5000   0.006      1.2     0.006        0          0         0
2 dplyr         5000   0.005      1.0     0.006        0          0         0

答案 1 :(得分:0)

我同意@csgroen的观点,因为并行计算平均值非常快,并且设置起来会产生开销,所以并行执行此计算可能是不必要的,但这可能取决于问题的规模。您的矩阵有多大?

不是最快的并行方法是使用data.table。我已经在下面对包括上一个答案在内的一些方法进行了基准测试(尽管我无法在计算机上运行dplyr版本-我认为是因为mat没有列名)。 Data.table平均需要3毫秒左右的时间,而且聚合也不远。

mat <-  matrix(seq(1,10000), ncol  = 100)
vect_group  = rep(1:10, each = 10)

fn1_agg <- function(mat, vg) {
  aggregate(c(mat)~rep(vg, ncol(mat)), FUN = mean)
}

fn2_dt <- function(mat, vg){
  DT <- data.table::data.table(m = c(mat), v = rep(vg, ncol(mat)))
  data.table::setkey(DT, v)
  DT[, list(m = mean(m)), by = v]
}

fn3_split <- function(mat, vg) {
  splitData <- split(as.data.frame(mat), vect_group)
  sapply(splitData, colMeans)
}

microbenchmark::microbenchmark(fn1_agg(mat, vect_group),
                               fn2_dt(mat, vect_group),
                               fn3_split(mat, vect_group))
#> Unit: milliseconds
#>                        expr       min        lq      mean    median
#>    fn1_agg(mat, vect_group)  5.169709  5.437589  6.122462  6.293567
#>     fn2_dt(mat, vect_group)  1.197218  1.291972  3.004166  1.472097
#>  fn3_split(mat, vect_group) 15.480264 15.751230 16.998514 16.267098
#>         uq        max neval cld
#>   6.481626   9.454458   100  b 
#>   1.538948 142.368800   100 a  
#>  17.060969  60.686907   100   c

reprex package(v0.2.1)于2019-02-07创建