展平嵌套列表并保留每个底层元素的所有父键

Flatten nested list and retain all parent keys for each bottom-level element

我需要将一个任意嵌套列表展平到一个数据框,并在一列中保留键/索引的路径,同时将底层的每个元素提取到一个单独的元素行。

考虑以下列表:

lst <- list(
    animals = list(
        lamas = c("brown", "white"),
        primates = list(
            humans = c("asia", "europe"),
            apes = c("good", "fast", "angry")
        )
    ),
    objects = c("expensive", "cheap"),
    plants = NULL
)

flatten_list(lst, delimiter="_") 的结果应该如下所示:

data.frame(
  path = c("animals_lamas", "animals_lamas", "animals_primates_humans", "animals_primates_humans", "animals_primates_apes", "animals_primates_apes", "animals_primates_apes", "objects", "objects", "plants"),
  value = c("brown", "white", "asia", "europe", "good", "fast", "angry", "expensive", "cheap", NA)
)

令我惊讶的是我无法使用 tidyr 或 data.tables 实现这一目标。我是否需要递归函数,或者是否有一些开箱即用的解决方案?赞赏!

编辑: akrun 提供的解决方案适用于原始数据。我意识到当元素在底层 NULL 时会出现问题,因此改写了问题。

EDIT2 我目前的解决方法是在应用 akrun 解决方案之前递归地用 NA 替换 NULL,使用函数 [再次由 akrun ;)].

可以通过 melt 进入 data.frame 然后 unite 键列

来完成
library(reshape2)
library(dplyr)
library(tidyr)
out2 <- melt(lst) %>% 
        unite(path, L1:L3, sep = "_", na.rm = TRUE) %>% 
        select(path, value)

-检查 OP 的输出

> all.equal(out, out2)
[1] TRUE

我们也可以用 unliststack 来自 base R

stack(unlist(lapply(lst, \(x) if(is.null(x)) NA_character_ else x)))[2:1]

可以对付NULL的解决方案,基于rrapply

library(tidyverse)
library(rrapply)

rrapply(lst, f = \(x) if (is.null(x)) NA else x, how = "melt") %>% 
  unnest(value) %>% unite(path, L1:L3, na.rm = T)

#> # A tibble: 10 × 2
#>    path                    value    
#>    <chr>                   <chr>    
#>  1 animals_lamas           brown    
#>  2 animals_lamas           white    
#>  3 animals_primates_humans asia     
#>  4 animals_primates_humans europe   
#>  5 animals_primates_apes   good     
#>  6 animals_primates_apes   fast     
#>  7 animals_primates_apes   angry    
#>  8 objects                 expensive
#>  9 objects                 cheap    
#> 10 plants                  <NA>