如何将含多字段的列表转换为带嵌套列的tibble?
通用方法将嵌套列表转换为指定格式的tibble
问题说明
需要将包含多个字段的嵌套列表转换为tibble,要求:
- Category字段唯一无重复,每行对应一个Category
- 其他字段可为单值、多值或缺失
- 多值字段需转为嵌套列表,单值字段保持原类型,缺失字段填充为NA
- 方案需支持任意数量的字段
示例输入数据
lst <- list( "A"=list( "Category"="A", "Team"=c("x"), "City"="N" ), "C"=list( "Category"="C", "Team"=c( "x","v","z" ), "City"="V" ), "F"=list( "Category"="F", "Team"=c("o") ), "K"=list( "Category"="K", "Team"=c( "f","h","y" ), "City"="T" ), "B"=list( "Category"="B", "City"=c("Q") ) )
期望输出
Category Team City <chr> <list> <chr> 1 A <chr [1]> N 2 C <chr [3]> V 3 F <chr [1]> NA 4 K <chr [3]> T 5 B <chr [1]> Q
尝试代码及报错信息
map_dfr(lst,~as_tibble(.x)) |> group_by(Category) |> summarise( across(everything(), ~ { if (n_distinct(.) == 1) { first(.) } else { list(.) } }) )
报错:
Error in `summarise()`: ℹ In argument: `across(...)`. Caused by error: ! `Team` must return compatible vectors across groups. ℹ Result of type <character> for group A: `Category = "A"`. ℹ Result of type <list> for group C: `Category = "C"`. Run `rlang::last_trace()` to see where the error occurred.
报错原因:同一字段在不同分组中返回的类型不一致(字符 vs 列表),导致tibble列类型无法统一。
通用解决方案
方法一:先统一转为列表类型,再按需展开标量字段
library(dplyr) library(purrr) # 1. 合并所有子列表,自动补全缺失字段为NA # 2. 按Category分组,将每个字段的值转为列表(统一类型) # 3. 对所有元素长度为1的字段,展开为标量类型 result <- bind_rows(lst) %>% group_by(Category) %>% summarise( across(everything(), ~ list(unique(.))), .groups = "drop" ) %>% mutate(across(-Category, function(col) { # 判断字段所有元素是否都是长度为1的列表 if (all(map_lgl(col, ~ length(.x) == 1))) { unlist(col) # 展开为标量 } else { col # 保持列表类型 } })) print(result)
方法二:先处理每个子列表,再合并
library(dplyr) library(purrr) # 处理每个子列表:补全缺失字段,将向量转为列表 processed_lst <- map(lst, function(x) { # 获取所有可能的字段名 all_fields <- unique(unlist(map(lst, names))) # 补全缺失字段为NA x[setdiff(all_fields, names(x))] <- NA # 将向量类型字段转为列表(统一为列表类型) modify(x, function(val) { if (is.vector(val) && !is.list(val)) list(val) else val }) }) # 合并为tibble,再按需展开标量字段 result <- map_dfr(processed_lst, as_tibble) %>% mutate(across(-Category, function(col) { if (all(map_lgl(col, ~ length(.x) == 1))) { unlist(col) } else { col } })) print(result)
两种方法都能得到符合要求的输出,且支持任意数量的字段:
- 自动处理缺失字段,填充为NA
- 多值字段保持嵌套列表类型
- 单值字段自动转为标量类型
- 完全兼容任意数量的字段,无需针对特定字段编写代码
内容的提问来源于stack exchange,提问作者mindlessgreen
相关产品推荐
相关产品推荐

