为何case_when()与if_else()处理日期时出现异常及警告?
处理Excel导出的混乱日期数据时,常遇到两种格式:1900年前的日期以字符串形式存储(如"0982-12-27"),1900年后的日期以Excel数字格式存储(如32874)。用read_excel()读取为字符型后,使用case_when()或if_else()转换为统一日期格式时,尽管结果符合预期,但会触发警告,以下是原因分析和解决方案。
数据示例
library(janitor) library(tidyverse) df <- tibble(date=c("0982-12-27", "0996-01-01", "1201-02-10", 32874, 36526))
尝试的两种方法及警告
方法1:case_when()实现
# 生成预期输出但提示日期解析失败 df <- df %>% mutate(date_new1=case_when(grepl("-", date) ~ ymd(date), .default=excel_numeric_to_date(as.numeric(date))))
警告:2个日期解析失败
方法2:if_else()实现
# 生成预期输出但提示早于1900年的日期被强制转换为NA df <- df %>% mutate(date_new2=if_else(grepl("-", date), true=date, false=as.character(excel_numeric_to_date(as.numeric(date))))) %>% mutate(date_new2=ymd(date_new2))
警告:强制转换引入NA值
转换后结果:
df #> date date_new1 date_new2 #> <chr> <date> <date> #> 1 0982-12-27 0982-12-27 0982-12-27 #> 2 0996-01-01 0996-01-01 0996-01-01 #> 3 1201-02-10 1201-02-10 1201-02-10 #> 4 32874 1990-01-01 1990-01-01 #> 5 36526 2000-01-01 2000-01-01
警告原因解析
case_when()的警告case_when()会对所有分支的表达式全量计算,不会根据条件跳过未匹配的分支。例如,即使某行满足grepl("-", date),代码仍会执行excel_numeric_to_date(as.numeric(date))。对于带"-"的日期字符串,as.numeric(date)会返回NA,传入excel_numeric_to_date()后就会触发“日期解析失败”的警告——这些NA最终不会被case_when()选中,但计算过程已产生警告。if_else()的警告
警告来自后续的ymd(date_new2)执行过程。ymd()函数在解析时会先尝试将所有输入转换为POSIXct类型(基于1970年的时间戳),而早于1900年的日期无法被POSIXct表示,因此触发“强制转换为NA”的警告。不过最终ymd()会返回支持更早日期的Date类型,所以结果是正确的。
消除警告的解决方案
通过逐行处理避免不必要的全量计算,就能消除警告:
方案1:rowwise() + case_when()
df <- df %>% rowwise() %>% mutate(date_clean = case_when( grepl("-", date) ~ ymd(date), TRUE ~ excel_numeric_to_date(as.numeric(date)) )) %>% ungroup()
rowwise()让代码逐行执行,仅对符合条件的行计算对应分支的表达式,避免无效计算产生警告。
方案2:purrr::map()逐行处理
df <- df %>% mutate(date_clean = map_chr(date, ~{ if (grepl("-", .x)) { .x } else { as.character(excel_numeric_to_date(as.numeric(.x))) } }) %>% ymd())
用map_chr()遍历每个日期值,针对性执行转换逻辑,再统一用ymd()解析为日期类型。
内容的提问来源于stack exchange,提问作者Zach Bridgewater

