2017-08-17 164 views
4

我有代表表被交给我这个样子转嵌套列表

> l = list(list(1, 4), list(2, 5), list(3, 6)) 
> str(l) 
List of 3 
$ :List of 2 
    ..$ : num 1 
    ..$ : num 4 
$ :List of 2 
    ..$ : num 2 
    ..$ : num 5 
$ :List of 2 
    ..$ : num 3 
    ..$ : num 6 

列表结构,我想将其转换为这个

> lt = list(x = c(1, 2, 3), y = c(4, 5, 6)) 
> str(lt) 
List of 2 
$ x: num [1:3] 1 2 3 
$ y: num [1:3] 4 5 6 

我写了一个功能,它以一种非常简单的方式使用Reduce,但我觉得必须有一个更聪明的方法来做到这一点。

任何帮助表示赞赏,感谢


基准

谢谢大家!非常感激。基准的答案,拿起最快获得较大的测试用例:

f1 = function(l) { 
    k <- length(unlist(l))/length(l) 
    lapply(seq_len(k), function(i) sapply(l, "[[", i)) 
} 

f2 = function(l) { 
    n <- length(l[[1]]) 
    split(unlist(l, use.names = FALSE), paste0("x", seq_len(n))) 
} 

f3 = function(l) { 
    split(do.call(cbind, lapply(l, unlist)), seq(unique(lengths(l)))) 
} 

f4 = function(l) { 
    l %>% 
    purrr::transpose() %>% 
    map(unlist) 
} 

f5 = function(l) { 
    # bind lists together into a matrix (of lists) 
    temp <- Reduce(rbind, l) 
    # split unlisted values using indices of columns 
    split(unlist(temp), col(temp)) 
} 

f6 = function(l) { 
    data.table::transpose(lapply(l, unlist)) 
} 

microbenchmark::microbenchmark(
    lapply  = f1(l), 
    split_seq = f2(l), 
    unique  = f3(l), 
    tidy  = f4(l), 
    Reduce  = f5(l), 
    dt   = f6(l), 
    times  = 10000 
) 

Unit: microseconds 
     expr  min  lq  mean median  uq  max neval 
    lapply 165.057 179.6160 199.9383 186.2460 195.0005 4983.883 10000 
split_seq 85.655 94.6820 107.5544 98.5725 104.1175 4609.378 10000 
    unique 144.908 159.6365 182.2863 165.9625 174.7485 3905.093 10000 
     tidy 99.547 122.8340 141.9482 129.3565 138.3005 8545.215 10000 
    Reduce 172.039 190.2235 216.3554 196.8965 206.8545 3652.939 10000 
     dt 98.072 106.6200 120.0749 110.0985 116.0950 3353.926 10000 
+0

什么是背后的逻辑要输出?包含三个项目的两个向量列表或向量列表? – PoGibas

+0

两个向量的列表,但一般n个向量 –

+0

所以你可以有x,y,z向量在最后? – Sotos

回答

4

对于具体的例子,你可以使用这个非常简单的方法:

split(unlist(l), c("x", "y")) 
#$x 
#[1] 1 2 3 
# 
#$y 
#[1] 4 5 6 

它回收上的X-Y矢量和分裂。


它推广到每个列表中的“N”的元素,你可以使用:

l = list(list(1, 4, 5), list(2, 5, 5), list(3, 6, 5)) # larger test case 

split(unlist(l, use.names = FALSE), paste0("x", seq_len(length(l[[1L]])))) 
# $x1 
# [1] 1 2 3 
# 
# $x2 
# [1] 4 5 6 
# 
# $x3 
# [1] 5 5 5 

这是假设,即对l顶层的所有列表元素具有相同的长度,如你的例子。

2

我们可以使用

library(tidyverse) 
r1 <- l %>% 
     transpose %>% 
     map(unlist) 
identical(r1, unname(lt)) 
#[1] TRUE 
+2

类似的东西:'data.table :: transpose(lapply(x,unlist))''。 – mt1022

4

这里是一个想法与unlisting每个列表即

split(do.call(cbind, lapply(l, unlist)), seq(unique(lengths(l)))) 

赋予,

$`1` 
[1] 1 2 3 

$`2` 
[1] 4 5 6 
+1

谢谢 - 这比接受的答案稍慢,这就是为什么我选择另一个:) –

1

在两行使用Reducesplit第二基R法是

# bind lists together into a matrix (of lists) 
temp <- Reduce(rbind, l) 
# split unlisted values using indices of columns 
split(unlist(temp), col(temp)) 
$`1` 
[1] 1 2 3 

$`2` 
[1] 4 5 6 

此假定每个列表项具有相同数量的元素。如果需要的话setNames您可以在第二行中添加名称:

setNames(split(unlist(temp), col(temp)), c("x", "y")) 
1

sapply提取创建数值向量的l各成分的第i个元素及lapply应用它超过1:2(因为有在每l分量k = 2元素)。

如果你知道k是2,那么第一行可以用k <- 2代替。还要注意,在第一行中,我们除以max(...,1)以避免在l是零长度列表的情况下被0除。

下面的代码给出了问题中显示的输出;但是,主题涉及嵌套列表,如果我们需要列表而不是数字向量列表,那么我们可以用lapply替换sapply

k <- length(unlist(l))/max(length(l) , 1) 
lapply(seq_len(k), function(i) sapply(l, "[[", i)) 

,并提供:

[[1]] 
[1] 1 2 3 

[[2]] 
[1] 4 5 6