R:有没有办法在两个不同的 dfs 中找到与两个字符串列的相同第一个元素的部分字符串匹配?
R: Is there a way to find partial string matches with the same first element of both the string columns in two different dfs?
我在两个不同的数据帧 df1 和 df2 中有两个字符串列 -> df1$name
和 df2$name
。 df1 有超过 10000 行,而 df2 有大约 200 多行。例如:
df1 <- data.frame(name = c("Peter P", "Jim Gordon", "Bruce Wayne", "Tony Stark","Mony Blake" ))
df2<- data.frame(name = c( "Jeter P", "Bruce Wayne", "Mony Blake" ))
注意:dfs 比这些大得多。
我首先使用了合并功能。它首先匹配公共行,但对于 "Jeter P" 它没有。
然后我使用来自 Stringdist 库的部分匹配函数 amatch
和 method = "lv"
。它匹配了 Peter P 和 Jeter P,两个不同的个体。我知道 amatch 接受位置和字母等的变化,但我希望函数搜索 df,同时在匹配字符串时保持字符串的第一个元素相同。
基本上,当我在 df2$name
中对 Jeter P
使用部分字符串匹配时,它只会将 df1$name
中字符串以 J 开头的行视为潜在的部分匹配。可能吗?
提前致谢。
@RonakShah 今天早些时候发布了一个版本,但随后由于他的解决方案不完全符合要求而将其删除。
想法是使用fuzzyjoin
包,它有很多函数可以在两个数据集之间进行模糊匹配。 None 其中完全符合这个问题的要求,但这里有一个更长的答案。
stringdist_inner_join
函数进行常规模糊匹配。它的工作原理是构造一个复杂的函数以在 fuzzy_join
中使用。
它不导出该功能;但是您可以创建自己的函数(我称之为 stringdist_match
),它只创建函数并将其导出。然后将其与比较第一个字母的函数结合起来,并在 fuzzy_join
中使用组合函数 (custom_match
)。这是一些代码。大多数 stringdist_match
函数是从 fuzzyjoin
包中复制的。
library(fuzzyjoin)
stringdist_match <- function(max_dist = 2,
method = c("osa", "lv", "dl", "hamming", "lcs", "qgram",
"cosine", "jaccard", "jw", "soundex"),
mode = "inner",
ignore_case = FALSE,
distance_col = NULL, ...) {
# It's a good idea to force evaluation of all the arguments
# in case they get changed between when we call this function and
# when we use the function it returns.
force(max_dist)
force(mode)
force(ignore_case)
force(distance_col)
forceotherargs <- list(...)
method <- match.arg(method)
if (method == "soundex") {
# soundex always returns 0 or 1, so any other max_dist would
# lead either to always matching or never matching
max_dist <- .5
}
function(v1, v2) {
if (ignore_case) {
v1 <- stringr::str_to_lower(v1)
v2 <- stringr::str_to_lower(v2)
}
# shortcut for Levenshtein-like methods: if the difference in
# string length is greater than the maximum string distance, the
# edit distance must be at least that large
# length is much faster to compute than string distance
if (method %in% c("osa", "lv", "dl")) {
length_diff <- abs(stringr::str_length(v1) - stringr::str_length(v2))
include <- length_diff <= max_dist
dists <- rep(NA, length(v1))
dists[include] <- stringdist::stringdist(v1[include], v2[include], method = method, ...)
} else {
# have to compute them all
dists <- stringdist::stringdist(v1, v2, method = method, ...)
}
ret <- tibble::tibble(include = (dists <= max_dist))
if (!is.null(distance_col)) {
ret[[distance_col]] <- dists
}
ret
}
}
# Now the example. First, create a matching function that
# just does the fuzzy part.
fuzzy_match <- stringdist_match()
# Next create a matching function that just compares first letters.
first_letter_match <- function(col1, col2)
sub("(^.).*", "\1", col1) == sub("(^.).*", "\1", col2)
# Now create one that requires both to match.
custom_match <- function(col1, col2)
first_letter_match(col1, col2) & fuzzy_match(col1, col2)
# Now run the example
df1 <- data.frame(name = c("Peter P", "Jim Gordon", "Bruce Wayne", "Tony Stark","Mony Blake" ))
df2<- data.frame(name = c( "Jeter P", "Bruce Wayne", "Mony Blake" ))
fuzzy_inner_join(df1, df2, by = "name", match_fun = custom_match)
#> name.x name.y
#> 1 Bruce Wayne Bruce Wayne
#> 2 Mony Blake Mony Blake
由 reprex package (v0.3.0)
于 2020 年 2 月 21 日创建
有关 stringdist_match
的所有参数的文档,请参阅 ?fuzzyjoin::stringdist_join
。
我在两个不同的数据帧 df1 和 df2 中有两个字符串列 -> df1$name
和 df2$name
。 df1 有超过 10000 行,而 df2 有大约 200 多行。例如:
df1 <- data.frame(name = c("Peter P", "Jim Gordon", "Bruce Wayne", "Tony Stark","Mony Blake" ))
df2<- data.frame(name = c( "Jeter P", "Bruce Wayne", "Mony Blake" ))
注意:dfs 比这些大得多。
我首先使用了合并功能。它首先匹配公共行,但对于 "Jeter P" 它没有。
然后我使用来自 Stringdist 库的部分匹配函数 amatch
和 method = "lv"
。它匹配了 Peter P 和 Jeter P,两个不同的个体。我知道 amatch 接受位置和字母等的变化,但我希望函数搜索 df,同时在匹配字符串时保持字符串的第一个元素相同。
基本上,当我在 df2$name
中对 Jeter P
使用部分字符串匹配时,它只会将 df1$name
中字符串以 J 开头的行视为潜在的部分匹配。可能吗?
提前致谢。
@RonakShah 今天早些时候发布了一个版本,但随后由于他的解决方案不完全符合要求而将其删除。
想法是使用fuzzyjoin
包,它有很多函数可以在两个数据集之间进行模糊匹配。 None 其中完全符合这个问题的要求,但这里有一个更长的答案。
stringdist_inner_join
函数进行常规模糊匹配。它的工作原理是构造一个复杂的函数以在 fuzzy_join
中使用。
它不导出该功能;但是您可以创建自己的函数(我称之为 stringdist_match
),它只创建函数并将其导出。然后将其与比较第一个字母的函数结合起来,并在 fuzzy_join
中使用组合函数 (custom_match
)。这是一些代码。大多数 stringdist_match
函数是从 fuzzyjoin
包中复制的。
library(fuzzyjoin)
stringdist_match <- function(max_dist = 2,
method = c("osa", "lv", "dl", "hamming", "lcs", "qgram",
"cosine", "jaccard", "jw", "soundex"),
mode = "inner",
ignore_case = FALSE,
distance_col = NULL, ...) {
# It's a good idea to force evaluation of all the arguments
# in case they get changed between when we call this function and
# when we use the function it returns.
force(max_dist)
force(mode)
force(ignore_case)
force(distance_col)
forceotherargs <- list(...)
method <- match.arg(method)
if (method == "soundex") {
# soundex always returns 0 or 1, so any other max_dist would
# lead either to always matching or never matching
max_dist <- .5
}
function(v1, v2) {
if (ignore_case) {
v1 <- stringr::str_to_lower(v1)
v2 <- stringr::str_to_lower(v2)
}
# shortcut for Levenshtein-like methods: if the difference in
# string length is greater than the maximum string distance, the
# edit distance must be at least that large
# length is much faster to compute than string distance
if (method %in% c("osa", "lv", "dl")) {
length_diff <- abs(stringr::str_length(v1) - stringr::str_length(v2))
include <- length_diff <= max_dist
dists <- rep(NA, length(v1))
dists[include] <- stringdist::stringdist(v1[include], v2[include], method = method, ...)
} else {
# have to compute them all
dists <- stringdist::stringdist(v1, v2, method = method, ...)
}
ret <- tibble::tibble(include = (dists <= max_dist))
if (!is.null(distance_col)) {
ret[[distance_col]] <- dists
}
ret
}
}
# Now the example. First, create a matching function that
# just does the fuzzy part.
fuzzy_match <- stringdist_match()
# Next create a matching function that just compares first letters.
first_letter_match <- function(col1, col2)
sub("(^.).*", "\1", col1) == sub("(^.).*", "\1", col2)
# Now create one that requires both to match.
custom_match <- function(col1, col2)
first_letter_match(col1, col2) & fuzzy_match(col1, col2)
# Now run the example
df1 <- data.frame(name = c("Peter P", "Jim Gordon", "Bruce Wayne", "Tony Stark","Mony Blake" ))
df2<- data.frame(name = c( "Jeter P", "Bruce Wayne", "Mony Blake" ))
fuzzy_inner_join(df1, df2, by = "name", match_fun = custom_match)
#> name.x name.y
#> 1 Bruce Wayne Bruce Wayne
#> 2 Mony Blake Mony Blake
由 reprex package (v0.3.0)
于 2020 年 2 月 21 日创建有关 stringdist_match
的所有参数的文档,请参阅 ?fuzzyjoin::stringdist_join
。