【问题标题】:How can I best flatten a nested list to a data.frame in R?如何最好地将嵌套列表展平为 R 中的 data.frame?
【发布时间】:2025-11-28 10:50:03
【问题描述】:

我想将来自 json 形式的 rest api 的信息转换为 data.frame。该列表是嵌套的,理论上我可以重复调用 purrr::flatten() 以到达列表的底部,然后使用例如 purrr:::map_dfr 和 magrittr:::extract 提取信息。但是,这是非常特定于域的,并且在从多个“层次结构”中提取信息时效果不佳。我在 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 和细节值的方法。

非常感谢。

【问题讨论】:

  • 也可以试试unlist(result)
  • @RAB 是的,很抱歉。我更正了。

标签: r json purrr rjson


【解决方案1】:

您可以取消列出结果并像这样提取 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']
)

【讨论】:

    【解决方案2】:

    我也会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
    

    【讨论】:

    • @Ronak Shah 非常感谢您的回复。这是我担心的一个问题(并没有特别提到)。我很乐意使用您的方法。
    【解决方案3】:

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

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

    这将返回如下数据框:

    # 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
    

    【讨论】:

    • 谢谢格什特。这是一个很好的解决方案,甚至更好,因为我更喜欢使用 dplyr。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

      最近几天我一直在研究这个问题。我认为最好的解决方案是 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)
      

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

      输出:

      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 创建

      【讨论】:

        最近更新 更多