使用Indice Vector一起汇总多列的巧妙方法

  • 本文关键字:方法 Indice Vector 一起 使用 r
  • 更新时间 :
  • 英文 :


我的问题相对简单,我已经在R中找到了一种方法,但是我正在寻找一种更聪明的方法来进行教育目的。我的解决方案使用循环,我总是尽量避免循环尽可能多。

我有一个表(或矩阵):

set.seed(1)
tb <- matrix(round(runif(40,0,5)),4,10)
tb
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    1    1    3    3    4    5    1    4    2     4
[2,]    2    4    0    2    5    1    2    2    1     1
[3,]    3    5    1    4    2    3    0    2    4     4
[4,]    5    3    1    2    4    1    2    3    3     2

我想在indice(或索引)向量的情况下组合不同的列组。

ind <- c(1,1,1,2,2,1,3,3,3,4)

for循环方式:

res.ls <- NULL
for(i in unique(ind)) {
  res.ls[[i]] <- rowSums(subset(tb,select=ind==i))
}
do.call("cbind",res.ls)
     [,1] [,2] [,3] [,4]
[1,]   10    7    7    4
[2,]    7    7    5    1
[3,]   12    6    6    4
[4,]   10    6    8    2

我敢肯定有一种更聪明的方法可以做到这一点。任何提示?

它似乎与您的输入不符,但是遵循逻辑,您可以使用rowsum,而是在原始矩阵的转置版本上:

t(rowsum(t(tb), ind))
#      1 2 3 4
#[1,] 15 4 6 3
#[2,] 17 8 5 3
#[3,] 11 4 5 3
#[4,] 12 2 6 4

或使用rowsum()在每行上使用apply()函数,以避免两次转移:

t(apply(tb, 1, rowsum, ind))
#     [,1] [,2] [,3] [,4]
#[1,]   15    4    6    3
#[2,]   17    8    5    3
#[3,]   11    4    5    3
#[4,]   12    2    6    4

如果您有数据框架,则可能会更有效,因为它不会将数据框架转换为矩阵:

df <- data.frame(tb)
do.call(cbind, lapply(split.default(df, ind), rowSums)) # use split.default to split data 
# frames as multiple data frames by columns and apply rowSums to each sub data frame
#      1 2 3 4
#[1,] 15 4 6 3
#[2,] 17 8 5 3
#[3,] 11 4 5 3
#[4,] 12 2 6 4

我不确定我是否应该将其发布为答案,但是我想测试此处介绍的所有5种方法的效率(我的循环,@psidom 3答案和 @IMO的答案)。我已经使数据更大:

set.seed(1)
tb <- matrix(round(runif(40000000,0,15)),40000,1000)
ind <- round(runif(1000,1,300))

运行所有选项:

 res.ls <- NULL
 system.time({
 for(i in unique(ind)) {
   res.ls[[i]] <- rowSums(subset(tb,select=ind==i))
 }
 res1 <- do.call("cbind",res.ls)
 })
utilisateur     système      écoulé 
       0.60        0.04        0.64 
 system.time(
 res2 <- t(rowsum(t(tb), ind))
 )
utilisateur     système      écoulé 
       0.68        0.02        0.70 
 system.time(
 res3 <- t(apply(tb, 1, rowsum, ind))
 )
utilisateur     système      écoulé 
      20.01        0.21       20.24 
 system.time(
 res4 <- sapply(split(tb, rep(ind, each=nrow(tb))), function(x) rowSums(matrix(x, nrow(tb))))
 )
utilisateur     système      écoulé 
      58.68        0.42       59.13 

 df1 <- data.frame(tb)
 system.time(
 res5 <- do.call(cbind, lapply(split.default(df1, ind), rowSums)))
utilisateur     système      écoulé 
        0.3         0.0         0.3 

with:

 all(res1==res2)
[1] TRUE
 all(res1==res3)
[1] TRUE
 all(res1==res4)
[1] TRUE
 all(res1==res5)
[1] TRUE

因此,循环似乎并不慢,并且数据版本是最好的。有趣的结果!

这是使用splitrowSums的第二种方法:

    sapply(split(tb, rep(ind, each=nrow(tb))), function(x) rowSums(matrix(x, nrow(tb))))
      1 2 3 4
[1,] 10 7 7 4
[2,]  7 7 5 1
[3,] 12 6 6 4
[4,] 10 6 8 2

数据

set.seed(1)
tb <- matrix(round(runif(40,0,5)),4,10)
ind <- c(1,1,1,2,2,1,3,3,3,4)

相关内容

最新更新