我的问题相对简单,我已经在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
因此,循环似乎并不慢,并且数据版本是最好的。有趣的结果!
这是使用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)