如何将rjson::fromJSON输出转为列名无修改的CSV同款数据框?
问题:用rjson读取含特殊列名的JSON并生成与CSV一致的数据框
我正在研究R读取含不友好列名文件的能力,其中JSON文件可使用rjson或jsonlite读取,jsonlite表现良好不在本次讨论范围内。
示例数据
先给出两段含特殊列名的示例数据(实际数据还包含数值列):
library(rjson) library(dplyr) csv_file <- 'English,中文 (Simplified),中文 (Traditional),فارسى,Русский язык,Tiếng Việt Alice,蔼,佩佩,آیسا,Аделаида,Anh Đào Amy,安,姗姗,افسانه,Анна,Anh Thư Barbara,芳,安娜,اندیشه,Валентина,Bấc Carol,淑芬,寶珠,نادره,Вера,Bạch Tuyểt Elizabeth,菊,小蘭,بهشته,Зинаида,Bảo Châu Jaclyn,兰,小鳳,تبسم,Изабэлла,Chín Jane,丽丽,惠娟,توسکا,Капитолина,Cúc Judy,梅,淑儀,روژین,Лариса,Dậu Katie,敏,燕玲,زرّین تاج,Маргарита,Diễm' json_file <- '[{"English":"Alice","中文 (Simplified)":"蔼","中文 (Traditional)":"佩佩","فارسى":"آیسا","Русский язык":"Аделаида","Tiếng Việt":"Anh Đào"}, {"English":"Amy","中文 (Simplified)":"安","中文 (Traditional)":"姗姗","فارسى":"افسانه","Русский язык":"Анна","Tiếng Việt":"Anh Thư"}, {"English":"Barbara","中文 (Simplified)":"芳","中文 (Traditional)":"安娜","فارسى":"اندیشه","Русский язык":"Валентина","Tiếng Việt":"Bấc"}, {"English":"Carol","中文 (Simplified)":"淑芬","中文 (Traditional)":"寶珠","فارسى":"نادره","Русский язык":"Вера","Tiếng Việt":"Bạch Tuyểt"}, {"English":"Elizabeth","中文 (Simplified)":"菊","中文 (Traditional)":"小蘭","فارسى":"بهشته","Русский язык":"Зинаида","Tiếng Việt":"Bảo Châu"}, {"English":"Jaclyn","中文 (Simplified)":"兰","中文 (Traditional)":"小鳳","فارسى":"تبسم","Русский язык":"Изабэлла","Tiếng Việt":"Chín"}, {"English":"Jane","中文 (Simplified)":"丽丽","中文 (Traditional)":"惠娟","فارسى":"توسکا","Русский язык":"Капитолина","Tiếng Việt":"Cúc"}, {"English":"Judy","中文 (Simplified)":"梅","中文 (Traditional)":"淑儀","فارسى":"روژین","Русский язык":"Лариса","Tiếng Việt":"Dậu"}, {"English":"Katie","中文 (Simplified)":"敏","中文 (Traditional)":"燕玲","فارسى":"زرّین تاج","Русский язык":"Маргарита","Tiếng Việt":"Diễm"}]'
现有尝试与问题
我可以轻松从CSV生成列名未修改的数据框(使用check.names = FALSE),以此作为标准对比其他文件的读取结果:
# 从CSV生成标准数据框 df_csv = read.csv(text = csv_file, check.names = FALSE) # 用rjson读取JSON数据,得到嵌套列表 raw_json <- rjson::fromJSON(json_file)
尝试1:data.frame(do.call(rbind, raw_json), check.names = FALSE)
将嵌套列表合并后转数据框,但生成的所有列都是列表类型,导致setdiff对比报错:
df_json1 <- data.frame(do.call(rbind, raw_json), check.names = FALSE) setdiff(df_csv, df_json1)
报错信息:
Error in `setdiff()`: ! `x` and `y` are not compatible. ✖ Incompatible types for column `English`: character vs list. ✖ Incompatible types for column `中文 (Simplified)`: character vs list. ✖ Incompatible types for column `中文 (Traditional)`: character vs list. ✖ Incompatible types for column `فارسى`: character vs list. ✖ Incompatible types for column `Русский язык`: character vs list. ✖ Incompatible types for column `Tiếng Việt`: character vs list.
尝试2:do.call(rbind.data.frame, raw_json)
直接用rbind.data.frame合并嵌套列表,虽然列是向量类型,但列名被自动修改为R友好格式,无法保留原列名,对比再次报错:
df_json2 <- do.call(rbind.data.frame, raw_json) setdiff(df_csv, df_json2)
报错信息:
Error in `setdiff()`: ! `x` and `y` are not compatible. ✖ Cols in `y` but not `x`: `中文..Simplified.`, `中文..Traditional.`, `Русский.язык`, `Tiếng.Việt`. ✖ Cols in `x` but not `y`: `中文 (Simplified)`, `中文 (Traditional)`, `Русский язык`, `Tiếng Việt`.
我的需求是:创建与CSV生成的完全一致的数据框,无需预处理列名,实现准确对比。
解决方案
方法1:使用purrr::map_dfr逐个转换并合并
借助purrr包的map_dfr函数,将每个子列表转换为保留原列名的小数据框,再合并成完整数据框。此方法会自动处理列类型,且保留原始列名:
library(purrr) # 逐个转换子列表为数据框(保留原列名),再合并 df_json3 <- map_dfr(raw_json, ~data.frame(.x, check.names = FALSE)) # 验证一致性,返回空说明完全匹配 setdiff(df_csv, df_json3)
方法2:手动转换为矩阵再转数据框
如果不想依赖purrr包,可以先将嵌套列表转为矩阵,再转换为数据框并指定原列名:
# 获取原始列名 col_names <- names(raw_json[[1]]) # 将每个子列表转为向量,合并成矩阵 json_matrix <- do.call(rbind, lapply(raw_json, unlist)) # 转换为数据框,保留原列名 df_json4 <- data.frame(json_matrix, check.names = FALSE) # 验证一致性 setdiff(df_csv, df_json4)
为什么这两种方法可行?
- 方法1中,每个子列表先被转为
check.names=FALSE的数据框,保留了原始列名,map_dfr合并时会自动对齐列,且列类型为向量而非列表。 - 方法2中,
unlist将子列表转为字符向量,合并后的矩阵所有元素都是字符型,转数据框时指定check.names=FALSE即可保留原列名,列类型也与CSV读取的一致。
内容的提问来源于stack exchange,提问作者Sciolism Apparently
相关产品推荐
相关产品推荐

