1. CHNS膳食数据清洗的核心挑战
中国健康与营养调查(CHNS)作为国内持续时间最长的营养流行病学研究之一,其膳食数据具有典型的"双轨制"特征——既包含家庭食物称重记录,又涵盖个人24小时膳食回顾。我在处理2011年波次数据时,曾遇到一个典型案例:某家庭连续3天记录的食用油消耗量为150g,但成员个人回顾数据中脂肪摄入量却显著偏低。这种家庭与个人数据不匹配的现象,正是膳食调查数据清洗需要解决的首要问题。
膳食编码系统的代际差异是另一大痛点。CHNS项目跨越30余年,期间经历了1991、2002、2004三个版本的**食物成分表(FCT)**更迭。比如2004年波次数据中,马铃薯的编码可能是"010101"(FCT 1991标准)或"011101"(FCT 2004标准)。若不进行标准化处理,直接计算营养素会导致严重偏差。这里分享一个实用技巧:用R语言的stringr::str_pad()函数统一补全6位编码:
library(stringr) food_intake$foodcode_std <- str_pad(food_intake$FOODCODE, width = 6, side = "left", pad = "0")2. 个人膳食数据的精细处理
个人24小时回顾数据通常以SAS格式存储(如nutr3_00.sas7bdat),处理时要注意三个关键维度:
2.1 数据过滤与验证
- 剔除无效记录:删除食物重量为0或缺失的条目
- 验证个体唯一性:确保IDind与调查波次(WAVE)组合唯一
- 典型错误示例:2015年某研究发现,未过滤异常值会导致蛋白质摄入量被高估30%
2.2 营养素计算流程
- 匹配食物成分表:建议使用dplyr::left_join()合并数据
- 单位转换:特别注意脂肪酸等以百分比计量的成分
- 三日平均计算:需按个体ID和波次分组求均值
nutr3_clean <- nutr3_raw %>% filter(!is.na(FOODCODE), V39 > 0) %>% mutate(nutrient_energy = (ENERGY_kcal/100)*V39) %>% group_by(IDind, WAVE) %>% summarise(avg_energy = mean(nutrient_energy)/3)3. 家庭食物称重的转换艺术
家庭数据(nutr1_00.sas7bdat)的特殊性在于:
- 记录的是全家总消耗量
- 需要按人日数(person-days)分配到个体
- 典型场景:5口之家3天消耗大米2000g,实际参与就餐人日数为12,则人均日消耗量为166.67g
处理流程中的关键步骤:
- 异常值处理:将负值或极端大值替换为0
- 营养素计算:先计算家庭总摄入量,再按人日数分配
- 数据验证:检查家庭与个人数据逻辑一致性
home_nutrient <- df_home %>% mutate(jtcd = ifelse(jtcd < 0, 0, jtcd), fat_per100g = 脂肪酸Total * jtcd / 100) %>% group_by(hhid_WAVE) %>% summarise(total_fat = sum(fat_per100g)) %>% left_join(nutr2_persondays, by = "hhid_WAVE") %>% mutate(fat_per_person = total_fat * jun / 3)4. 数据合并的黄金法则
当个人与家庭数据需要合并时,建议采用"三层验证法":
- 编码一致性检查:确保食物编码系统完全匹配
- 营养素范围验证:设置合理阈值(如成人日能量摄入500-5000kcal)
- 逻辑关系校验:家庭数据营养素总量应≥个人数据总和
合并操作示例:
final_data <- personal_nutrient %>% full_join(home_nutrient, by = "IDind_WAVE") %>% mutate( energy_final = ifelse(is.na(d3kcal), avg_energy, d3kcal), flag = case_when( abs(avg_energy - d3kcal) > 1000 ~ "需复核", is.na(d3kcal) ~ "仅个人数据", TRUE ~ "数据一致" ) )实际项目中我发现,约15%的个案需要人工复核。建议输出核查清单:
write.xlsx( filter(final_data, flag == "需复核"), "需要复核的异常个案.xlsx" )5. 实战中的避坑指南
5.1 编码转换陷阱
- 2004版FCT中"粳米"编码为011101,而2002版为011001
- 解决方案:建立跨版本映射表
5.2 单位混淆问题
- 脂肪酸数据可能同时存在g/100g和%两种单位
- 处理建议:统一转换为毫克每百克(mg/100g)
5.3 缺失值处理策略
- 对于<5%的随机缺失,可采用同类食物均值填补
- 系统性缺失(如某类食物完全未检测)应保留NA并标注
我曾处理过一个包含2000个家庭的数据集,最初因忽略单位统一导致钙摄入量被低估40%。后来通过建立标准化的预处理流程,将数据清洗时间从2周缩短到3天。关键是把所有转换规则封装成函数:
clean_nutrient <- function(raw_data, fct_version) { # 单位标准化 if(fct_version == "2004") { raw_data <- mutate(raw_data, calcium_mg = 钙.Ca..mg. * 10) # 原数据为mg/100g } # 更多处理规则... return(raw_data) }