数据清洗:变量清洗时编码处理与手动录入的选择准则咨询
在数据科学课程初期,我学到清洗变量时应避免手动录入修改数值,而采用编码方式。比如我遇到过格式混乱的年龄变量,取值包括3 yo、4 years、40 days、6yr、4-5 d、11month这类格式,手动逐行统一取值很有诱惑力。但用编码清洗的话,需要大量正则表达式代码,而且最终还是得做部分手动修改(如下方代码)。想请教有没有判断准则或建议,用来决定变量清洗时选编码还是手动录入?
dt[, onset_age := str_extract(onset_age, "(.*?)(?=y\/o)")] %>% # extract string before y/o .[onset_age %like% "\-", onset_age := str_extract(onset_age, "(.*?)(?=\-")] %>% # use first number if it's a range # impute by hand, express onset age in year .[onset_age == "6-month-old", onset_age := 0.5] %>% # impute by hand .[onset_age == "4 m/o", onset_age := 0.5] %>% # impute by hand .[onset_age == "40 d", onset_age := 40/365.25] %>% # impute by hand .[onset_age == "first attack: 2019/03/23", # impute by hand onset_age := interval(end = ymd("2019/03/12"), start = birth_date) %/% years(1)] # get modulo
以下是几个核心判断准则,帮你在编码和手动修改之间做选择:
1. 重复可能性:是否需要复用或处理增量数据
如果这份数据后续会有增量更新,或者你需要把清洗逻辑复用到其他类似数据集上,必须用编码。哪怕当前写正则麻烦,后续新增的5 mo、2 weeks这类格式,只要扩展正则就能批量处理,而手动修改每次都要重复劳动,还容易出错。
比如你例子里的6-month-old和4 m/o,其实可以写个正则匹配所有带月的格式,把月份转换成年份,不用单独手动赋值:
# 替换手动处理月的逻辑,用正则匹配数字+月相关关键词 dt[, onset_age := case_when( str_detect(onset_age, "\\d+.*(month|m/o|mo)") ~ as.numeric(str_extract(onset_age, "\\d+"))/12, str_detect(onset_age, "\\d+.*(day|d)") ~ as.numeric(str_extract(onset_age, "\\d+"))/365.25, # 其他规则... TRUE ~ onset_age )]
2. 数据规模:样本量大小决定效率
- 如果样本量小于50条,手动修改可能更快,毕竟写正则调试的时间可能比手动改完还久。但要注意:手动修改后必须做记录,把修改的条目和规则写在注释里,方便后续追溯。
- 如果样本量大于100条,哪怕格式混乱,也优先写编码。手动修改容易漏改、错改,而且没法验证一致性。
3. 异常值比例:极端特殊值的占比
如果需要手动处理的是占比低于5%的极端异常值(比如你例子里的first attack: 2019/03/23这种完全不符合格式的条目),可以手动处理,但要把这些异常值的处理逻辑用编码固化(比如你代码里的最后一行,其实已经是用编码结合手动指定条件来处理了,这是合理的)。
但如果异常值占比超过20%,说明你的正则逻辑覆盖不全,应该先优化正则,而不是逐个手动改——这时候数据的格式混乱是系统性的,必须用编码批量解决。
4. 可追溯性:是否需要审计或复现结果
如果你的分析需要可复现(比如学术研究、企业数据分析报告),必须用编码。手动修改的过程无法被记录,其他人没法验证你的清洗步骤,也没法复现结果。哪怕是手动处理的异常值,也要把处理规则写成代码(像你例子里那样,用.[]的条件赋值,而不是直接在Excel里改单元格),这样整个清洗流程是可追溯的。
总结建议
- 优先用编码处理有规律的格式混乱,通过正则或字符串匹配覆盖绝大多数情况;
- 仅对无规律的极端异常值(占比极低)用手动指定条件的编码方式处理(而非直接修改原始数据);
- 永远不要直接在原始数据里手动修改单元格,哪怕是小样本,也要用代码或带注释的记录来留存修改过程。
内容的提问来源于stack exchange,提问作者Ian Wang

