假设以下列表:

x <- list(list(q = 1880L, properties = list(), last_Import_Date = "2024-09-16"), 
          list(q = 1888L, properties = list(list(a = "x", b = "y")), last_Import_Date = "2024-09-16"),
          list(q = 1890L, properties = list(list(a = "x", b = "y")), last_Import_Date = "2024-09-16"))

我想将此列表转换为数据框(按行)。通常,dplyr::bind_rows效果很好。但是,我的列表中有一些元素有时为空(“属性”),在这种情况下 bind_rows 会失败,因为它只保留那些不为空的行。

有人能解释一下这是为什么吗?

有没有什么 (简短的) 解决方法?我目前正在使用相当丑陋的解决方法,使用 list2DF,然后转置,然后转换为数据框,然后分配名称。

错误的结果(仅保留非空属性):

x |>
  bind_rows()

# A tibble: 2 × 3
      q properties       last_Import_Date
  <int> <list>           <chr>           
1  1888 <named list [2]> 2024-09-16      
2  1890 <named list [2]> 2024-09-16 

更新:我需要一些额外的帮助来解除这种特殊的“属性”列的嵌套。使用unnest_longer将导致删除 NULL 行的相同“错误”,而使用 unnest_wider 需要一些额外的解决方法来修复名称。


最佳答案
3

bind_rowsvctrs::data_frame底层使用。事实证明vctrs::data_frame,当存在长度为 0 的元素时(即 list(0)、interger(0)、character(0) 等),会创建空数据框:

vctrs::data_frame(!!!list(q = 1880L, properties = list(), last_Import_Date = "2024-09-16"),.name_repair="unique")
[1] q                properties       last_Import_Date
<0 rows> (or 0-length row.names)

vctrs::data_frame(a=list("a"),b= integer(0))
[1] a b
<0 rows> (or 0-length row.names)

vctrs::data_frame(a=list(),b= 1)
[1] a b
<0 rows> (or 0-length row.names)

另一种方法是使用vctrs::vec_rbind

vctrs::vec_rbind(!!!x)
     q properties last_Import_Date
1 1880       NULL       2024-09-16
2 1888       x, y       2024-09-16
3 1890       x, y       2024-09-16

3

  • 您说到点子上了!解决方法又好又简单,+1!


    – 

  • 我应该更清楚地说明我需要修复/解决方法的地方,即如何轻松地取消嵌套这样的列(“属性”)。将更新我的帖子。


    – 

  • 对于接受哪个答案,@G.Grothendieck 和 ThomasIsCoding 很难做出决定,但我决定接受这个答案,因为它回答了我最初的问题(即为什么 bind_rows 会失败)。如果可以的话,我会接受所有答案,因为它们提供了有价值的信息和解决方案。


    – 

bind_rows1)如果您像这样预先和之后处理输入,bind_rows将起作用:

library(dplyr)
x |> lapply(unlist) |> bind_rows() |> type.convert(as.is = TRUE)

## # A tibble: 3 × 4
##       q last_Import_Date properties.a properties.b
##   <int> <chr>            <chr>        <chr>       
## 1  1880 2024-09-16       <NA>         <NA>        
## 2  1888 2024-09-16       x            y           
## 3  1890 2024-09-16       x            y           

2) 转置转置x然后删除中的额外列表层properties允许我们使用hoist提升ab从中使用properties

library(purrr)
library(tidyr)
x |>
  transpose() |>
  list2DF() |>
  transform(properties = lapply(properties, unlist)) |>
  hoist(properties, "a", "b")

##      q    a    b last_Import_Date
## 1 1880 <NA> <NA>       2024-09-16
## 2 1888    x    y       2024-09-16
## 3 1890    x    y       2024-09-16

3) 基数 R如果的列表列properties足够,则此双重迭代仅使用基数 R:

Map(\(z) sapply(x, "[[", z), names(x[[1]])) |> list2DF()

##      q properties last_Import_Date
## 1 1880       NULL       2024-09-16
## 2 1888       x, y       2024-09-16
## 3 1890       x, y       2024-09-16

4)rrapply rrapply可以直接创建数据框:

library(rrapply)
rrapply(x, how = "bind")

##      q last_Import_Date properties.1.a properties.1.b
## 1 1880       2024-09-16           <NA>           <NA>
## 2 1888       2024-09-16              x              y
## 3 1890       2024-09-16              x              y

更新

如果您想使用unnest而不删除中的空条目properties,您应该指定选项keep_empty = TRUE(基于

vctrs::vec_rbind(!!!x) %>%
    unnest(cols = everything(), keep_empty = TRUE)

由此得出

# A tibble: 3 × 3
      q properties       last_Import_Date
  <int> <list>           <chr>
1  1880 <NULL>           2024-09-16
2  1888 <named list [2]> 2024-09-16
3  1890 <named list [2]> 2024-09-16 

其基本 R 等价关系可能是

list2DF(
    lapply(
        as.data.frame(do.call(rbind, x)),
        \(v) unlist(replace(v, lengths(v) == 0, list(list(NULL))), FALSE)
    )
) 

由此得出

     q properties last_Import_Date
1 1880       NULL       2024-09-16
2 1888       x, y       2024-09-16
3 1890       x, y       2024-09-16

结构如下

'data.frame':   3 obs. of  3 variables:
 $ q               : int  1880 1888 1890
 $ properties      :List of 3
  ..$ : NULL
  ..$ :List of 2
  .. ..$ a: chr "x"
  .. ..$ b: chr "y"
  ..$ :List of 2
  .. ..$ a: chr "x"
  .. ..$ b: chr "y"
 $ last_Import_Date: chr  "2024-09-16" "2024-09-16" "2024-09-16"

较旧(快速修复)

这是一个基本的 R 快速修复

> as.data.frame(do.call(rbind, x))
     q properties last_Import_Date
1 1880       NULL       2024-09-16
2 1888       x, y       2024-09-16
3 1890       x, y       2024-09-16

它的结构如下

> as.data.frame(do.call(rbind, x)) %>% str()
'data.frame':   3 obs. of  3 variables:
 $ q               :List of 3
  ..$ : int 1880
  ..$ : int 1888
  ..$ : int 1890
 $ properties      :List of 3
  ..$ : list()
  ..$ :List of 1
  .. ..$ :List of 2
  .. .. ..$ a: chr "x"
  .. .. ..$ b: chr "y"
  ..$ :List of 1
  .. ..$ :List of 2
  .. .. ..$ a: chr "x"
  .. .. ..$ b: chr "y"
 $ last_Import_Date:List of 3
  ..$ : chr "2024-09-16"
  ..$ : chr "2024-09-16"
  ..$ : chr "2024-09-16"

2

  • 谢谢!我应该更清楚我需要修复/解决方法的地方,即如何轻松地取消嵌套这样的列(“属性”)。我会更新我的帖子。


    – 

  • @deschen 请检查更新是否符合您的要求


    –