假设我有以下命名的数值向量:
a <- 1:8
names(a) <- rep(c('I', 'II'), each = 4)
如何将此向量转换为长度为 2 的列表(如下所示)?
a.list
# $I
# [1] 1 2 3 4
# $II
# [1] 5 6 7 8
请注意,
as.list(a)
不是我要找的。
我非常不满意(对于大向量来说速度很慢)的解决方案是:
names.uniq <- unique(names(a))
a.list <- setNames(vector('list', length(names.uniq)), names.uniq)
for(i in 1:length(names.uniq)) {
names.i <- names.uniq[i]
a.i <- a[names(a)==names.i]
a.list[[names.i]] <- unname(a.i)
}
预先感谢您的帮助, 德文
就像我在评论中所说,您可以使用
split
创建列表。
a.list <- split(a, names(a))
a.list <- lapply(a.list, unname)
单行将是
a.list <- lapply(split(a, names(a)), unname)
#$I
#[1] 1 2 3 4
#
#$II
#[1] 5 6 7 8
编辑。
然后,thelatemail 在他的评论中发布了对此的简化。我使用 Devin King 的方式计时,它不仅更简单,而且速度快了 25%。
a.list <- split(unname(a),names(a))
我建议查看擅长聚合大量数据的包,例如
data.table
包。使用 data.table
,您可以:
a <- 1:5e7
names(a) <- c(rep('I',1e7), rep('II',1e7), rep('III',1e7),
rep('IV',1e7), rep('V',1e7))
library(data.table)
temp <- data.table(names(a), a)[, list(V2 = list(a)), V1]
a.list <- setNames(temp[["V2"]], temp[["V1"]])
以下是一些用于测试各种选项的函数:
myFun <- function(invec) {
x <- data.table(names(invec), invec)[, list(V2 = list(invec)), V1]
setNames(x[["V2"]], x[["V1"]])
}
rui1 <- function(invec) {
a.list <- split(invec, names(invec))
lapply(a.list, unname)
}
rui2 <- function(invec) {
split(unname(invec), names(invec))
}
op <- function(invec) {
names.uniq <- unique(names(invec))
a.list <- setNames(vector('list', length(names.uniq)), names.uniq)
for(i in 1:length(names.uniq)) {
names.i <- names.uniq[i]
a.i <- a[names(invec) == names.i]
a.list[[names.i]] <- unname(a.i)
}
a.list
}
以及 10 次重复的微基准测试结果:
library(microbenchmark)
microbenchmark(myFun(a), rui1(a), rui2(a), op(a), times = 10)
# Unit: milliseconds
# expr min lq mean median uq max neval
# myFun(a) 698.1553 768.6802 932.6525 934.6666 1056.558 1168.889 10
# rui1(a) 2967.4927 3097.6168 3199.9378 3185.1826 3319.453 3413.185 10
# rui2(a) 2152.0307 2285.4515 2372.9896 2362.7783 2426.821 2643.033 10
# op(a) 2672.4703 2872.5585 2896.7779 2901.7979 2971.782 3039.663 10
另请注意,在测试不同的解决方案时,您可能需要考虑其他场景,例如,您希望有许多不同名称的情况。在这种情况下,您的
for
循环会显着减慢。例如,尝试使用以下数据的上述函数:
set.seed(1)
b <- sample(100, 5e7, TRUE)
names(b) <- sample(c(letters, LETTERS, 1:100), 5e7, TRUE)
在更大的向量上测试 Rui Barradas 的解决方案与我的原始解决方案
a <- 1:5e7
names(a) <- c(rep('I',1e7), rep('II',1e7), rep('III',1e7), rep('IV',1e7), rep('V',1e7))
瑞的
st1 <- Sys.time()
a.list <- split(a, names(a))
a.list <- lapply(a.list, unname)
Sys.time() - st1
Time difference of 2.560906 secs
我的
st1 <- Sys.time()
names.uniq <- unique(names(a))
a.list <- setNames(vector('list', length(names.uniq)), names.uniq)
for(i in 1:length(names.uniq)) {
names.i <- names.uniq[i]
a.i <- a[names(a)==names.i]
a.list[[names.i]] <- unname(a.i)
}
Sys.time() - st1
Time difference of 2.712066 secs
最新邮件
st1 <- Sys.time()
a.list <- split(unname(a),names(a))
Sys.time() - st1
Time difference of 1.62851 secs
要处理未命名的向量,请使用 then:
vec_to_list <- function(vec) {
if (is.null(names(vec))) names(vec) <- 1:length(vec)
split(unname(vec), names(vec))
}
一个快速的解决方案是使用 lapply(),因为它创建的列表的元素将从应用该函数的向量/列表中获取名称。所以在这种情况下:
> a <- 1:8
> names(a) <- rep(c('I', 'II'), each = 4)
> a %>% lapply(function(x) x)
$I
[1] 1
$I
[1] 2
$I
[1] 3
$I
[1] 4
$II
[1] 5
$II
[1] 6
$II
[1] 7
$II
[1] 8