【问题标题】:Extract date after string in R在R中的字符串之后提取日期
【发布时间】:2021-07-08 04:02:30
【问题描述】:

我正在尝试使用 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 上尝试此操作时,它按预期工作。

谢谢。易卜拉欣

【问题讨论】:

    标签: r regex tidyr


    【解决方案1】:

    您可以在此处使用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,因为如果S/DATE不是 在文中找到。

    【讨论】:

      【解决方案2】:

      一种方法也可以是这样的。 (假设您需要 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
      

      【讨论】:

        【解决方案3】:

        我会结合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-pattern 替换为另一个 (sub),其中我使用 tolower 来制作所有小写字母。然后我删除开始日期之前的所有内容,以及更改str_remove_all 之后的所有内容。最后我修剪空白 (trimws) 并变成日期 (lubridate::dmy)。

        【讨论】:

          【解决方案4】:

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

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

          # 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
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-06-28
            • 2011-02-17
            • 1970-01-01
            相关资源
            最近更新 更多