矢量化条件字符串操作
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"
我正在尝试对我的数据中的日期列进行以下矢量化操作。我找到了一个非常不优雅的解决方案,并且确信有一个更干净整洁的解决方案。玩具示例:
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"