You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用case_when与正则条件时出现意外警告,疑似多案例匹配

问题:case_when处理混合时间格式时出现未预期警告但结果正确

问题背景

处理包含混合时间格式的数据集,格式涵盖hh:mm类时间字符串与Excel时间序列号(小于1的小数)。将所有数据转为字符串后,通过case_when结合stringr、readr识别不同格式并解析,最终输出结果符合预期,但执行过程中出现了NA强制转换和时间解析失败的警告,这些警告对应的观测值本应被case_when的条件排除,因此对case_when的执行逻辑存在疑问。

示例数据

dummy <- tibble(x = c("13:15:21", "02:03:17+01:00", "12:03", "0.1234"))

正则规则与函数实现

使用的正则匹配规则:

  • ^0\.:识别小于1的小数形式Excel时间序列号
  • \+:识别带夏令时时区标识的时间字符串
  • .+(?=\+):提取时区标识前的时间部分用于解析
  • ::匹配通用时间格式,作为兜底规则放在最后

处理函数代码:

convert_times <- function(x){
  case_when(str_detect(x, "^0\\.")        ~ convert_excel_time(x), 
            str_detect(x, "\\+")          ~ parse_time(str_extract(x, ".+(?=\\+)")), 
            str_detect(x, ":")            ~ parse_time(x),
            .default = NA)
}

convert_excel_time <- function(x){
  as.numeric(x) * 24 * 60 * 60 %>%
  as_datetime() %>% 
  hms::as_hms()
}

执行结果与警告

预期输出

> dummy %>% 
+   mutate(new = convert_times(x))
# A tibble: 4 × 2
  x              new        
  <chr>          <time>     
1 13:15:21       13:15:21.00
2 02:03:17+01:00 02:03:17.00
3 12:03          12:03:00.00
4 0.1234         02:57:41.76

警告信息

[[1]]
<warning/rlang_warning>
Warning in `mutate()`:
ℹ In argument: `new = convert_times(x)`.
Caused by warning in `convert_excel_time()`:
! NAs introduced by coercion
---
Backtrace:
    ▆
 1. ├─dummy %>% mutate(new = convert_times(x))
 2. ├─dplyr::mutate(., new = convert_times(x))
 3. └─dplyr:::mutate.data.frame(., new = convert_times(x))

[[2]]
<warning/rlang_warning>
Warning in `mutate()`:
ℹ In argument: `new = convert_times(x)`.
Caused by warning:
! 2 parsing failures.
row col   expected         actual
  2  -- time like  02:03:17+01:00
  4  -- time like  0.1234        
---
Backtrace:
    ▆
 1. ├─dummy %>% mutate(new = convert_times(x))
 2. ├─dplyr::mutate(., new = convert_times(x))
 3. └─dplyr:::mutate.data.frame(., new = convert_times(x))

原因分析

case_when是向量式执行的函数:它会先计算所有右侧分支的表达式(即对整个向量执行所有分支的函数),再根据左侧的条件选择对应位置的结果,而不是逐行判断条件后再执行对应分支的函数。

这就导致:

  1. 所有元素都会执行convert_excel_time(x),非Excel格式的字符串转为numeric时会产生NA强制转换警告;
  2. 所有元素都会执行parse_time(str_extract(...))和parse_time(x),第2行带时区的字符串直接用parse_time解析会失败,第4行的Excel序列号无法被parse_time识别,因此产生解析失败警告。

这些警告对应的计算结果最终会被case_when的条件过滤掉,所以最终输出是正确的,但执行过程中不必要的函数调用会触发警告。

解决方案

方案1:用purrr::map逐元素处理

通过map遍历每个元素,在单个元素内部执行case_when,这样每个元素只会执行符合条件的分支函数,避免不必要的计算:

library(purrr)
library(dplyr)
library(readr)
library(hms)

convert_times <- function(x){
  map(x, ~{
    case_when(
      str_detect(.x, "^0\\.") ~ convert_excel_time(.x),
      str_detect(.x, "\\+") ~ parse_time(str_extract(.x, ".+(?=\\+)")),
      str_detect(.x, ":") ~ parse_time(.x),
      .default = NA
    )
  }) %>% 
    list_c() # 将列表转换为向量
}

convert_excel_time <- function(x){
  as.numeric(x) * 24 * 60 * 60 %>%
    as_datetime() %>% 
    hms::as_hms()
}

方案2:在分支函数内部添加条件过滤

在每个分支的函数中,只处理符合对应条件的元素,其余返回NA,避免对不匹配的元素执行无效计算:

library(dplyr)
library(readr)
library(hms)
library(stringr)

convert_excel_time <- function(x){
  if_else(str_detect(x, "^0\\."), 
          as.numeric(x) * 24 * 60 * 60 %>% as_datetime() %>% hms::as_hms(),
          NA)
}

convert_times <- function(x){
  case_when(
    str_detect(x, "^0\\.") ~ convert_excel_time(x), 
    str_detect(x, "\\+") ~ {
      idx <- str_detect(x, "\\+")
      res <- rep(NA, length(x))
      res[idx] <- parse_time(str_extract(x[idx], ".+(?=\\+)"))
      res
    }, 
    str_detect(x, ":") ~ {
      idx <- str_detect(x, ":") & !str_detect(x, "\\+")
      res <- rep(NA, length(x))
      res[idx] <- parse_time(x[idx])
      res
    },
    .default = NA
  )
}

两种方案都能消除不必要的函数调用,从而解决警告问题,同时保持最终输出结果正确。

内容的提问来源于stack exchange,提问作者j.hmg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 11:04:54