regex 跨两个表的正则表达式搜索

zed5wv10  于 2023-06-25  发布在  其他
关注(0)|答案(2)|浏览(118)
    • bounty将在6天内到期**。回答此问题可获得+100声望奖励。Ashti正在寻找一个答案从一个有信誉的来源

我有下面的df,其中有一个name列和class列:

df=data.frame(name=c("name1","name2","name3","name4"), class=c("classA","classA","classB","classC"))

我有下面的dataframe,其中包含名称的组合:

df2=data.frame(names=c("name1;name3","name5,name6","name2 name 8","name4"))

我需要通过执行正则表达式搜索来确定df2中名称的分类,并标记它们属于哪个类(classes),并创建一个标志(class_flag):

df2=data.frame(names=c("name1;name3","name5,name6","name2 name 8","name4"),class_flag=c("Y","N","Y","Y"),classes=c("classA,classB", NA, "classA","classC"))

我现在有一个繁琐的过程来做这件事,我将展示,但我想有一个更好的方法来做这件事,我必须为每个类做这件事,我只是为下面的一个类展示它:

class_A_search=paste(paste0("\\b",toupper(df$name),collapse = "|"))

df2%>%mutate(class_flag=ifelse(str_detect(toupper(names),class_A_search),"Y","N"), class_A=ifelse(str_detect(toupper(names),class_A_search),"classA",NA))

这是数据集的简化版本,df2有100万行,names列并不限于这里显示的那些。

jhiyze9q

jhiyze9q1#

可能带有regmatches的基本R选项会有所帮助

transform(
    transform(
        df2,
        classes = sapply(
            regmatches(names, gregexpr(paste0(df$name, collapse = "|"), names)),
            \(x) {
                with(
                    df,
                    replace(
                        d <- paste0(unique(class[match(x, name)]), collapse = ","),
                        !nzchar(d),
                        NA
                    )
                )
            }
        )
    ),
    class_flag = c("Y", "N")[1 + is.na(classes)]
)

它给出了

names       classes class_flag
1  name1;name3 classA,classB          Y
2  name5,name6          <NA>          N
3 name2 name 8        classA          Y
4        name4        classC          Y
q3qa4bjr

q3qa4bjr2#

以下是tidyverse选项:

library(tidyverse)

lookup <- deframe(df)
df2 |>
  mutate(class = str_extract_all(names, str_c("\\b", df$name, "\\b", collapse = "|")),
         class = map(class, ~ set_names(unname(lookup[.x]))),
         class_flag = ifelse(lengths(class), "Y", "N"),
         unnest_class = class) |>
  unnest_wider(unnest_class)

如何运作

  1. str_extract_all返回一个列表列,每个列表元素对应于 Dataframe 中的一行。每个列表元素都是提取的正则表达式匹配的向量。
    1.由于class是一个列表列,我们使用map来迭代它,并在deframe(df)创建的命名向量中查找提取的值。set_names用于在每个列表元素中创建一个命名向量,以便在步骤#4中取消嵌套(这些成为列名)。
  2. class仍然是一个列表列。我之所以这样做,是因为R中有很多处理列表的功能,而不是将其折叠成字符串。
    1.使用lengths创建class_flag,如果列表元素为空(例如character(0)),则返回0(相当于FALSE)。
    1.复制名为unnest_class的列表列class,并将其解嵌套到列中。
    如果您确实需要class作为字符列,则可以将此输出通过管道传输到mutate(class = map_chr(class, str_flatten_comma))

输出

names        class     class_flag classA classB classC
  <chr>        <list>    <chr>      <chr>  <chr>  <chr> 
1 name1;name3  <chr [2]> Y          classA classB NA    
2 name5,name6  <chr [0]> N          NA     NA     NA    
3 name2 name 8 <chr [1]> Y          classA NA     NA    
4 name4        <chr [1]> Y          NA     NA     classC

基准

df2中的行数增加到100,000以获得更好的性能。
如果您不需要列classAclassB等,并删除unnest_wider管道,这个答案与@ThomasIsCoding发布的答案更具可比性。在没有unnest_wider的情况下,我发现这个答案更快,但我将其保留在基准测试中,因为看起来你需要这些列:

set.seed(1)
df2 <- df2[sample(1:nrow(df2), 1E5, replace = T),, drop = F]

(bench <- microbenchmark::microbenchmark(
  stringr = {lookup <- deframe(df)
  df2 |>
    mutate(class = str_extract_all(names, str_c("\\b", df$name, "\\b", collapse = "|")),
           class = map(class, ~ set_names(unname(lookup[.x]))),
           class_flag = ifelse(lengths(class), "Y", "N"),
           unnest_class = class) |>
    unnest_wider(unnest_class)},
  baseR = {transform(
    transform(
      df2,
      classes = sapply(
        regmatches(names, gregexpr(paste0(df$name, collapse = "|"), names)),
        \(x) {
          with(
            df,
            replace(
              d <- paste0(unique(class[match(x, name)]), collapse = ","),
              !nzchar(d),
              NA
            )
          )
        }
      )
    ),
    class_flag = c("Y", "N")[1 + is.na(classes)]
  )},
  times = 20L,
  unit = "seconds"
))

Unit: seconds
    expr      min       lq     mean   median       uq      max neval cld
 stringr 3.759750 4.249305 4.461382 4.527822 4.732876 4.953810    20  a 
   baseR 2.736081 2.835327 3.019493 3.044236 3.137328 3.427364    20   b

ggplot2::autoplot(bench)

相关问题