【问题标题】:How to detect more than one regex in a case_when statement如何在 case_when 语句中检测多个正则表达式
【发布时间】:2018-12-19 12:49:01
【问题描述】:

我最近从 ifelse 转换为 case_whendplyr

瞄准

我希望能够使用case_when 从数据框中的语句中检测多个正则表达式,如下所示:

输入

statement<-data.frame(statement = c("I have performed APC and RFA",
 "An EMR was done","I didn't do anything"),stringsAsFactors=FALSE)

期望的输出

statement                            out

I have performed APC and RFA        APC,RFA
An EMR was done                     EMR
I didn't do anything                No Event

尝试

library(dplyr)
library(stringr)

      dataframe <- 
        dataframe %>% 
        mutate(
          EVENT = case_when(
            str_detect(statement,"EMR") ~ "EMR", 
            str_detect(statement, "HALO|RFA") ~ "RFA", 
            str_detect(statement, "APC") ~ "APC", 
             TRUE ~ "No Event"
          )
        )

问题

如果存在多个字符串,则每个语句仅提供一个输出,而不是多个输出。有没有办法检测多个字符串?

【问题讨论】:

    标签: r


    【解决方案1】:

    1) gsubfn::strapply strapply 可以一次完成提取和翻译。对于stmt 的每个组件,strapply 将匹配pat 模式,所有匹配项将使用 L 进行转换然后返回。 empty 参数定义了为 stat 的没有匹配项的组件返回的内容。这给出了一个匹配列表,每行一个列表组件,在其上应用toString 以将每个匹配转换为逗号分隔的字符串。这是此处介绍的 3 个备选方案中最短的一个。

    library(gsubfn)
    
    L <- list(APC = "APC", EMR = "EMR", HALO = "RFA", RFA = "RFA")
    pat <- paste(names(L), collapse = "|")
    transform(statement, 
      out = sapply(strapply(stmt, pat, L, empty = "No Event"), toString),
      stringsAsFactors = FALSE)
    

    给予:

                              stmt      out
    1 I have performed APC and RFA APC, RFA
    2              An EMR was done      EMR
    3         I didn't do anything No Event
    

    2) Base R 使用上面的Lpat,创建一个函数,该函数接受单词x 的字符向量并将pat 匹配的单词提取到@ 987654334@。如果g 具有非零长度,则使用L 转换其元素并使用toString 将其压缩为单个字符串;否则,返回No Event

    现在使用strsplitstmt 的每个元素拆分为单词,并将process 应用于每个这样的字符向量。

    process <- function(x) {
      g <- grep(pat, x, value = TRUE)
      if (length(g)) toString(L[g]) else "No Event"
    }
    transform(statement, out = sapply(strsplit(stmt, "\\s+"), process),
      stringsAsFactors = FALSE)
    

    3) dplyr/tidyr 使用 (1) 中的L 按行号和stmt 分组,并将单词分成单独的行。过滤掉names(L) 中的这些单词,并将所有行折叠到一个stmt 组中,通过L 进行翻译,并使用toString 生成逗号分隔的字符串。删除 n 列。在这一点上,我们得到了想要的结果,除了 No Event 行仍然丢失,所以右加入我们所拥有的 statement 并将 NAs 替换为 No Event

    library(dplyr)
    library(tidyr)
    
    statement %>%
      group_by(n = 1:n(), out = stmt) %>%
      separate_rows(out) %>%
      filter(out %in% names(L)) %>%
      summarize(stmt = stmt[1], out = toString(L[out])) %>%
      ungroup %>%
      select(-n) %>%
      right_join(statement, by = "stmt") %>%
      mutate(out = if_else(is.na(out), "No Event", out))
    

    给予:

    # A tibble: 3 x 2
      stmt                         out     
      <chr>                        <chr>   
    1 I have performed APC and RFA APC, RFA
    2 An EMR was done              EMR     
    3 I didn't do anything         No Event
    

    注意

    我们用这个作为输入:

    statement <- structure(list(stmt = c("I have performed APC and RFA", 
      "An EMR was done", "I didn't do anything")), 
      class = "data.frame", row.names = c(NA, -3L))
    

    更新

    重新阅读问题后已多次修改。还添加了更多选择。

    【讨论】:

    • 谢谢@G。格洛腾迪克。我有类似的情况,但略有不同。我用case_when 得到了一个奇怪的结果。我的case_when语句是这样的:mutate( EVENT = case_when( str_detect(statement,"30 multiple conditions") ~ "EMR", str_detect(statement, "30 multiple conditions") ~ "RFA", str_detect(statement, "30 multiple conditions") ~ "APC", TRUE ~ "statement" )这种情况下怎么办?
    • @Wialliam,您不能多次使用相同的条件。如果第一个为真,它将返回其右侧,如果为假,则所有这些都将为假,并将返回默认值。因此随后的重复条件永远不会匹配。建议您阅读?case_when 并查看该页面底部的示例以及尽可能多的 SO 示例。
    【解决方案2】:

    case_when 的逻辑是一旦满足一个条件就不会执行剩下的条件,所以你实际上不能从case_when 语句中得到两个输出。因此,如果您想使用case_when,建议从最不常见的条件开始,然后慢慢继续使其更普遍。 (因此,TRUE 是最后一个条件)

    如果您想坚持使用case_when,您可以添加一个附加条件并分别检查这两种情况并相应地给出输出。

    library(dplyr)
    
    statement %>% 
         mutate(
         EVENT = case_when(
               str_detect(statement, "APC") & str_detect(statement, "RFA") ~ "APC,RFA",
               str_detect(statement,"EMR") ~ "EMR", 
               str_detect(statement, "HALO|RFA") ~ "RFA", 
               str_detect(statement, "APC") ~ "APC", 
               TRUE ~ "No Event"
                )
               )
    
    
    
    #                     statement    EVENT
    #1 I have performed APC and RFA  APC,RFA
    #2              An EMR was done      EMR
    #3         I didn't do anything No Event
    #4                        FALSE No Event
    

    【讨论】:

    • 感谢@RonakShah。不幸的是,这并不是一个真正可扩展的答案,我不想创建 str_detect 的每个排列,如果它确实扩大了
    【解决方案3】:

    通过base R的一个想法是提取所有大写字母的单词,并粘贴大于1个字符的单词,即,

    sapply(regmatches(statement$statement, gregexpr('\\b[A-Z]+\\b', statement$statement)), 
                                              function(i) {
                                                          v1 <- i[nchar(i) > 1];
                                                          toString(v1)
                                                          })
    
    
    #[1] "APC, RFA" "EMR"      ""
    

    【讨论】:

      【解决方案4】:

      我不认为case_when 是最好的选择。我认为这在一定程度上取决于您拥有多少像"HALO|RFA" 这样的映射。如果有很多,可能值得花时间编写一个合适的函数。但是,如果只是这个,使用 dplyr 动词将管道放在一起可能会更快。

      我建议使用str_extract_allunnest 来获得包含相关动词的整洁数据框,然后使用str_replace_all 来解析映射。最后,我会使用unique 来确保我们没有来自替换的重复行。

      请注意,带有 APC、RFA 的第一列将一分为二。我意识到这不是您所要求的,但它会使 tidyverse 中的后续处理变得更加容易。有关整洁数据约定的更多信息,请参阅此链接:https://tidyr.tidyverse.org/articles/tidy-data.html

      在当前的实现中,unnest 将删除没有匹配模式的最后一行。如果您想要 NA,则可以使用原始数据执行 full join。另见https://github.com/tidyverse/tidyr/issues/358

      statement<-data.frame(statement = c("I have performed APC and RFA","An EMR was done","I didn't do anything"),stringsAsFactors=FALSE)
      library(tidyverse)
      statement %>% mutate(
        out = statement %>% 
          str_extract_all("((EMR)|(HALO)|(RFA)|(APC))")
        ) %>%  unnest(.drop =FALSE) %>% 
        mutate(
          out = out %>% str_replace_all("HALO", "RFA")
        ) %>% 
        unique() %>% 
        full_join(statement)
      

      输出将是

                       statement    out
      I have performed APC and RFA  APC
      I have performed APC and RFA  RFA
      An EMR was done               EMR
      I didn't do anything          <NA>
      

      【讨论】:

      • 输出只给了我一个结果。例如第一个文本字符串只输出 APC
      猜你喜欢
      • 1970-01-01
      • 2021-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-11
      • 1970-01-01
      • 1970-01-01
      • 2012-01-10
      相关资源
      最近更新 更多