【发布时间】:2015-09-02 13:51:58
【问题描述】:
我有一个数组,例如arr = cbind(c(1,1,2,2,3), c(1,2,3,4,5)),我想聚合具有相同第一个元素的行,并将结果存储为列表。有什么方法可以做到这一点?此示例的预期输出result = list( cbind(c(1,1), c(1,2)), cbind(c(2,2), c(3,4)), cbind(c(3), c(5)) )
【问题讨论】:
我有一个数组,例如arr = cbind(c(1,1,2,2,3), c(1,2,3,4,5)),我想聚合具有相同第一个元素的行,并将结果存储为列表。有什么方法可以做到这一点?此示例的预期输出result = list( cbind(c(1,1), c(1,2)), cbind(c(2,2), c(3,4)), cbind(c(3), c(5)) )
【问题讨论】:
我们可以循环(lapply)通过arr 的第一列的unique 值,并根据第一列的匹配元素与唯一元素对行进行子集化。
lapply(unique(arr[,1]), function(i) arr[arr[,1]==i,,drop=FALSE])
或者我们split'arr'的行序列和'arr'的第一列,并使用行索引来子集'arr'。
lapply(split(1:nrow(arr), arr[,1]), function(i) arr[i,, drop=FALSE])
或者正如 @Jota 在 cmets 中提到的那样,我们可以 split 第一列的“arr”将 vector 作为 list 元素。我们使用lapply 循环并将vector 转换为matrix。
lapply(split(arr, arr[,1]), matrix, ncol=2)
arr1 <- cbind(rep(seq(1000), each=1000), seq(1e6))
system.time(res1 <- lapply(unique(arr1[,1]), function(i)
arr1[arr1[,1]==i,,drop=FALSE]))
# user system elapsed
# 18.980 0.000 15.906
system.time(res2 <- lapply(split(1:nrow(arr1), arr1[,1]),
function(i) arr1[i,, drop=FALSE]))
# user system elapsed
# 0.00 0.00 0.07
names(res2) <- NULL
identical(res1, res2)
#[1] TRUE
system.time(res3 <- lapply(split(arr1, arr1[,1]), matrix, ncol=2))
# user system elapsed
# 0.000 0.000 0.186
names(res3) <- NULL
identical(res1, res3)
#[1] TRUE
system.time(res4 <- split(data.frame(arr1), arr1[,1]))
# user system elapsed
# 1.151 0.000 1.039
基于上述基准,split 方法更好。
【讨论】:
lapply(split(arr, arr[,1]), matrix, ncol=2) 是类似的策略。
为什么不采用这种更短的方法:
split(data.frame(arr), arr[,1])
#$`1`
# X1 X2
#1 1 1
#2 1 2
#$`2`
# X1 X2
#3 2 3
#4 2 4
#$`3`
# X1 X2
#5 3 5
【讨论】: