我怎样才能最好地将嵌套列表展平为 R 中的 data.frame?

How can I best flatten a nested list to a data.frame in R?

我想将以 json 形式出现的余数 api 的信息转换为 data.frame。该列表是嵌套的,理论上我可以重复调用 purrr::flatten() 以到达列表底部,然后使用例如 purrr:::map_dfr 和 magrittr:::extract 提取信息。然而,这是非常特定于领域的,并且在从多个 "hierarchies" 中提取信息时效果不佳。我在 R 中有以下设置:

library(rjson)

url <- "https://api3.geo.admin.ch/rest/services/api/SearchServer?searchText=Avenue de Lavaux 63, 1009 Pully&origins=address&type=locations"
result <- rjson::fromJSON(file = URLencode(url))

出现两个问题:

  1. 我怎样才能很好地提取细节、x 和 y 等属性并将它们写入 data.frame?
  2. 最重要的是,我如何直接通过名称提取值。这就是如何提取重量、x、y 和细节的值。

非常感谢。

您可以取消列出结果并像这样提取 x 和 y:

res <- unlist(result)
res['results.attrs.x']
# results.attrs.x 
#  "151398.09375"

res['results.attrs.y']
# results.attrs.y 
#  "540429.3125"

您可以像这样获取所有其他值的名称:

names(res)
#[1] "results.id"  "results.weight"  "results.attrs.origin"         
#    "results.attrs.geom_quadindex" "results.attrs.zoomlevel"     
#[6] "results.attrs.featureId" "results.attrs.lon" "results.attrs.detail"   
#    "results.attrs.rank" "results.attrs.geom_st_box2d" "results.attrs.lat"
#    "results.attrs.num" "results.attrs.y" "results.attrs.x"  "results.attrs.label" 

然后你可以将它们合并到一个数据框中:

res_df <- data.frame(
  X = res['results.attrs.x'],
  Y = res['results.attrs.y']
)

我也会unlist。但请注意,当您 unlist 时,列表的名称会以特定方式更改。例如,result$results[[1]]$id 变为 results.idresult$results[[1]]$weight 变为 results.weight。我们可以使用这个 属性 来定义感兴趣的键并使用 lapply 将它们提取到单独的列表中。然后我们可以将列表转换为单独的数据框。

result1 <- unlist(result)
keys <- c("detail", "x", "y", "weight")

df1 <- as.data.frame(lapply(keys, function(x) 
          unname(result1[grepl(paste0("\.", x), names(result1))])))
names(df1) <- values

df1
#                                           detail            x           y weight
#1 avenue de lavaux 63 1009 pully 5590 pully ch vd 151398.09375 540429.3125      7

提取所有数据可能更容易,也更有用。这通过简单地将名称提取到新的数据框变量,使用这些名称传播属性,然后取消嵌套值来解决长变量名称的问题:

library(tidyverse)

as_tibble(result$results[[1]]) %>% 
    mutate(attr_names = names(attrs)) %>% 
    spread(attr_names, attrs) %>% 
    unnest()

这将 return 如下所示的数据框:

# A tibble: 1 x 15
       id weight detail  featureId geom_quadindex geom_st_box2d  label   lat   lon   num origin  rank      x      y zoomlevel
    <dbl>  <dbl> <chr>   <chr>     <chr>          <chr>          <chr> <dbl> <dbl> <dbl> <chr>  <dbl>  <dbl>  <dbl>     <dbl>
1 2172570      7 avenue… 785542_0  0212222220211… BOX(540429.29… Aven…  46.5  6.66    63 addre…     7 1.51e5 5.40e5        10

根据您的回答并受到启发,我现在使用以下管道

library(tidyverse)

result %>%
   pluck("results", 1) %>%
   flatten() %>% 
   as_tibble() %>% 
   select(id, weight, detail, x, y)

编辑:不是对问题的直接回答,但我认为这是一种普遍有用的方法。

我这两天在研究这个问题。我认为最好的解决方案是 unlist()。这不依赖于嵌套并保留名称。

library(tidyverse)
result %>% unlist() %>% 
tibble(names = names(.), values = .) %>% # take names as column in tibble
mutate(
    n_seps = str_count(names, "\.") # count separators to separate to unknown number of columns
) %>%
separate(names,str_c(
        "col_",
        c(1:(max(.$n_seps) +1)
        )), sep = "\."
        ) %>% 
select(-n_seps)

唯一需要注意的是,如果列名已经包含“.”,这将为每个“.”添加一个嵌套级别。我找到了递归重命名列表的解决方案:.

输出:

result <- rjson::fromJSON(file = "c:/Users/elshikh/Downloads/SearchServer.json")


mydailyhelpers::tidy_nested_lists(result)
#> Warning: Expected 3 pieces. Missing pieces filled with `NA` in 2 rows [1, 2].
#> # A tibble: 15 x 4
#>    col_1   col_2  col_3          values                                         
#>    <chr>   <chr>  <chr>          <chr>                                          
#>  1 results id     <NA>           2692129                                        
#>  2 results weight <NA>           1                                              
#>  3 results attrs  origin         address                                        
#>  4 results attrs  geom_quadindex 021222222021110121000                          
#>  5 results attrs  zoomlevel      10                                             
#>  6 results attrs  featureId      785542_0                                       
#>  7 results attrs  lon            6.66245317459106                               
#>  8 results attrs  detail         avenue de lavaux 63 1009 pully 5590 pully ch vd
#>  9 results attrs  rank           7                                              
#> 10 results attrs  geom_st_box2d  BOX(540428.897 151398.511999999,540428.897 151~
#> 11 results attrs  lat            46.5112342834473                               
#> 12 results attrs  num            63                                             
#> 13 results attrs  y              540428.875                                     
#> 14 results attrs  x              151398.515625                                  
#> 15 results attrs  label          Avenue de Lavaux 63 <b>1009 Pully</b>

reprex package (v2.0.1)

创建于 2022-02-16