str_detect 在同一行的多个列上

str_detect on multiple columns in the same row

我有两个数据集,一个有全名,一个有名字和姓氏。

library(tidyverse)
(x = tibble(fullname = c("Michael Smith", 
                         "Elisabeth Brown", 
                         "John-Henry Albert")))
#> # A tibble: 3 x 1
#>   fullname         
#>   <chr>            
#> 1 Michael Smith    
#> 2 Elisabeth Brown  
#> 3 John-Henry Albert

(y = tribble(~first, ~last,
            "Elisabeth", "Smith",
            "John", "Albert",
            "Roland", "Brown"))
#> # A tibble: 3 x 2
#>   first     last  
#>   <chr>     <chr> 
#> 1 Elisabeth Smith 
#> 2 John      Albert
#> 3 Roland    Brown

我想创建一个布尔值列,只有当第一列和最后一列在全名列中时才为真。

本质上,我正在寻找类似的东西:

x %>% 
  mutate(fname_match = str_detect(fullname, paste0(y$first, collapse = "|")), ## correct 
         lname_match = str_detect(fullname, paste0(y$last, collapse = "|"))) ## correct
#> # A tibble: 3 x 3
#>   fullname          fname_match lname_match
#>   <chr>             <lgl>       <lgl>      
#> 1 Michael Smith     FALSE       TRUE       
#> 2 Elisabeth Brown   TRUE        TRUE       
#> 3 John-Henry Albert TRUE        TRUE

但在这里,如果我选择包含两个 TRUE 的列,Elisabeth Brown 将是误报,因为匹配的名字和姓氏不在同一行。

到目前为止,我最好的想法是合并第一列和最后一列并搜索它,但这会为 John-Henry 造成漏报

y = tribble(~first, ~last,
            "Elisabeth", "Smith",
            "John", "Albert",
            "Roland", "Brown") %>% 
    rowwise() %>% 
    mutate(longname = paste(first, last, sep = "&"))


x %>% 
  mutate(full_match = str_detect(fullname, paste0(y$longname, collapse = "|")))
#> # A tibble: 3 x 2
#>   fullname          full_match
#>   <chr>             <lgl>     
#> 1 Michael Smith     FALSE     
#> 2 Elisabeth Brown   FALSE     
#> 3 John-Henry Albert FALSE

我认为这可以满足您的需求,使用 purrr::map2 遍历 firstlast.

的元组
library(dplyr)
library(purrr)

y %>%
  mutate(
    name_match = map2_lgl(
      first, last, 
      .f = ~any(grepl(paste0(.x, '.*', .y), x$fullname, ignore.case = T))
    )
  )

请注意,paste0(.x, '.*', .y) 将它们组合成一个正则表达式,该正则表达式只允许姓氏在第一个 之后 完全出现的行通过。这似乎是合理的(否则,名字“Elisabeth”,姓氏“Abe”仍然是 TRUE,我在这里假设你不会想要)。 另外,以上是大小写 insensitive.

// 更新:
我忘了;相反,如果你想检查 x 中的 fullname 值,那么你可以 运行 this:

x %>%
  rowwise() %>%
  mutate(
    name_match = any(map2_lgl(
      y$first, y$last,
      .f = ~grepl(paste0('\b', .x, '\b.*\b', .y, '\b'), fullname, ignore.case = T)
    ))
  )

根据此检查对您的重要性以及您想做出多少假设,进一步调整上述正则表达式可能有意义:

  1. 确保名字和姓氏在全名
    中为isolated words -> paste0('\b', .x, '\b.*\b', .y, '\b')
  2. 测试名字是否出现在开头
    -> paste0('^', .x, '\b.*\b', .y, '\b')
  3. 测试全名是否在姓氏之后结束
    -> paste0('\b', .x, '\b.*\b', .y, '$')