我的问题相对简单,我已经在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
我确信有一种更聪明的方法可以做到这一点。任何提示?
答案 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)
以下是使用split
和rowSums
的第二种方法:
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)