我怎样才能最好地将嵌套列表展平为 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))
出现两个问题:
- 我怎样才能很好地提取细节、x 和 y 等属性并将它们写入 data.frame?
- 最重要的是,我如何直接通过名称提取值。这就是如何提取重量、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.id
,result$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
我想将以 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))
出现两个问题:
- 我怎样才能很好地提取细节、x 和 y 等属性并将它们写入 data.frame?
- 最重要的是,我如何直接通过名称提取值。这就是如何提取重量、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.id
,result$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