R语言数据清洗实战从survival包lung数据集掌握缺失值与变量处理技巧在数据分析的完整流程中数据清洗往往占据了70%以上的时间成本。对于初学者而言跳过这一环节直接建模就像在沙滩上盖高楼——再精美的算法也会因为基础不牢而崩塌。今天我们就以survival包中的经典lung数据集为案例手把手带你破解数据清洗中的两大难题缺失值处理和变量类型转换。1. 认识我们的数据战场lung数据集全景解析在开始任何数据操作前充分理解数据集的结构和背景至关重要。加载survival包后我们可以通过几条简单命令快速把握数据全貌# 加载必要包 library(survival) # 查看数据结构 str(lung) # 查看前6行 head(lung)执行后会看到这个包含228个观察值和10个变量的临床数据集。每个变量都承载着特定医学意义变量名类型描述取值范围instnumeric医疗机构代码1-33timenumeric生存天数5-1022statusnumeric生存状态(1删失,2死亡)1-2agenumeric患者年龄(岁)39-82sexnumeric性别(1男,2女)1-2ph.ecognumericECOG体能评分0-3ph.karnonumeric医生评估的Karnofsky评分50-100pat.karnonumeric患者自评的Karnofsky评分30-100meal.calnumeric每日膳食热量摄入(卡路里)96-2600wt.lossnumeric最近6个月体重减轻(磅)-24-68注意数据中的负体重变化(-24)实际表示体重增加这是医学记录中的常见表示方法2. 缺失值检测与处理的系统性解决方案缺失值是现实数据中的常态而非例外。在lung数据集中ph.karno、pat.karno和meal.cal等变量都存在不同程度的缺失。传统的complete.cases()直接删除法会损失大量信息我们需要更精细的处理策略。2.1 三维度缺失模式诊断首先通过可视化快速定位问题# 安装并加载可视化包 install.packages(naniar) library(naniar) # 绘制缺失值热图 gg_miss_upset(lung)这幅图会揭示变量间的缺失关联模式。比如我们可能发现ph.karno和pat.karno往往同时缺失而meal.cal的缺失则相对独立。2.2 智能填补的四种武器根据缺失机制和变量特性选择最适合的填补方法中位数/众数填补- 适用于偏态分布的连续变量lung$ph.karno - ifelse(is.na(lung$ph.karno), median(lung$ph.karno, na.rm TRUE), lung$ph.karno)k-最近邻(kNN)填补- 利用变量间相关性install.packages(VIM) library(VIM) lung_imputed - kNN(lung, variable c(ph.karno, pat.karno))多重插补(mice)- 黄金标准方法install.packages(mice) library(mice) imp - mice(lung, m 5, maxit 10) lung_complete - complete(imp)模型预测填补- 对重要变量使用model - lm(ph.karno ~ age sex ph.ecog, data lung) lung$ph.karno[is.na(lung$ph.karno)] - predict(model, newdata lung[is.na(lung$ph.karno), ])专业提示对生存分析数据时间相关变量的缺失最好保留为单独类别而非简单填补3. 变量类型转换的艺术与科学原始数据中的变量类型往往不适合直接分析。我们需要根据变量特性和分析目的进行精准转换。3.1 分类变量的进阶处理将数值型编码的分类变量(如sex)转换为因子是基础操作lung$sex - factor(lung$sex, levels c(1, 2), labels c(male, female))但对有序分类变量(如ph.ecog)需要特别处理lung$ph.ecog - ordered(lung$ph.ecog, levels 0:3, labels c(asymptomatic, symptomatic_ambulatory, in_bed_50%, in_bed_50%))3.2 连续变量的智能分箱年龄这样的连续变量有时需要转换为分类变量library(dplyr) lung - lung %% mutate(age_group case_when( age 50 ~ 50, age 50 age 65 ~ 50-65, age 65 ~ 65 ))3.3 日期时间格式转换虽然本例没有日期变量但掌握时间转换很有必要# 假设有日期字符串 date_str - c(01/15/2020, 02/20/2021) as.Date(date_str, format %m/%d/%Y) # 将生存天数转换为日期 baseline_date - as.Date(2020-01-01) lung$end_date - baseline_date lung$time4. 数据清洗质量控制的四道防线完成清洗后必须系统验证数据质量。我推荐分层检查法完整性检查sapply(lung, function(x) sum(is.na(x)))一致性检查# 检查体重变化合理性 filter(lung, wt.loss 100 | wt.loss -30) # 检查年龄与评分的逻辑关系 ggplot(lung, aes(xage, yph.karno)) geom_point() geom_smooth()分布检查# 绘制所有数值变量的分布 library(ggplot2) lung %% select(where(is.numeric)) %% pivot_longer(everything()) %% ggplot(aes(value)) geom_histogram() facet_wrap(~name, scales free)业务逻辑检查# 确保没有生存时间为负值 filter(lung, time 0) # 检查分类变量水平 levels(lung$sex)最后将清洗后的数据保存为分析专用版本saveRDS(lung, lung_cleaned.rds)在实际项目中我习惯创建一个数据清洗日志记录每个处理步骤的决策依据。当三个月后回顾分析或交接工作时这个习惯能节省大量重新理解数据的时间。数据清洗没有唯一正确答案关键是建立系统化的思考框架并根据具体分析目标做出合理妥协。