使用sub函数更新DataFrame时R语言数学减法计算异常排查
解决缺失年龄计算的R脚本问题
问题背景
我有一个包含Age、Sex、MoB、YoB、MoI、YoI六列的患者数据DataFrame,其中后缀“B”代表生日(月/年),“I”代表疾病发病日期(月/年)。部分行Age值缺失,我想通过YoI与YoB的差值结合月份判断来计算这些行的Age,但编写的R脚本(使用dplyr)出现多个异常:
- 使用
age_calculated <- YoI - YoB时,2006-1940得到68而非正确的66; - 使用
age_calculated <- YoI - YoB - 2能得到正确值,但不知道原因; - 使用目标公式
age_calculated <- YoI + IafterB - YoB时,辅助列IafterB被忽略; - 测试
age_calculated <- IafterB时,所有缺失Age的行都被设为0,证明IafterB未生效。
核心错误分析
sub()函数误用:你用sub("-1", calculateAge(...), dataframe_csv$Age)替换缺失值,但sub()是字符串替换工具,它会把calculateAge()返回的向量转换成单个字符串,然后统一替换所有"-1",根本做不到逐行对应计算。这就是为什么测试IafterB时全变成0——sub只会用向量的第一个元素去替换所有匹配项。IafterB逻辑颠倒:你的
case_when逻辑搞反了。正确逻辑是:- 如果发病月份晚于出生月份(MoI > MoB):当年生日已过,年龄=YoI-YoB;
- 如果发病月份早于或等于出生月份(MoI ≤ MoB):生日还没过,年龄=YoI-YoB-1。
你现在的判断条件刚好反过来,导致调整项完全错误。
数据类型混乱:读取CSV时,Age列因为混合了空值和数字被自动转成字符型,后续设为
-1后还是字符型,计算时容易出现隐式转换错误。
修正后的完整脚本
# 初始化环境 library(dplyr) message("\n开始修复缺失年龄数据\n") # 定义文件路径(用file.path更稳妥,避免路径拼接错误) base_path <- dirname(sys.frame(1)$ofile) dataset_file_in <- file.path(base_path, "dataset_missing_age.csv") dataset_file_out <- file.path(base_path, "dataset_fixed.csv") # 打印路径信息 message(paste("基础路径 :", base_path)) message(paste("输入数据集 :", dataset_file_in)) message(paste("输出数据集 :", dataset_file_out)) # 读取数据,指定字符串不转因子 dataframe_csv <- read.csv(dataset_file_in, sep=";", stringsAsFactors=FALSE) message(paste("\n数据集行数 :", nrow(dataframe_csv))) message(paste("数据集列数 :", length(dataframe_csv))) # 修复缺失Age,全程用dplyr管道风格 dataframe_csv <- dataframe_csv %>% # 转换月份、年份为数值型,避免字符比较问题 mutate( MoB = as.numeric(MoB), MoI = as.numeric(MoI), YoB = as.numeric(YoB), YoI = as.numeric(YoI) ) %>% # 计算年龄调整项:发病在生日后则不减,生日前则减1 mutate(age_adjust = if_else(MoI > MoB, 0, -1)) %>% # 仅替换Age缺失的行,保留原有非缺失值 mutate( Age = case_when( is.na(Age) ~ as.character(YoI - YoB + age_adjust), TRUE ~ Age ) ) %>% # 移除临时辅助列 select(-age_adjust) # 打印修复后的结果 message("\n修复后的完整数据集:\n") print(dataframe_csv) # 保存结果到CSV write.csv2(dataframe_csv, dataset_file_out, row.names = FALSE, quote=FALSE) message("\n修复完成,结果已保存至指定路径")
测试验证
用你提供的测试数据,修复后缺失Age的行结果完全符合预期:
- 第4行(MoB=3, YoB=1940; MoI=8, YoI=2006):2006-1940+0=66 ✔️
- 第5行(MoB=4, YoB=1940; MoI=9, YoI=2006):2006-1940+0=66 ✔️
- 第6行(MoB=6, YoB=1940; MoI=10, YoI=2006):2006-1940+0=66 ✔️
- 第7行(MoB=8, YoB=1940; MoI=6, YoI=2006):2006-1940-1=65 ✔️
- 第8行(MoB=10, YoB=1940; MoI=2, YoI=2006):2006-1940-1=65 ✔️
- 第9行(MoB=11, YoB=1940; MoI=2, YoI=2006):2006-1940-1=65 ✔️
- 第10行(MoB=12, YoB=1940; MoI=2, YoI=2006):2006-1940-1=65 ✔️
内容的提问来源于stack exchange,提问作者serkelion
相关产品推荐
相关产品推荐

