JSON转R DataFrame遇阻,求可行转换方案及原理说明
嵌套JSON转R DataFrame的解决方案及原理说明
问题背景
我尝试将JSON数据转换为DataFrame,使用as.data.frame(ss_dtd)和以下代码均无效:
test <- lapply(ss_dtd, function(x) { x[sapply(x, is.null)] <- NA unlist(x) }) test2 <-as.data.frame(do.call("cbind", test))
通过ss_dtd <- jsonlite::fromJSON("original_json.json")导入的JSON结构如下(str(ss_dtd)输出):
List of 8232 $ billy@fakeemail.com :List of 2 ..$ joins :'data.frame': 1 obs. of 2 variables: .. ..$ project: chr "Sunflower Project" .. ..$ when : chr "2020-09-18T13:09:53.789030" ..$ participation: list() $ sara@fakeemail.com :List of 2 ..$ joins :'data.frame': 1 obs. of 2 variables: .. ..$ project: chr "Project with Cells" .. ..$ when : chr "2020-05-12T15:15:40.557827" ..$ participation: list() $ jerry@fakeemail.com :List of 2 ..$ joins :'data.frame': 2 obs. of 2 variables: .. ..$ project: chr [1:2] "Water Project" "Sunflower Project" .. ..$ when : chr [1:2] "2022-01-12T15:37:45.467523" "2020-10-15T18:57:59.623376" ..$ participation:'data.frame': 1 obs. of 3 variables: .. ..$ project : chr "Sunflower Project" .. ..$ when : chr "2020-10-15T20:16:12.689239" .. ..$ repetitions: int 1 $ randy@fakeemail.com :List of 2 ..$ joins :'data.frame': 1 obs. of 2 variables: .. ..$ project: chr "Cloud Project" .. ..$ when : chr "2022-02-17T20:05:24.991847" ..$ participation: list()
期望输出的DataFrame结构:
email join_or_participate project when repetitions 1 billy@fakeemail.com join Sunflower Project 2020-09-18T13:09:53.789030 NA 2 sara@fakeemail.com join Project with Cells 2020-05-12T15:15:40.557827 NA 3 jerry@fakeemail.com join Water Project 2022-01-12T15:37:45.467523 NA 4 jerry@fakeemail.com join Sunflower Project 2020-10-15T18:57:59.623376 NA 5 jerry@fakeemail.com participate Sunflower Project 2020-10-15T20:16:12.689239 1 6 randy@fakeemail.com join Cloud Project 2022-02-17T20:05:24.991847 NA
可行转换代码
使用purrr和dplyr包处理嵌套结构,代码如下:
library(purrr) library(dplyr) # 定义单个用户数据的处理函数 process_user <- function(user_data, email) { # 处理joins记录 joins_df <- if (nrow(user_data$joins) > 0) { user_data$joins %>% mutate( email = email, join_or_participate = "join", repetitions = NA_integer_ ) %>% select(email, join_or_participate, project, when, repetitions) } else { NULL } # 处理participation记录 participation_df <- if (nrow(user_data$participation) > 0) { user_data$participation %>% mutate( email = email, join_or_participate = "participate" ) %>% select(email, join_or_participate, project, when, repetitions) } else { NULL } # 合并两类记录 bind_rows(joins_df, participation_df) } # 遍历所有用户并合并结果 final_df <- imap_dfr(ss_dtd, process_user) # 查看最终结果 print(final_df)
转换原理说明
1. 原始结构解析
导入后的ss_dtd是命名列表:
- 列表元素名为用户邮箱
- 每个元素是子列表,包含
joins和participation两个字段:joins为DataFrame,存储用户"加入"项目的记录(含project和when)participation为DataFrame(或空列表),存储用户"参与"项目的记录(多了repetitions字段)
之前方法失效的原因:
as.data.frame(ss_dtd)直接将嵌套DataFrame作为列,无法展开成扁平结构unlist会破坏层级,丢失joins和participation的类型区分
2. 核心逻辑
- 保留类型标记:分别处理两类记录,给每条记录添加
join_or_participate字段区分操作类型 - 统一字段结构:给
joins记录补充repetitions为NA,保证两类记录字段一致 - 批量合并:用
imap_dfr遍历所有用户(同时获取邮箱名和对应数据),自动合并所有记录为扁平DataFrame
3. 关键函数解释
imap_dfr:遍历命名列表,同时传递元素值和名字(此处名字即邮箱),最终自动合并为DataFramebind_rows:合并结构相似的DataFrame,自动补全缺失字段为NAmutate:添加新字段,统一不同类型记录的数据结构
内容的提问来源于Stack Exchange,提问作者Bradley Allf
相关产品推荐
相关产品推荐

