根据条件连接两个数据帧 (grepl)

Jes*_* CT 3 r left-join grepl fuzzyjoin

我希望根据条件连接两个数据帧,在本例中,一个字符串位于另一个字符串内。假设我有两个数据框,

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))

       fullnames ages
1       Jane Doe   30
2 Mr. John Smith   51
3 Nate Cox, Esq.   45
4   Bill Lee III   38
5 Ms. Kate Smith   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"))
  lastnames ages homestate
1       Doe   30        NJ
2       Cox   45        CT
3     Smith   20        MA
4      Jung   28        RI
5     Smith   51        MA
6       Lee   38        NY

Run Code Online (Sandbox Code Playgroud)

我想对这两个数据帧的年龄和其中df2$lastnames包含的行进行左连接df1$fullnames。我想fuzzy_join可能会这样做,但我不认为它喜欢我的grepl

joined_dfs <- fuzzy_join(df1, df2, by = c("ages", "fullnames"="lastnames"), 
+                          match_fun = c("=", "grepl()"),
+                          mode="left")
Error in which(m) : argument to 'which' is not logical
Run Code Online (Sandbox Code Playgroud)

期望的结果:与第一个数据框相同但附加了“homestate”列的数据框。有任何想法吗?

Gre*_*reg 5

总长DR

你只需要修复match_fun

# ...
match_fun = list(`==`, stringr::str_detect),
# ...
Run Code Online (Sandbox Code Playgroud)

背景

match_fun您的想法是正确的,但是您对 中参数的解释出错了fuzzyjoin::fuzzy_join()。根据文档match_fun应该是

给定两列的矢量化函数,返回 TRUE 或 FALSE 以确定它们是否匹配。可以是函数列表,一个函数对应于中指定的每一对列by(如果是命名列表,则使用 x 中的名称)。如果只给出一个函数,它将用于所有列对。

解决方案

一个简单的更正就可以解决问题,并通过 进一步格式化dplyr。为了概念清晰,我在排版上将by列与function用于匹配它们的 s 对齐:

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)
Run Code Online (Sandbox Code Playgroud)

结果

鉴于此处复制的示例数据

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")
)
Run Code Online (Sandbox Code Playgroud)

data.frame该解决方案应为生成以下内容joined_dfs,按要求格式化:

        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
Run Code Online (Sandbox Code Playgroud)

笔记

因为每个ages都是唯一的键,所以以下连接仅*names

fuzzy_join(
  df1, df2,
  by = c("fullnames" = "lastnames"),
  match_fun = stringr::str_detect,
  mode = "left"
)
Run Code Online (Sandbox Code Playgroud)

将更好地说明子字符串匹配的行为:

       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
Run Code Online (Sandbox Code Playgroud)

你错在哪里

类型错误

传递给的值match_fun应该是(symbolfor)afunction

fuzzyjoin::fuzzy_join(
  # ...
  match_fun = grepl
  # ...
)
Run Code Online (Sandbox Code Playgroud)

list这样的(symbols for )functions:

fuzzyjoin::fuzzy_join(
  # ...
  match_fun = list(`=`, grepl)
  # ...
)
Run Code Online (Sandbox Code Playgroud)

而不是提供 a listof symbols

match_fun = list(=, grepl)
Run Code Online (Sandbox Code Playgroud)

您错误地提供了一个vector字符串character

match_fun = c("=", "grepl()")
Run Code Online (Sandbox Code Playgroud)

语法错误

用户应命名functions

`=`
grepl
Run Code Online (Sandbox Code Playgroud)

但你错误地试图称呼他们:

=
grepl()
Run Code Online (Sandbox Code Playgroud)

命名它们将按照预期将functions本身传递给match_fun,而调用它们将传递它们的返回值* 。在 R 中,类似的运算符=使用反引号命名:`=`

* 假设调用没有因错误而失败。在这里,他们失败。

功能不当

要比较两个值是否相等(此处为character向量df1$fullnames和 )df2$lastnames,您应该使用关系运算符==;但您错误地提供了赋值运算符=

此外,grepl()它并没有按照期望的方式进行矢量化match_fun。虽然它的第二个参数( x) 确实是一个向量

寻找匹配的字符向量,或者可以由 as.character 强制转换为字符向量的对象。支持长向量。

它的第一个参数( pattern) 被(视为)单个character字符串:

fixed = TRUE包含要在给定字符向量中匹配的正则表达式(或 的字符串)的字符串。如果可能的话,强制转换as.character为字符串。如果提供长度为 2 或以上的字符向量,则使用第一个元素时会发出警告。regexpr除了、gregexpr和 之外,允许缺失值regexec

因此,grepl()不是一个

给定两列的向量化函数...

而是function给定一个字符串(标量)和一列(向量)字符串。

你祈祷的答案不是grepl(),而是类似的东西stringr::str_detect(),即

string和进行矢量化pattern。相当于grepl(pattern, x)

并包裹stringi::stri_detect().

笔记

由于您只是尝试检测文字字符串 in是否df1$fullnames包含文字字符串 in df2$lastnames,因此您不想意外地将字符串视为df2$lastnames则表达式 模式。现在,您的df2$lastnames列在统计上不太可能包含具有特殊正则表达式字符的名称;唯一的例外是-,它按字面解释在 之外[]不太可能在名称中找到。

如果您仍然担心意外的正则表达式,您可能需要考虑使用或 的替代搜索方法。它们分别通过字节“规范等价”执行文字匹配;后者根据区域设置和特殊字符进行调整,以与自然语言处理保持一致。stringi::stri_detect_fixed()stringi::stri_detect_coll()