【问题标题】:Aggregate rows in a large matrix by rowname按行名聚合大矩阵中的行
【发布时间】:2011-12-29 16:07:23
【问题描述】:

我想通过在具有相同行名的行中添加值来聚合矩阵的行。我目前的做法如下:

> M
  a b c d
1 1 1 2 0
1 2 3 4 2
2 3 0 1 2
3 4 2 5 2
> index <- as.numeric(rownames(M))
> M <- cbind(M,index)
> Dfmat <- data.frame(M)
> Dfmat <- aggregate(. ~ index, data = Dfmat, sum)
> M <- as.matrix(Dfmat)
> rownames(M) <- M[,"index"]
> M <- subset(M, select= -index)
> M
   a b c d
 1 3 4 6 2
 2 3 0 1 2
 3 4 2 5 2

这种方法的问题是我需要将它应用于许多非常大的矩阵(最多 1.000 行和 30.000 列)。在这些情况下,计算时间非常长(使用 ddply 时同样的问题)。有没有更有效的解决方案?原始输入矩阵是 tm 包中的 DocumentTermMatrix 是否有帮助?据我所知,它们以稀疏矩阵格式存储。

【问题讨论】:

  • 不完全清楚你需要做什么,但我会玩一下reshape 包(melt() 和 cast())。但更重要的是:你是如何首先允许重复的行名的?这通常是个坏主意。
  • 在我的数据中,行名是日期。每当我在同一日期有多个观察结果时,它们都是重复的。
  • @Christian 没关系。我认为 Carl 正在考虑数据帧,其中严格不允许重复。
  • 嗯,是的,但我的意思是行名应该区分行。但是不要紧。詹姆斯的回答可能就足够了。尝试对方法的每个步骤进行一些时间测试,看看主要瓶颈在哪里。

标签: r aggregate


【解决方案1】:

这是一个使用bycolSums 的解决方案,但由于by 的默认输出,需要进行一些调整。

M <- matrix(1:9,3)
rownames(M) <- c(1,1,2)
t(sapply(by(M,rownames(M),colSums),identity))
  V1 V2 V3
1  3  9 15
2  3  6  9

【讨论】:

  • 谢谢詹姆斯,我从来没有听说过by,但看起来这将是一个很好的功能。我像这样简化了输出:out &lt;- by(M, rownames(M), colMeans, simplify = T); out &lt;- do.call(rbind, out)
  • @DanielFreeman 我有效地使用此解决方案按行名聚合一些数据。但我现在想做同样的事情,聚合数据,但通过两列(字符)上的元素,同时对所有其余列(数字)求和。对更改上述代码以使其适用于我的情况的任何建议?
【解决方案2】:

James 的答案按预期工作,但对于大型矩阵来说相当慢。这是avoids creating of new objects的版本:

combineByRow <- function(m) {
    m <- m[ order(rownames(m)), ]

    ## keep track of previous row name
    prev <- rownames(m)[1]
    i.start <- 1
    i.end <- 1

    ## cache the rownames -- profiling shows that it takes
    ## forever to look at them
    m.rownames <- rownames(m)
    stopifnot(all(!is.na(m.rownames)))


    ## go through matrix in a loop, as we need to combine some unknown
    ## set of rows
    for (i in 2:(1+nrow(m))) {

        curr <- m.rownames[i]

        ## if we found a new row name (or are at the end of the matrix),
        ## combine all rows and mark invalid rows
        if (prev != curr || is.na(curr)) {

            if (i.start < i.end) {
                m[i.start,] <- apply(m[i.start:i.end,], 2, max)
                m.rownames[(1+i.start):i.end] <- NA
            }

            prev <- curr
            i.start <- i
        } else {
            i.end <- i
        }
    }

    m[ which(!is.na(m.rownames)),]    
}

测试它表明这比使用by 的答案快大约 10 倍(本例中为 2 秒对 20 秒):

N <- 10000

m <- matrix( runif(N*100), nrow=N)
rownames(m) <- sample(1:(N/2),N,replace=T)

start <- proc.time()
m1 <- combineByRow(m)
print(proc.time()-start)

start <- proc.time()
m2 <- t(sapply(by(m,rownames(m),function(x) apply(x, 2, max)),identity))
print(proc.time()-start)

all(m1 == m2)

【讨论】:

  • 这很有用。如果有 combineByCol 那就太棒了。我害怕把事情搞砸,因为我不完全理解。
【解决方案3】:

现在Matrix.utils 中有一个聚合函数。这可以用一行代码完成你想要的,比combineByRow 解决方案快10 倍,比by 解决方案快100 倍:

N <- 10000

m <- matrix( runif(N*100), nrow=N)
rownames(m) <- sample(1:(N/2),N,replace=T)

> microbenchmark(a<-t(sapply(by(m,rownames(m),colSums),identity)),b<-combineByRow(m),c<-aggregate.Matrix(m,row.names(m)),times = 10)
Unit: milliseconds
                                                  expr        min         lq       mean     median         uq        max neval
 a <- t(sapply(by(m, rownames(m), colSums), identity)) 6000.26552 6173.70391 6660.19820 6419.07778 7093.25002 7723.61642    10
                                  b <- combineByRow(m)  634.96542  689.54724  759.87833  732.37424  866.22673  923.15491    10
                c <- aggregate.Matrix(m, row.names(m))   42.26674   44.60195   53.62292   48.59943   67.40071   70.40842    10

> identical(as.vector(a),as.vector(c))
[1] TRUE

编辑:弗兰克是对的,rowsum 比这些解决方案中的任何一个都快。仅当您使用 Matrix,尤其是稀疏函数,或者您正在执行除 sum 之外的聚合时,您才需要考虑使用这些其他函数中的另一个。

【讨论】:

  • 也许您可以添加rowsum(m, rownames(m)),这是基本解决方案(奇怪的是这里没有出现在答案中)。
  • 不要使用这个功能。它在“有趣”论点的几乎所有可能性上都默默地失败了。 github.com/cran/Matrix.utils/issues/1
  • @eric_kernfeld aggregate.Matrix 接受字符串参数“count”、“mean”或“sum”。这在当前版本中有更好的记录。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-13
  • 2018-05-31
相关资源
最近更新 更多