6

我有一个列表结构,它代表一个像这样交给我的表格

> 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
4

5 回答 5

5

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

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

它回收 xy 向量并对其进行拆分。


要将其推广到每个列表中的“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都具有相同的长度,如您的示例所示。

于 2017-08-17T12:13:48.920 回答
4

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

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

这使,

$`1`
[1] 1 2 3

$`2`
[1] 4 5 6
于 2017-08-17T12:00:27.310 回答
2

我们可以用

library(tidyverse)
r1 <- l %>% 
        transpose %>%
        map(unlist)
identical(r1, unname(lt))
#[1] TRUE
于 2017-08-17T11:58:35.463 回答
1

在两行中使用Reduceand的第二种基本 R 方法是split

# 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"))
于 2017-08-17T12:02:11.773 回答
1

sapply提取l创建数字向量的每个分量的第 i 个元素,并将其应用于lapply1:2(因为 的每个分量中有 k=2 个元素l)。

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

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

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
于 2017-08-17T12:19:29.683 回答