转置嵌套列表

时间:2017-08-17 11:51:36

标签: r list transpose list-manipulation

我有一个列表结构,表示像这样传递给我的表

> 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

5 个答案:

答案 0 :(得分:5)

对于特定示例,您可以使用这种非常简单的方法:

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

它循环使用x-y向量并将其拆分。

将此概括为&#34; n&#34;每个列表中的元素,您可以使用:

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顶层的所有列表元素都具有相同的长度,如您的示例所示。

答案 1 :(得分:4)

这是一个关于不列出每个列表的想法,即

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

给出,

$`1`
[1] 1 2 3

$`2`
[1] 4 5 6

答案 2 :(得分:2)

我们可以使用

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

答案 3 :(得分: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"))

答案 4 :(得分:1)

accessoryView提取sapply的每个组件的第i个元素,创建一个数字向量,l将其应用于1:2(因为每个组件中有k = 2个元素) lapply)。

如果您知道k为2,那么第一行可以替换为l。另请注意,在第一行中,我们除以max(...,1),以避免在k <- 2为零长度列表的情况下除以0。

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

lapply

,并提供:

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