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
遍历 first
和 last
.
的元组
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)
))
)
根据此检查对您的重要性以及您想做出多少假设,进一步调整上述正则表达式可能有意义:
- 确保名字和姓氏在全名
中为isolated words
-> paste0('\b', .x, '\b.*\b', .y, '\b')
- 测试名字是否出现在开头
-> paste0('^', .x, '\b.*\b', .y, '\b')
- 测试全名是否在姓氏之后结束
-> paste0('\b', .x, '\b.*\b', .y, '$')
我有两个数据集,一个有全名,一个有名字和姓氏。
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
遍历 first
和 last
.
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)
))
)
根据此检查对您的重要性以及您想做出多少假设,进一步调整上述正则表达式可能有意义:
- 确保名字和姓氏在全名
中为isolated words ->paste0('\b', .x, '\b.*\b', .y, '\b')
- 测试名字是否出现在开头
->paste0('^', .x, '\b.*\b', .y, '\b')
- 测试全名是否在姓氏之后结束
->paste0('\b', .x, '\b.*\b', .y, '$')