使用indice vector

时间:2016-12-09 13:55:07

标签: 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

我想结合不同的列列rowSumming它们跟随一个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

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

3 个答案:

答案 0 :(得分:3)

您的输入结果似乎不符合您的输入,但按照您的逻辑,您可以使用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

或者在apply()的每一行使用rowsum()功能,以避免转置两次:

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

答案 1 :(得分:2)

我不确定我是否会将此作为答案发布,但我想测试这里介绍的所有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 

使用:

 all(res1==res2)
[1] TRUE

 all(res1==res3)
[1] TRUE

 all(res1==res4)
[1] TRUE

 all(res1==res5)
[1] TRUE

所以看起来循环不是那么慢而且data.frame版本是最好的。有趣的结果!

答案 2 :(得分:1)

以下是使用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)