R语言实战:用dplyr和tidyr搞定中国家庭营养调查(CHNS)膳食数据清洗(附完整代码)
R语言实战用dplyr和tidyr搞定中国家庭营养调查CHNS膳食数据清洗附完整代码在公共卫生和营养学研究领域中国家庭营养调查CHNS作为一项重要的纵向研究为理解我国居民膳食结构和营养状况提供了宝贵数据。然而这类复杂调查数据的处理往往让研究人员头疼——食物编码不统一、个人与家庭数据需要合并、营养素计算涉及多重转换等问题层出不穷。本文将手把手教你使用R语言中的dplyr和tidyr包系统解决这些技术难题。1. 数据准备与环境搭建在开始正式的数据清洗前我们需要做好充分的准备工作。首先确保已安装必要的R包install.packages(c(dplyr, tidyr, haven, stringr, openxlsx))加载这些包后我们可以开始导入原始数据。CHNS膳食数据通常以SAS格式(.sas7bdat)提供library(haven) library(dplyr) # 导入个人膳食摄入数据 nutr3_00 - read_sas(nutr3_00.sas7bdat) # 导入家庭膳食数据 nutr1 - read_sas(nutr1_00.sas7bdat)关键检查点确认数据维度dim(nutr3_00)检查变量名称names(nutr3_00)验证时间跨度table(nutr3_00$WAVE)注意CHNS数据在不同年份可能使用不同的食物编码系统这是后续处理需要特别注意的地方。2. 食物编码标准化处理食物编码的标准化是膳食数据分析的基础。CHNS数据中常见的编码问题包括编码位数不一致如123 vs 00123不同年份使用不同版本的食物成分表特殊字符如-的存在使用stringr包处理编码标准化library(stringr) # 补全6位编码左侧补零 nutr3_00 - nutr3_00 %% mutate(FOODCODE as.character(FOODCODE), foodcode_std str_pad(FOODCODE, width6, sideleft, pad0)) # 去除食物成分表编码中的特殊字符 FCT - openxlsx::read.xlsx(FCT_2002_2004_合并的脂肪酸数据-4.xlsx) %% mutate(FCT_code gsub(-, , 编码.Code))编码匹配的常见问题及解决方案问题类型解决方案代码示例编码位数不足左侧补零str_pad(FOODCODE, 6, left, 0)特殊字符干扰去除特殊符号gsub(-, , code)编码系统变更建立映射表left_join(data, mapping_table)3. 个人膳食数据处理实战个人膳食数据的核心是计算每人每日的营养素摄入量。我们需要将食物摄入量(g)与食物成分表匹配计算每次调查(WAVE)的三天平均摄入处理重复测量的个体数据# 匹配食物成分数据 nutr3_matched - nutr3_00 %% left_join(FCT, byc(foodcode_stdFCT_code)) %% filter(!is.na(编码.Code)) # 移除未匹配的记录 # 计算三天平均摄入 nutr3_avg - nutr3_matched %% group_by(IDind, WAVE) %% summarise( energy_kcal sum(V39 * 能量.kcal. / 100 / 3, na.rmTRUE), protein_g sum(V39 * 蛋白质.g. / 100 / 3, na.rmTRUE), fat_g sum(V39 * 脂肪.g. / 100 / 3, na.rmTRUE) ) %% ungroup()处理过程中的常见坑单位转换注意食物成分表中的含量通常是每100g需要转换缺失值使用na.rmTRUE但要记录缺失情况极端值设置合理范围过滤异常值4. 家庭与个人数据合并策略家庭膳食数据需要按家庭成员人数进行分配这是CHNS数据分析的关键难点# 计算家庭成员每日消费比例 nutr2 - read_sas(nutr2_00.sas7bdat) %% select(IDind, HHID, WAVE, personDaysV35) %% group_by(HHID, WAVE) %% mutate(prop personDays / sum(personDays)) %% ungroup() # 合并家庭与个人数据 final_data - nutr3_avg %% left_join(nutr2, byc(IDind, WAVE)) %% mutate( hhid_wave paste(HHID, WAVE, sep_), id_wave paste(IDind, WAVE, sep_) )家庭数据分配的注意事项确保家庭ID和个人ID的对应关系准确检查分配比例是否合理0-1之间处理家庭成员变动情况不同wave可能有变化5. 数据质量控制与验证完成数据处理后必须进行严格的质量控制# 营养素摄入量合理性检查 summary_stats - final_data %% summarise( energy_mean mean(energy_kcal, na.rmTRUE), energy_sd sd(energy_kcal, na.rmTRUE), protein_range quantile(protein_g, c(0.01, 0.99), na.rmTRUE) ) # 识别极端值 outliers - final_data %% filter(energy_kcal 500 | energy_kcal 5000) %% select(IDind, WAVE, energy_kcal)推荐的质量控制步骤范围检查各营养素摄入量是否在生理合理范围内逻辑检查能量与宏量营养素的比例是否合理一致性检查同一个体不同wave间的变化是否合理缺失分析检查缺失数据的模式和原因6. 高效数据处理技巧针对大规模CHNS数据推荐以下效率优化技巧内存管理# 及时移除不再需要的对象 rm(list c(nutr3_00, nutr1)) gc() # 手动触发垃圾回收 # 使用data.table处理大数据 library(data.table) large_data - as.data.table(large_data)并行处理library(furrr) plan(multisession) # 设置并行后端 # 并行处理各wave数据 results - future_map(unique(data$WAVE), function(w) { data %% filter(WAVE w) %% process_wave() })数据保存优化# 使用RDS保存中间结果 saveRDS(final_data, CHNS_processed_final.rds) # 对于需要与他人共享的数据 write.csv(final_data, CHNS_final.csv, row.namesFALSE)7. 完整工作流示例将上述步骤整合为可复现的工作流library(tidyverse) library(haven) # 1. 数据导入 nutr3 - read_sas(nutr3_00.sas7bdat) fct - readxl::read_excel(FCT.xlsx) # 2. 编码标准化 nutr3_clean - nutr3 %% mutate( FOODCODE str_pad(as.character(FOODCODE), 6, left, 0), ID_WAVE paste(IDind, WAVE, sep_) ) # 3. 匹配食物成分 nutr3_matched - nutr3_clean %% left_join( fct %% mutate(FOODCODE gsub(-, , 编码.Code)), by c(FOODCODE) ) %% filter(!is.na(能量.kcal.)) # 4. 计算营养素摄入 nutrient_intake - nutr3_matched %% group_by(ID_WAVE) %% summarise( energy sum(V39 * 能量.kcal. / 100 / 3), protein sum(V39 * 蛋白质.g. / 100 / 3), fat sum(V39 * 脂肪.g. / 100 / 3) ) # 5. 保存结果 saveRDS(nutrient_intake, CHNS_nutrient_intake.rds)实际项目中遇到的典型问题解决方案当遇到编码不匹配时建立手工映射表对于缺失的重量数据(V39)使用同食物代码的中位数替代处理特殊食物如调味品时需调整计算方法