【问题标题】:Vectorized conditional string manipulation向量化条件字符串操作
【发布时间】:2020-05-28 04:57:13
【问题描述】:

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

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 中那样依赖字符串索引?

谢谢!

【问题讨论】:

  • @Sotos 同意,但效果不佳。
  • 您也应该通过查看lubridate 受益(请参阅下面的答案),尤其是如果您有多种格式

标签: r string dplyr substring sapply


【解决方案1】:

您可以使用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})),后跟一个斜杠或短划线 ([/-]),然后是一个或两个数字,再次是短划线或斜杠,然后是四个位数。它将这些替换为仅用破折号分隔的三组数字,并删除第一个字符串后面的所有内容。

【讨论】:

  • 太好了,谢谢!不熟悉正则表达式。现在将了解更多信息。
【解决方案2】:

tidyverse 中的另一个选项是使用separate_rows 拆分元素,然后使用lubridate 转换为Date

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"

【讨论】:

  • 我只是采用了这样的方法来解决不同的问题,真的很喜欢。 Tidyverse 赢得胜利。谢谢。
【解决方案3】:

一个想法是在删除任何多余的日期后使用 mdy(month day year) from lubridate,即

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-23
    • 2018-07-27
    • 2013-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多