【问题标题】:Split, aggregate and combine matrix and keep the same structure in base R拆分、聚合和组合矩阵并在基础 R 中保持相同的结构
【发布时间】:2019-01-29 19:59:44
【问题描述】:

我有一个矩阵,其中包含一个分组列和一个用于汇总值的列。

我想将矩阵拆分成组,总结一些值(通过保持相同长度的向量),unsplit然后它们分配到新列?

什么是最高效和基本的 R-onic 方法?

目前的获胜者是 lapply 函数,但我想知道是否还有其他我缺少的函数? stats::aggregate 之类的东西保持相同的结构?

我想留在基础 R 中并保留矩阵,所以没有 dplyrdata.table ;)。

Edit1:我包含了聚合 + 合并和由 @IceCreamToucan 提供支持的 sapply 解决方案。 (感谢那)。对于 aggregate,这不是一个非常公平的比较,因为我首先转换为 data.frames,然后再转换回矩阵。

Edit2: 使用更大的矩阵和 100 个组 ave 优于其他函数。感谢@Gregor 的这个。

set.seed(104)
smpl = sample(1:100, size = 10000, T)
mat0 <- as.matrix(data.frame(
  group=smpl,
  sum=sample(seq(0,100,10), length(smpl), T)
))
mat1 <- cbind(mat0, "sums"=0)


library(microbenchmark)
check <- function(values) {
  all(sapply(values[-1], function(x) all.equal(values[[1]], x)))}
mf = microbenchmark(#check = check,
                    forloop = {
                      mat <- mat1
                      for (z in unique(mat[,'group'])) {
                        mat[mat[,'group'] == z,'sums'] = sum(mat[mat[,'group'] == z,'sum'])
                      }
                      mat
                    },
                    lapply = {
                      mat <- mat1
                      mat[,'sums'] <- unlist(lapply(unique(mat[,'group']), function(i) {
                        sums = sum(mat[mat[,'group'] == i,'sum'])
                        rep(sums, length(mat[mat[,'group'] == i,'sum']))
                      }))
                      mat
                    },
                    sapply = {
                      mat <- mat1
                      mat <- mat[order(mat[,'group']),]
                      mat[,'sums'] <- rep(sapply(split(mat[, 'sum'], mat[, 'group']), sum), 
                                          table(mat[, 'group']))
                      mat
                    },
                    ave = {
                      mat <- mat1
                      mat[,'sums'] <- ave(x = mat[, 'sum'], mat[, 'group'], FUN = sum)
                      mat[order(mat[,'group']),]
                    },
                    aggregate = {
                      matA <- mat0
                      matA <- matA[order(matA[,'group']),]
                      res = aggregate(sum ~ group, FUN = sum, data = matA)
                      matdf = data.frame(matA)
                      base::merge(res, matdf, by ="group")
                    }
)
mf
Unit: milliseconds
      expr      min       lq     mean   median       uq       max neval cld
   forloop 19.94083 25.73131 25.95823 25.97898 26.58043  38.68300   100  bc
    lapply 15.96057 21.44226 24.23693 21.88130 22.41287 311.00252   100  bc
    sapply 21.89081 22.41981 23.42291 22.70492 23.04978  37.41853   100  b 
       ave 11.79256 12.08868 12.51119 12.27613 12.52803  18.20577   100 a  
 aggregate 26.54753 27.31484 29.09592 27.71163 28.71937  54.75284   100   c

【问题讨论】:

  • 您可以将sums 列为rep(sapply(split(mat[, 'sum'], mat[, 'group']), sum), table(mat[, 'group']))。我认为这比仅使用数据框更令人困惑,但如果您与矩阵结婚,我会这样做。
  • 有趣。当然,你可以包含它。
  • ave 比 for 循环快,但仍比 lapply 慢 4-5 倍。 ave(x = mat1[, 'sum'], mat1[, 'group'], FUN = sum)
  • ...虽然时间以微秒为单位,但我想知道这个例子是否足够大以至于很好。如果您真的担心性能,我建议您做一个示例,例如,至少 100 个组,每个组有几行。
  • ave 在内部只是 splitlapply ;)

标签: r matrix aggregate


【解决方案1】:

查阅各种R-FAQ(how to sum by group?Grouping functions and the *apply family),用于按组求和而不聚合的基本R函数是ave

ave(x = mat1[, 'sum'], mat1[, 'group'], FUN = sum)

正如问题所编辑的那样,ave 在有很多组时相当快。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-23
    相关资源
    最近更新 更多