TLDR
你只需要修复match_fun:
# ...
match_fun = list(`==`, stringr::str_detect),
# ...
背景
您的想法是正确的,但是您对fuzzyjoin::fuzzy_join() 中的match_fun 参数的解释出错了。根据documentation,match_fun 应该是一个
给定两列的向量化函数,返回 TRUE 或 FALSE 以判断它们是否匹配。可以是by 中指定的每对列的函数列表(如果是命名列表,则使用 x 中的名称)。如果只给出一个函数,则它会用于所有列对。
解决方案
通过dplyr 进一步格式化,一个简单的修正就可以解决问题。为了概念清晰,我将by 列与用于匹配它们的functions 排版对齐:
library(dplyr)
# ...
# Existing code
# ...
joined_dfs <- fuzzy_join(
df1, df2,
by = c("ages", "fullnames" = "lastnames"),
# |----| |-----------------------|
match_fun = list(`==` , stringr::str_detect ),
# |--| |-----------------|
# Match by equality ^ ^ Match by detection of `lastnames` in `fullnames`
mode = "left"
) %>%
# Format resulting dataset as you requested.
select(fullnames, ages = ages.x, homestate)
结果
鉴于您在此处复制的示例数据
df1 <- data.frame(
fullnames = c("Jane Doe", "Mr. John Smith", "Nate Cox, Esq.", "Bill Lee III", "Ms. Kate Smith"),
ages = c(30, 51, 45, 38, 20)
)
df2 <- data.frame(
lastnames = c("Doe", "Cox", "Smith", "Jung", "Smith", "Lee"),
ages = c(30, 45, 20, 28, 51, 38),
homestate = c("NJ", "CT", "MA", "RI", "MA", "NY")
)
此解决方案应为joined_dfs 生成以下data.frame,并按要求格式化:
fullnames ages homestate
1 Jane Doe 30 NJ
2 Mr. John Smith 51 MA
3 Nate Cox, Esq. 45 CT
4 Bill Lee III 38 NY
5 Ms. Kate Smith 20 MA
注意
因为每一个ages都是一个唯一的key,所以下面只加入*names
fuzzy_join(
df1, df2,
by = c("fullnames" = "lastnames"),
match_fun = stringr::str_detect,
mode = "left"
)
将更好地说明匹配子字符串的行为:
fullnames ages.x lastnames ages.y homestate
1 Jane Doe 30 Doe 30 NJ
2 Mr. John Smith 51 Smith 20 MA
3 Mr. John Smith 51 Smith 51 MA
4 Nate Cox, Esq. 45 Cox 45 CT
5 Bill Lee III 38 Lee 38 NY
6 Ms. Kate Smith 20 Smith 20 MA
7 Ms. Kate Smith 20 Smith 51 MA
你哪里出错了
类型错误
传递给match_fun 的值应该是(symbol for)function
fuzzyjoin::fuzzy_join(
# ...
match_fun = grepl
# ...
)
或list (symbols for)functions:
fuzzyjoin::fuzzy_join(
# ...
match_fun = list(`=`, grepl)
# ...
)
而不是提供list 的symbols
match_fun = list(=, grepl)
您错误地提供了 vector 的 character 字符串:
match_fun = c("=", "grepl()")
语法错误
用户应该命名functions
`=`
grepl
但你错误地尝试呼叫他们:
=
grepl()
按预期命名它们会将functions 它们自己传递给match_fun,而调用它们将传递它们的返回值*。在 R 中,像 = 这样的运算符使用反引号命名:`=`。
* 假设调用没有因错误而失败。在这里,他们会失败。
不当功能
要比较两个值是否相等,这里是character 向量df1$fullnames 和df2$lastnames,您应该使用关系运算符==;但是您错误地提供了 assignment 运算符 =。
此外,grepl() 并没有像 match_fun 所希望的那样矢量化。而它的第二个argument (x) 确实是一个向量
寻找匹配的字符向量,或者可以被 as.character 强制转换为字符向量的对象。支持长向量。
它的第一个 argument (pattern) 是(被视为)单个 character 字符串:
字符串,包含要在给定字符向量中匹配的正则表达式(或fixed = TRUE 的字符串)。如果可能,由as.character 强制转换为字符串。 如果提供长度为 2 或更大的字符向量,则使用第一个元素并发出警告。除了regexpr、gregexpr 和regexec 之外,允许缺少值。
因此,grepl() 不是
给定两个列的向量化函数...
而是给定一个字符串(标量)和一列(向量)字符串的function。
你的祈祷的答案不是grepl(),而是类似stringr::str_detect(),即
在string 和pattern 上矢量化。相当于grepl(pattern, x)。
其中包含stringi::stri_detect()。
注意
由于您只是想检测df1$fullnames 中的literal 字符串是否包含df2$lastnames 中的literal 字符串,因此您不想意外处理df2$lastnames 中的字符串为 regular expression patterns。现在您的df2$lastnames 列在统计上不太可能包含带有特殊正则表达式字符的名称;除了- 之外的唯一例外,它在[] 之外进行字面解释,不太可能在名称中找到。
如果您仍然担心意外的正则表达式,您可能需要考虑 alternative search methods 和 stringi::stri_detect_fixed() 或 stringi::stri_detect_coll()。它们分别通过byte 或"canonical equivalence" 执行文字匹配;后者根据自然语言处理调整区域设置和特殊字符。