矢量化条件字符串操作

Vectorized conditional string manipulation

我正在尝试对我的数据中的日期列进行以下矢量化操作。我找到了一个非常不优雅的解决方案,并且确信有一个更干净整洁的解决方案。玩具示例:

index <- c(1,2)
input <- c('11-9-2019', '11/01/2019-01/31/2020')
output <- c('11-9-2019', '11-01-2019')

df_in <- data.frame('index'=index, 'data'=input)

df_out <- data.frame('index' =index, 'data'=output)

我可以使用 sapply 解决问题,如下所示:

df_out$data <- sapply(range(1:2), function(x) ifelse(str_length(df_in$data[x]) > 12, 
                                          str_sub(df_in$data[x], -10, -1), 
                                                  df_in$data[x]))
df_out$data <- str_replace_all(df_out$data, '/', '-')
df_out$data

有没有办法做到这一点 a) 使用一条矢量化线,b) 不像我在 str_sub 中所做的那样依赖字符串索引?

谢谢!

您可以使用 gsub:

 gsub("(\d{1,2})[/-](\d{1,2})[/-](\d{4}).*","\1-\2-\3",df_in$data)
 [1] "11-9-2019"  "11-01-2019"

如果您不熟悉正则表达式,请进行说明:

这会搜索一个字符串,该字符串包含一位或两位数字 ((\d{1,2})),后跟一个斜线或破折号 ([/-]),然后是一位或两位以上的数字,再次是破折号或一个斜杠,然后是四位数字。它只用破折号分隔的三组数字替换它们,并删除第一个字符串后面的任何内容。

一个想法是在删除任何多余的日期后使用 lubridate 中的 mdy(月日年),即

lubridate::mdy(ifelse(nchar(df_in$data > 10), substr(df_in$data, 1, 10), df_in$data))
#[1] "2019-11-09" "2019-11-01"

tidyverse 中的另一个选项是用 separate_rows 拆分元素,然后用 lubridate[=15= 转换为 Date class ]

library(lubridate)
library(dplyr)
library(tidyr)
df_in %>% 
   separate_rows(data, sep="-(?=[0-9]{2}[^0-9])") %>%
   group_by(index) %>%
   slice(1) %>% 
   transmute(data = lubridate::mdy(data)) %>%
   pull(data)
#[1] "2019-11-09" "2019-11-01"