提问人:Pxanalyst 提问时间:7/20/2023 最后编辑:ProgmanPxanalyst 更新时间:7/21/2023 访问量:58
R:如何基于不完全相同的模糊匹配字符串左连接两个表
R: How to left join two tables based on fuzzy matching strings that are not exactly the same
问:
我正在尝试将表 1“人名”左联接到表 2“姓名”,并从表 2 的“工作组”列中获取值
df1 <- read.table(text="
Person_Name
PEREZ, MINDY
PEREZ, ABA
CLARKE, LINDA
THOMAS, NICOLE", header=T, sep="|")
df2 <- read.table(text=
'Name Work_Group
"Perez-Tie, Mindy" "Group A"
"Rulnick-Perez, Aba" "Group C"
"Mcabe-Clarke, Linda" "Group A"
"Thomas, Nicole" "Group B"', header=T)
这是我当前的代码,但它没有显示任何匹配项。我不确定我可能做错了什么,但任何帮助都会很棒!
ci_str_detect <- function(x, y){str_detect(x, regex(y, ignore_case = TRUE))}
result <- fuzzy_left_join(Table1,Table2, by = c("Person_Name" = "Name"),
match_fun = ci_str_detect ) %>%
select(PersoN_Name,Name,Work_Group)
print(result)
答:
1赞
Mark
7/20/2023
#1
下面是工作代码:
library(tidyverse)
# make sure the two dataframes have the same format and the same columns
df1 <- df1 %>%
mutate(Person_Name = str_to_title(Person_Name)) %>%
separate_wider_delim(Person_Name, delim=", ", names=c("Last_Name", "First_Name"))
df2 <- df2 %>%
mutate(Name = str_to_title(Name)) %>%
separate_wider_delim(Name, delim=", ", names=c("Last_Name", "First_Name"))
# Maybe the simplest way of doing things (but maybe not what you had in mind ;) )
left_join(df1, df2, by="First_Name")
# But this is more robust
cross_join(df1, df2) %>%
filter(str_detect(Last_Name.y, Last_Name.x), str_detect(First_Name.y, First_Name.x)) %>%
select(Last_Name = Last_Name.y, First_Name = First_Name.y, Work_Group)
简而言之,您拥有的代码的问题在于字符串不匹配。以第一行为例: “Perez, Mindy”与“Perez-tie, Mindy”不匹配,反之亦然(两者均在此处显示为小写,以使其更加明显)。您希望分别匹配名字和姓氏。
您可以查看的另一件事是包中的函数(我无法让它在所有行中正常工作,但它在您的情况下可能很有用)stringdist_join
fuzzyjoin
1赞
LMc
7/20/2023
#2
下面是一个选项,在匹配之前需要一些字符串操作:fuzzyjoin
根据@moodymudskipper的评论更新
library(fuzzyjoin)
rev_str <- \(x) paste(rev(x), collapse = " ")
df1$rev_name = sapply(strsplit(df1$Person_Name, ", "), rev_str)
df2$rev_name = sapply(strsplit(df2$Name, ", "), rev_str)
stringdist_left_join(df1, df2, by = "rev_name",
method = "jw",
p = 0.15,
distance_col = "distance",
ignore_case = TRUE,
max_dist = 0.15) |>
subset(select = -c(rev_name.x, rev_name.y, distance))
这使用 Jaro-Winkler 距离,您可以在其中添加权重 (),为以相同方式开始的字符串提供加成。这就是为什么我首先颠倒了这些名字,因为在你的回复中,它们往往是准确的。在查看了 的值后,我选择了距离截止值 ,我将其包含在代码中,以便您可以看到。您可能需要针对实际数据集进行调整。p
0.15
distance
注意:值接近于零表示字符串更相似。
输出
Person_Name Name Work_Group
1 PEREZ, MINDY Perez-Tie, Mindy Group A
2 PEREZ, ABA Rulnick-Perez, Aba Group C
3 CLARKE, LINDA Mcabe-Clarke, Linda Group A
4 THOMAS, NICOLE Thomas, Nicole Group B
评论
1赞
moodymudskipper
7/21/2023
Jaro-Winkler 距离不区分大小写吗?我会在匹配之前将所有内容转换为较低。其他答案使用,但我认为这并不那么好。str_to_title
0赞
LMc
7/21/2023
stringdist 连接有一个参数。默认值区分大小写。ignore_case
0赞
Pxanalyst
7/21/2023
非常感谢你!我在这里做了一些细微的调整,我认为|>是一个错别字,所以更改为%>%,对于我正在比较的较大数据集,最大分辨率也为 0.1
0赞
LMc
7/21/2023
@Pxanalyst 是 R 4.1.0 中引入的基本 R 管道运算符。管道在这里也工作正常。|>
magrittr
%>%
评论