R:有一种简单的方法可以让ave()为矩阵工作吗?

时间:2014-10-14 23:29:55

标签: r matrix tapply

很抱歉,如果这看起来微不足道,但在网上搜索了一段时间后,我无法找到解决方案。

我有一个矩阵和一个与列相关的因子向量。目标是分别为所有因子获取rowMeans并保持原始矩阵结构。所以可能它会像ave(),但在二维数组上工作。

这是一个粗略的示范:

(mat <- rbind(1:5,6:10,11:15))
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    2    3    4    5
[2,]    6    7    8    9   10
[3,]   11   12   13   14   15

groups <- c(1,1,1,2,2)

mat[,groups==1] <- rowMeans(mat[,groups==1]) # I am asking about this part
mat[,groups==2] <- rowMeans(mat[,groups==2]) # ...

mat
     [,1] [,2] [,3] [,4] [,5]
[1,]    2    2    2  4.5  4.5
[2,]    7    7    7  9.5  9.5
[3,]   12   12   12 14.5 14.5

实际上,这个矩阵将有数百万行(和更少的列)。因此,逐行工作的解决方案可能太慢了。

我正在编写自己的函数,但这似乎可能是一个简单的单行解决方案。

3 个答案:

答案 0 :(得分:3)

1)假设您要将每行的每个元素替换为该行的平均值,请尝试将m作为矩阵:

ave(m, row(m))

如果这不是你想要的,请提供一个完整的例子,包括输入和结果输出。

2)有关更新的问题,请尝试以下方法:

t(ave(t(m), group, t(row(m))))

或此等效变体:

ave(m, matrix(group, nrow(m), ncol(m), byrow = TRUE), row(m))

答案 1 :(得分:2)

也许是这样的:

mat.list  <- Map(matrix, split(mat, groups[col(mat)]), nrow = nrow(mat))
mean.list <- Map(rowMeans, mat.list)
do.call(cbind, mean.list[groups])

或者为了更快的速度:

idx.list  <- split(seq_len(ncol(mat)), groups)
get.cols  <- function(mat, idx) mat[, idx, drop = FALSE]
mat.list  <- lapply(idx.list, get.cols, mat = mat)
mean.list <- lapply(mat.list, rowMeans)
do.call(cbind, mean.list[groups])

答案 2 :(得分:1)

如果有一个优化的功能,比如rowGroupMeans,那会很好,但我不知道这样的事情。

我的解决方案是使用rowsum,如下所示:

means <- rowsum(t(mat), groups)/tabulate(groups)
t(means)[, groups]

      1  1  1    2    2
[1,]  2  2  2  4.5  4.5
[2,]  7  7  7  9.5  9.5
[3,] 12 12 12 14.5 14.5

这可以很好地适应更大的问题,例如

mat <- matrix(1:100e6, ncol = 100)
groups <- rep(1:10, each = 10)

## Map solution
for (i in 1:3){
    print(system.time({
        mat.list  <- Map(matrix, split(mat, groups[col(mat)]), nrow = nrow(mat))
        mean.list <- Map(rowMeans, mat.list)
        ans1 <- do.call(cbind, mean.list[groups])
    }))
}

   user  system elapsed 
   8.20    1.26    9.66 
   user  system elapsed 
  11.84    1.94   13.90 
   user  system elapsed 
  10.70    1.89   12.79

## rowsum solution
for (i in 1:3){
    print(system.time({
        means <- rowsum(t(mat), groups)/tabulate(groups)
        ans2 <- t(means)[,groups]
    }))
}

   user  system elapsed 
   1.56    0.22    1.78 
   user  system elapsed 
   1.48    0.27    1.74 
   user  system elapsed 
   1.57    0.14    1.72

正如已经指出的那样,ave解决方案无法很好地扩展 - 当我尝试为此运行时间时,我的R会话崩溃了。