R - 将列表转换为数据框

5
有一个类似这样的列表,从1开始。
[[7158]]
[1] 81 72

[[7159]]
[1] 81 69

[[7160]]
[1] 81 79

[[7161]]
[1] 81 84

需要将此内容更改为数据框,其中每个元素的第一个数字对齐在一列中,每个元素的第二个数字放置在数据框的第二列中。因此,它应该最终呈现如下:

> data.frame("col1" = c(81, 81, 81, 81), "col2" = c(72, 69, 79, 84))
  col1 col2
1   81   72
2   81   69
3   81   79
4   81   84

我尝试使用do.call(rbind.data.frame, my_list),但它似乎会将数字放入大量列中,而不是强制缩减为所需的两列。用循环做这件事相当简单,但有没有R的方法可以完成呢?谢谢。


2
我认为你可能被列名所误导了。尝试使用do.call(rbind, my_list),然后对其调用data.frame。这样应该比rbind.data.frame更快。 - A5C1D2H2I1M1N2O1R2T1
1
你也可以尝试使用 library(stringi); stri_list2matrix(my_list, byrow = TRUE) 或者 t(simplify2array(my_list))。你还需要将它们包装在 as.data.frame 中以获得一个 data.frame 作为输出。 - A5C1D2H2I1M1N2O1R2T1
是的,那是最好的方法,也许你应该将它添加为未来参考的答案。 - codingEnthusiast
感谢您,Ananda。如果您将其作为答案提交,我会将其标记为已接受。 - appleLover
5个回答

8

只需设置名称:

mylist <- list(c(81,72), c(81,63), c(81,79))
setNames(do.call(rbind.data.frame, mylist), c("col1", "col2"))
#  col1 col2
#1   81   72
#2   81   63
#3   81   79

与其他一些解决方案相比,这种方法也适用于混合数据类型:
mylist <- list(list("a", 72), list("b", 63), list("c", 79))
res <- setNames(do.call(rbind.data.frame, mylist), c("col1", "col2"))
str(res)
#'data.frame':  3 obs. of  2 variables:
# $ col1: Factor w/ 3 levels "a","b","c": 1 2 3
# $ col2: num  72 63 79

只是一个猜测,但如果混合数据类型是一个问题,您不认为在将其转换为“data.frame”后在输出上运行“type.convert”会比“rbind.data.frame”更快吗? - A5C1D2H2I1M1N2O1R2T1

4
您可以尝试以下任何一种:
do.call(rbind, my_list)

t(simplify2array(my_list))

library(stringi)
stri_list2matrix(my_list, byrow = TRUE)

上述所有方法均可生成您所描述的具有两列数据的矩阵,因此您可以使用as.data.frame将其转换为data.frame。以上替代方案和@Roland的建议可以在此Gist中找到。总之,“stringi”方法将是迄今为止提出的选项中最快的。如果我没有弄错的话,“data.table”中的rbindlist也应支持将向量列表转换-但我还没有在GitHub的开发版本上进行测试以验证,因此我没有在此处包含该选项。

1
你可以尝试使用以下代码:

Reduce( rbind, lapply(t1, t) )

不,这是低效的。 - Roland

1
另一个想法:

mylist = list(c(81, 72), c(81, 69), c(81, 79), c(81, 84))

f4 = function(x) 
{
    tlist = lapply(seq_along(x[[1]]), 
                   function(i) unlist(lapply(x, "[[", i)))
    structure(tlist, class = "data.frame", 
              row.names = .set_row_names(as.integer(length(tlist[[1]]))), 
              names = paste("col", seq_along(tlist), sep = ""))
}

f4(mylist)
#  col1 col2
#1   81   72
#2   81   69
#3   81   79
#4   81   84

与其他选项的基准测试:

library(stringi)              
f1 = function(x)
  setNames(as.data.frame(type.convert(stri_list2matrix(x, byrow = TRUE))), 
           paste("col", seq_along(x[[1]]), sep = ""))

f2 = function(x)
  setNames(do.call(rbind.data.frame, x), 
           paste("col", seq_along(x[[1]]), sep = ""))

f3 = function(x)
  setNames(as.data.frame(Reduce(rbind, lapply(x, t))), 
           paste("col", seq_along(x[[1]]), sep = "")) 

myls = replicate(1e3, sample(1e2), simplify = F)
identical(f1(myls), f2(myls))
#[1] TRUE
identical(f1(myls), f3(myls))
#[1] TRUE
identical(f1(myls), f4(myls))
#[1] TRUE
microbenchmark::microbenchmark(f1(myls), f2(myls), f3(myls), f4(myls), times = 10)
#Unit: milliseconds
#     expr       min        lq    median        uq       max neval
# f1(myls)  57.66834  58.46979  59.39131  61.43861 102.27333    10
# f2(myls) 393.81459 404.29019 418.03128 422.87740 494.79857    10
# f3(myls) 288.39078 299.51680 305.21727 314.75482 374.48683    10
# f4(myls)  52.54991  53.26575  55.34472  59.25559  75.19658    10

1
data.table的当前开发版本v1.9.5中,有一个新函数transpose(),它可以转置列表。我们可以结合setDT()使用它,如下所示。
require(data.table) # v1.9.5+
ll = lapply(1:1e6, function(x) sample(100, 2))
dt = setDT(transpose(ll))

system.time(setDT(transpose(ll))
#    user  system elapsed 
#   0.073   0.000   0.073 

如果你想要一个数据框,那么你可以在这个结果上使用setDF()(这将把一个数据表转换为数据框而不进行任何复制)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接