如何在case_when语句中检测多个正则表达式

时间:2018-12-19 12:49:02

标签: r

我最近已从case_when从ifelse转换为dplyr

目标

我希望能够使用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"
          )
        )

问题

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

4 个答案:

答案 0 :(得分:1)

1)gsubfn :: strapply @Override public List<RowType> getRows() { return Collections.unmodifiableList(implList); } 可以一次完成提取和翻译。对于strapply的每个组成部分,strapply将与模式stmt匹配,所有匹配项将使用L进行翻译,然后返回。 pat参数定义了empty没有匹配项的组件返回的内容。这样就给出了一个匹配项列表,每行一个列表项,在其上应用stat将它们转换为逗号分隔的字符串。这是这里提出的3种选择中最短的一种。

toString

给予:

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)

2)基数R 从上方使用 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 L,创建一个函数,该函数采用单词pat的字符向量并提取出匹配的单词由x变成pat。如果g的长度非零,则使用g转换其元素,然后使用L将其压缩为单个字符串;否则,返回toString

现在使用No Eventstmt的每个元素分解为单词,并将strsplit应用于每个这样的字符向量。

process

3)dplyr / tidyr 使用(1)中的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) 按行号和L分组,并将单词分成不同的行。过滤掉stmt中的这些单词,并将所有行折叠到一个names(L)组中,这些行通过stmt转换并使用L生成逗号分隔的字符串。删除toString列。在这一点上,我们得到了理想的结果,只是仍然缺少n行,因此请用No Event正确加入我们的行,并用statement替换NA。

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

更新

重新阅读问题后已进行了多次修订。还添加了更多替代方案。

答案 1 :(得分:0)

通过基数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"      ""

答案 2 :(得分:0)

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

答案 3 :(得分:0)

我不认为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>