将列表转换为数据框


513

我有一个嵌套的数据列表。它的长度是132,每个项目都是一个长度为20的列表。是否有一种快速的方法将该结构转换为具有132行和20列数据的数据帧?

以下是一些示例数据:

l <- replicate(
  132,
  list(sample(letters, 20)),
  simplify = FALSE
)

因此,您希望每个列表元素都作为data.frame中数据的一行吗?
约书亚·乌尔里希

2
@RichieCotton这不是正确的例子。“每个项目都是长度为20 的列表 ”,您得到的每个项目都是长度为20 的向量的一个元素列表
。– Marek

1
晚会晚了,但是我没有看到任何人提到这个,我认为这非常方便(对于我一直想做的事情)。
mflo-ByeSE


Answers:


390

假设您的列表列表称为l

df <- data.frame(matrix(unlist(l), nrow=length(l), byrow=T))

上面的代码将所有字符列转换为因子,为避免这种情况,您可以向data.frame()调用添加参数:

df <- data.frame(matrix(unlist(l), nrow=132, byrow=T),stringsAsFactors=FALSE)

109
如果您的数据不是全部属于同一类型,请注意此处。通过矩阵意味着所有数据将被强制转换为通用类型。即,如果您有一列字符数据和一列数字数据,则数字数据将由matrix()强制转换为字符串,然后由data.frame()强制转换为因子。
伊恩·苏德贝里

在列表中缺少值或在数据帧中包含NA的最佳方法是什么?
戴夫

1
@Dave:为我工作...在这里看到r-fiddle.org/#/fiddle?id=y8DW7lqL&version=3
nico

4
如果字符数据类型也要小心-data.frame会将其转换为因子。
亚历克斯·布朗

4
@nico是否可以将列表元素名称保留为df中的colnames或rownames?
N.Varela 2015年

472

rbind

do.call(rbind.data.frame, your_list)

编辑:返回的data.framelist的旧版本,而不是矢量(如@IanSudbery在注释中指出的)。


5
为什么这样做有效,但rbind(your_list)返回1x32列表矩阵?
eykanal 2011年

26
@eykanal do.call将的元素your_list作为参数传递给rbind。等于rbind(your_list[[1]], your_list[[2]], your_list[[3]], ....., your_list[[length of your_list]])
马雷克(Marek)

2
该方法遭受空状态的困扰。
弗兰克·王

3
@FrankWANG但是此方法并非设计为无效的情况。要求your_list包含大小相等的向量。NULL长度为0,因此应该失败。
马雷克(Marek)2012年

12
该方法似乎返回正确的对象,但是在检查该对象时,您会发现列是列表而不是向量,如果不期望会导致问题。
伊恩·苏德贝里

135

您可以使用该plyr包。例如,表单的嵌套列表

l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
      , b = list(var.1 = 4, var.2 = 5, var.3 = 6)
      , c = list(var.1 = 7, var.2 = 8, var.3 = 9)
      , d = list(var.1 = 10, var.2 = 11, var.3 = 12)
      )

现在的长度为4,并且每个列表中都l包含另一个长度为3的列表。现在您可以运行

  library (plyr)
  df <- ldply (l, data.frame)

并应获得与答案@Marek和@nico相同的结果。


8
好答案。我可以解释一下它是如何工作的吗?它只是为每个列表条目返回一个数据框?
迈克尔·巴顿

13
我最好的答案。它返回一个诚实的data.frame。所有数据类型(字符,数字等)均已正确转换。如果列表具有不同的数据类型,则它们都将通过matrix方法转换为字符。
罗阿

1
这里提供的示例不是问题提供的示例。此答案在原始数据集上的结果不正确。
MySchizoBuddy

对我来说很棒!并设置了结果数据框中的列名!Tx
ban 2016年

plyr是多核的吗?还是有与mclapply一起使用的lapply版本?
Garglesoap19年

103

data.frame(t(sapply(mylistlist,c)))

sapply将其转换为矩阵。 data.frame将矩阵转换为数据帧。


19
迄今为止最好的答案!其他解决方案都无法正确获得类型/列名称。谢谢!
d_a_c321 2014年

1
您打算c在这里扮演什么角色?列表数据的一个实例?哦,等等,c是连接函数吗?与@mnel的c用法混淆。我也同意@dchandler,在我的用例中,正确设置列名称是一个宝贵的需求。出色的解决方案。
jxramos 2014年

那个权利-标准C函数; 来自?cCombine Values into a Vector or List
亚历克斯·布朗

1
不适用于问题中提供的样本数据
MySchizoBuddy 2015年

3
这不是生成列表的data.frame吗?
卡尔

69

假设您的列表名为L

data.frame(Reduce(rbind, L))

2
好一个!@Alex Brown的解决方案与您的解决方案相比有一个区别,由于某种原因,您的路线产生以下警告消息:`警告消息:在data.row.names(row.names,rowsi,i)中:有些row.names重复:3,4 - > row.names不使用”
jxramos

很好!!工作对我来说在这里:stackoverflow.com/questions/32996321/...
阿纳斯塔西Pupynina

2
除非列表中只有一个元素,否则它data.frame(Reduce(rbind, list(c('col1','col2'))))会很好地工作:产生一个包含2行1列的数据帧(我希望1行2列)
Red Pea 2015年

61

该软件包data.table具有的rbindlist超快实现功能do.call(rbind, list(...))

它可以采取的一个列表 listsdata.framesdata.tables 作为输入。

library(data.table)
ll <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
  , b = list(var.1 = 4, var.2 = 5, var.3 = 6)
  , c = list(var.1 = 7, var.2 = 8, var.3 = 9)
  , d = list(var.1 = 10, var.2 = 11, var.3 = 12)
  )

DT <- rbindlist(ll)

这会传回的data.table继承data.frame

如果您真的想转换回data.frame,请使用as.data.frame(DT)


关于最后一行,setDF现在允许通过引用返回到data.frame。
弗兰克,

1
对于我的有3万个商品的列表,rbindlist的工作方式比ldply快得多
塔尔哈里什(tallharish)

35

所述tibble封装具有一个功能enframe(),解决了此问题,通过强迫嵌套list对象嵌套tibble(“整洁”的数据帧)的对象。这是R for Data Science的一个简短示例:

x <- list(
    a = 1:5,
    b = 3:4, 
    c = 5:6
) 

df <- enframe(x)
df
#> # A tibble: 3 × 2
#>    name     value
#>   <chr>    <list>
#>    1     a <int [5]>
#>    2     b <int [2]>
#>    3     c <int [2]>

由于列表中有多个嵌套l,因此您可以使用unlist(recursive = FALSE)删除不必要的嵌套,以获取单个层次结构列表,然后传递给enframe()。我tidyr::unnest()通常将输出取消嵌套到单个级别的“整洁”数据框中,该数据框有您的两列(一列用于组name,一列用于与组的观察value)。如果要使列变宽,则可以使用add_column()该列来添加,只需将值的顺序重复132次即可。然后就是spread()值。


library(tidyverse)

l <- replicate(
    132,
    list(sample(letters, 20)),
    simplify = FALSE
)

l_tib <- l %>% 
    unlist(recursive = FALSE) %>% 
    enframe() %>% 
    unnest()
l_tib
#> # A tibble: 2,640 x 2
#>     name value
#>    <int> <chr>
#> 1      1     d
#> 2      1     z
#> 3      1     l
#> 4      1     b
#> 5      1     i
#> 6      1     j
#> 7      1     g
#> 8      1     w
#> 9      1     r
#> 10     1     p
#> # ... with 2,630 more rows

l_tib_spread <- l_tib %>%
    add_column(index = rep(1:20, 132)) %>%
    spread(key = index, value = value)
l_tib_spread
#> # A tibble: 132 x 21
#>     name   `1`   `2`   `3`   `4`   `5`   `6`   `7`   `8`   `9`  `10`  `11`
#> *  <int> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1      1     d     z     l     b     i     j     g     w     r     p     y
#> 2      2     w     s     h     r     i     k     d     u     a     f     j
#> 3      3     r     v     q     s     m     u     j     p     f     a     i
#> 4      4     o     y     x     n     p     i     f     m     h     l     t
#> 5      5     p     w     v     d     k     a     l     r     j     q     n
#> 6      6     i     k     w     o     c     n     m     b     v     e     q
#> 7      7     c     d     m     i     u     o     e     z     v     g     p
#> 8      8     f     s     e     o     p     n     k     x     c     z     h
#> 9      9     d     g     o     h     x     i     c     y     t     f     j
#> 10    10     y     r     f     k     d     o     b     u     i     x     s
#> # ... with 122 more rows, and 9 more variables: `12` <chr>, `13` <chr>,
#> #   `14` <chr>, `15` <chr>, `16` <chr>, `17` <chr>, `18` <chr>,
#> #   `19` <chr>, `20` <chr>

引用OP:“是否有一种快速的方法将该结构转换为具有132行和20列数据的数据帧?” 因此,也许您需要扩展步骤或其他步骤。
弗兰克,

1
是的,只需要一个可以扩展的索引列即可。我会尽快更新。
马特·丹乔

17

根据列表的结构,有些tidyverse选项可以很好地用于不等长列表:

l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
        , b = list(var.1 = 4, var.2 = 5)
        , c = list(var.1 = 7, var.3 = 9)
        , d = list(var.1 = 10, var.2 = 11, var.3 = NA))

df <- dplyr::bind_rows(l)
df <- purrr::map_df(l, dplyr::bind_rows)
df <- purrr::map_df(l, ~.x)

# all create the same data frame:
# A tibble: 4 x 3
  var.1 var.2 var.3
  <dbl> <dbl> <dbl>
1     1     2     3
2     4     5    NA
3     7    NA     9
4    10    11    NA

您还可以混合矢量和数据帧:

library(dplyr)
bind_rows(
  list(a = 1, b = 2),
  data_frame(a = 3:4, b = 5:6),
  c(a = 7)
)

# A tibble: 4 x 2
      a     b
  <dbl> <dbl>
1     1     2
2     3     5
3     4     6
4     7    NA

这个dplyr :: bind_rows函数可以很好地工作,即使很难处理以JSON开头的列表。从JSON到非常干净的数据框。真好
GGAnderson

@sbha我尝试使用df <-purrr :: map_df(l,〜.x),但它似乎无法正常工作,我X2收到的错误消息是错误:无法将列从整数转换为字符
Jolin

16

Reshape2产生的输出与上面的plyr示例相同:

library(reshape2)
l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
          , b = list(var.1 = 4, var.2 = 5, var.3 = 6)
          , c = list(var.1 = 7, var.2 = 8, var.3 = 9)
          , d = list(var.1 = 10, var.2 = 11, var.3 = 12)
)
l <- melt(l)
dcast(l, L1 ~ L2)

产量:

  L1 var.1 var.2 var.3
1  a     1     2     3
2  b     4     5     6
3  c     7     8     9
4  d    10    11    12

如果您几乎没有像素,则可以用 recast()在1行中完成所有操作。


12

此方法使用tidyverse包(purrr)。

名单:

x <- as.list(mtcars)

将其转换为数据帧(tibble更具体地说):

library(purrr)
map_df(x, ~.x)

10

扩展@Marek的答案:如果您想避免字符串变成因素并且效率不是问题,请尝试

do.call(rbind, lapply(your_list, data.frame, stringsAsFactors=FALSE))

10

对于具有3个或更多级别的深度嵌套列表的一般情况,例如从嵌套JSON获得的列表:

{
"2015": {
  "spain": {"population": 43, "GNP": 9},
  "sweden": {"population": 7, "GNP": 6}},
"2016": {
  "spain": {"population": 45, "GNP": 10},
  "sweden": {"population": 9, "GNP": 8}}
}

首先考虑melt()将嵌套列表转换为高格式的方法:

myjson <- jsonlite:fromJSON(file("test.json"))
tall <- reshape2::melt(myjson)[, c("L1", "L2", "L3", "value")]
    L1     L2         L3 value
1 2015  spain population    43
2 2015  spain        GNP     9
3 2015 sweden population     7
4 2015 sweden        GNP     6
5 2016  spain population    45
6 2016  spain        GNP    10
7 2016 sweden population     9
8 2016 sweden        GNP     8

然后dcast()再扩大到一个整洁的数据集,其中每个变量形成一个列,每个观察值形成一个行:

wide <- reshape2::dcast(tall, L1+L2~L3) 
# left side of the formula defines the rows/observations and the 
# right side defines the variables/measurements
    L1     L2 GNP population
1 2015  spain   9         43
2 2015 sweden   6          7
3 2016  spain  10         45
4 2016 sweden   8          9


8

有时,您的数据可能是相同长度的向量列表的列表。

lolov = list(list(c(1,2,3),c(4,5,6)), list(c(7,8,9),c(10,11,12),c(13,14,15)) )

(内部向量也可以是列表,但是我正在简化以使其更易于阅读)。

然后,您可以进行以下修改。请记住,您可以一次取消列出一个级别:

lov = unlist(lolov, recursive = FALSE )
> lov
[[1]]
[1] 1 2 3

[[2]]
[1] 4 5 6

[[3]]
[1] 7 8 9

[[4]]
[1] 10 11 12

[[5]]
[1] 13 14 15

现在,使用其他答案中提到的您喜欢的方法:

library(plyr)
>ldply(lov)
  V1 V2 V3
1  1  2  3
2  4  5  6
3  7  8  9
4 10 11 12
5 13 14 15


4
l <- replicate(10,list(sample(letters, 20)))
a <-lapply(l[1:10],data.frame)
do.call("cbind", a)

3

对于使用purrr一系列解决方案的并行(多核,多会话等)解决方案,请使用:

library (furrr)
plan(multisession) # see below to see which other plan() is the more efficient
myTibble <- future_map_dfc(l, ~.x)

l清单在哪里。

要确定最有效的基准,plan()可以使用:

library(tictoc)
plan(sequential) # reference time
# plan(multisession) # benchamark plan() goes here. See ?plan().
tic()
myTibble <- future_map_dfc(l, ~.x)
toc()

3

以下简单命令对我有用:

myDf <- as.data.frame(myList)

参考(Quora回答

> myList <- list(a = c(1, 2, 3), b = c(4, 5, 6))
> myList
$a
[1] 1 2 3

$b
[1] 4 5 6

> myDf <- as.data.frame(myList)
  a b
1 1 4
2 2 5
3 3 6
> class(myDf)
[1] "data.frame"

但是,如果将列表转换为数据框的方式不明显,则将失败:

> myList <- list(a = c(1, 2, 3), b = c(4, 5, 6, 7))
> myDf <- as.data.frame(myList)
Error in (function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE,  : 
  arguments imply differing number of rows: 3, 4

注意:答案是针对问题的标题,可能会跳过问题的一些详细信息


请注意,从问题中输入的内容仅适用于这种情况。OP要求132行20列,但这给出20行132列。
格雷戈尔·托马斯

对于您使用不同长度的输入失败的示例,尚不清楚期望的结果是什么...
Gregor Thomas

@Gregor是的,但问题标题是“ R-数据框列表”。这个问题的许多访问者和投票者并没有OP的确切问题。根据问题标题,他们只是寻找一种将列表转换为数据框的方法。我自己也遇到了同样的问题,我发布的解决方案解决了我的问题
Ahmad

是的,只是注意。不投票。最好在答案中指出它所做的事情几乎与所有其他答案类似,但有明显不同。
格雷戈尔·托马斯

1

一种简单(但可能不是最快)的方法是使用base r,因为数据帧只是等长向量列表。因此,您的输入列表和30 x 132 data.frame之间的转换将是:

df <- data.frame(l)

从那里,我们可以将其转置为132 x 30矩阵,并将其转换回数据帧:

new_df <- data.frame(t(df))

作为单线:

new_df <- data.frame(t(data.frame(l)))

行名看起来很烦人,但是您总是可以使用

rownames(new_df) <- 1:nrow(new_df)


2
为什么这被否决?我想知道,所以我不会继续散布错误信息。
Will C

我之前肯定已经使用data.frame和t的组合来做到这一点!我猜那些投票否决的人会觉得有更好的方法,尤其是那些不会弄乱名字的方法。
Arthur Yip

1
很好,我想如果要将名称保留在列表中也是不正确的。
C

0

如何将map_函数与for循环一起使用?这是我的解决方案:

list_to_df <- function(list_to_convert) {
  tmp_data_frame <- data.frame()
  for (i in 1:length(list_to_convert)) {
    tmp <- map_dfr(list_to_convert[[i]], data.frame)
    tmp_data_frame <- rbind(tmp_data_frame, tmp)
  }
  print(tmp_data_frame)
}

其中,map_dfr将每个列表元素的成data.frame,然后rbind工会通通。

就您而言,我想应该是:

converted_list <- list_to_df(l)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.