在R中的字符串后提取日期

Extract date after string in R

我正在尝试使用 tidyr 的提取功能从 Notes 列中提取日期。我正在处理的数据如下所示:

dates <- data.frame(col1 = c("customer", "customer2", "customer3"),
                    Notes = c("DOB: 12/10/62
START: 09/01/2019
END: 09/01/2020", "
S/DATE: 28/08/19
R/DATE: 27/08/20", "DOB: 13/01/1980
Start:04/12/2018"),
                    End_date = NA,
                    Start_Date = NA )

我试过像这样在字符串“S/DATE”之后提取日期:

extract <- extract(
  dates,
  col = "Notes",
  into = "Start_date",
  regex = "(?<=(S\/DATE:)).*"  # Using regex lookahead
)

但是,这只会提取字符串“S/DATE:”,而不是其后的日期。当我在 regex101.com 上尝试这个时,它按预期工作。

谢谢。易卜拉欣

您可以在此处使用 sub 作为基础 R 选项:

s_date <- ifelse(grepl("S/DATE", dates$Notes),
                 sub("^.*\bS/DATE: (\S+).*$", "\1", dates$Notes), NA)
s_date

[1] NA         "28/08/19" NA

请注意,此处需要调用上面的 grepl,因为默认情况下 sub 将 return 整个输入字符串(在本例中为完整的 Notes)如果 S/DATE 而不是 在文本中找到。

一种方法也可以像这样。 (假设您需要 S/DATESTART 之一,因为您预期的新列名称是 Start_date)。但是,如果不需要所有这些值,您可以轻松修改此语法。

解释-

  • 最里面的 expr Notes 列已被这些分隔符 :\n.
  • 拆分为列表
  • 在此列表中,空格被删除
  • 在修改后的列表中,StartS/Date 旁边的项目使用 sapply 提取,这将列表简化为向量(如果可能)
  • 最后lubridate::dmy用在最外层表达式
sapply(strsplit(dates$Notes, 
                 "[: | \n]"), 
       function(x) subset(x, x != "")[1 + which(toupper(subset(x, x != "")) %in% c("S/DATE", "START"))])

[1] "09/01/2019" "28/08/19"   "04/12/2018"

如果您将以上内容包含在 lubridate::dmy 中,日期的格式也会正确

dmy(sapply(strsplit(dates$Notes, 
                        "[: | \n]"), 
               function(x) subset(x, x != "")[1 + which(toupper(subset(x, x != "")) %in% c("S/DATE", "START"))]))

[1] "2019-01-09" "2019-08-28" "2018-12-04"

此外,这可以传递到 dplyr 管道中,以便同时在您的 dates

中创建一个新列
dates %>% mutate(Start_Date = dmy(sapply(strsplit(Notes, 
                                                  "[: | \n]"), 
                                         function(x) subset(x, x != "")[1 + which(toupper(subset(x, x != "")) %in% c("S/DATE", "START"))])))

       col1                                             Notes End_date Start_Date
1  customer DOB: 12/10/62\nSTART: 09/01/2019\nEND: 09/01/2020       NA 2019-01-09
2 customer2              \nS/DATE: 28/08/19\nR/DATE: 27/08/20       NA 2019-08-28
3 customer3                 DOB: 13/01/1980\nStart:04/12/2018       NA 2018-12-04

另一种方法是拆分文本并处理更小的块。

一步一步的插图,一行数据

# Split the text on newlines, yielding dates with labels
dates$Notes %>% head(1) %>% strsplit("\n")

[[1]]
[1] "DOB: 12/10/62"     "START: 09/01/2019" "END: 09/01/2020"  

深入到下一个层次

# Split each name/value pair on colons
dates$Notes %>% head(1) %>% strsplit("\n") %>% 
    unlist() %>% strsplit(":\s*")

[[1]]
[1] "DOB"      "12/10/62"

[[2]]
[1] "START"      "09/01/2019"

[[3]]
[1] "END"        "09/01/2020"

提取单个值

# extract a vector of name labels
dates$Notes %>% head(1) %>% strsplit("\n") %>% 
    unlist() %>% strsplit(":\s*") %>%
    sapply(function(x) x[1])

[1] "DOB"   "START" "END" 


# extract a vector of associated values 
dates$Notes %>% head(1) %>% strsplit("\n") %>% 
    unlist() %>% strsplit(":\s*") %>%
    sapply(function(x) x[2])

[1] "12/10/62"   "09/01/2019" "09/01/2020"

通过一些巧妙的dplyr用法,您将得到一个数据框

dates %>%
    group_by(col1) %>%
    # summarize can collapse many rows into one or expand one into many
    summarize(
        name = Notes %>% strsplit("\n") %>%
            unlist() %>% strsplit(":\s*") %>% 
            sapply(function(x) x[1]),
        value = Notes %>% strsplit("\n") %>% 
            unlist() %>% strsplit(":\s*") %>% 
            sapply(function(x) x[2])
    ) %>%
    ungroup()
   

结果,所有值都已分离并准备好进行进一步处理

# A tibble: 8 x 3
  col1      name   value     
  <chr>     <chr>  <chr>     
1 customer  DOB    12/10/62  
2 customer  START  09/01/2019
3 customer  END    09/01/2020
4 customer2 NA     NA        
5 customer2 S/DATE 28/08/19  
6 customer2 R/DATE 27/08/20  
7 customer3 DOB    13/01/1980
8 customer3 Start  04/12/2018

我会合并 stringrlubridate:

dates %>% 
  mutate(
    Start_Date = 
      sub("\ns/date:", "\nstart:", tolower(Notes)) %>% 
      str_remove_all("(.*\nstart:)|(\n.*)") %>% 
      trimws() %>% 
      lubridate::dmy()
  )

#        col1                                             Notes End_date Start_Date
# 1  customer DOB: 12/10/62\nSTART: 09/01/2019\nEND: 09/01/2020       NA 2019-01-09
# 2 customer2              \nS/DATE: 28/08/19\nR/DATE: 27/08/20       NA 2019-08-28
# 3 customer3                 DOB: 13/01/1980\nStart:04/12/2018       NA 2018-12-04

答案没有那么简洁,但我觉得很直观,也很容易遵循这些步骤。

首先,我将一个 start 模式替换为另一个 (sub),其中我使用 tolower 来制作所有小写字母。然后我删除开始日期之前的所有内容,以及行更改 str_remove_all 之后的所有内容。最后我trim空格(trimws)转成日期(lubridate::dmy).