R 按条件的字符串距离矩阵
我写了一个脚本来对公司名称进行模糊匹配。我将一些不总是完全正确的公司名称(即可能有小的拼写错误或“inc.”后缀缺失)与“正确”的公司名称和ID进行匹配。显然,关键是要将ID正确地附加到不总是正确的公司名称上 下面是我正在匹配的数据集的一些非常简化的版本(我还没有使用zip部分,但稍后将返回):R 按条件的字符串距离矩阵,r,distance,stringdist,R,Distance,Stringdist,我写了一个脚本来对公司名称进行模糊匹配。我将一些不总是完全正确的公司名称(即可能有小的拼写错误或“inc.”后缀缺失)与“正确”的公司名称和ID进行匹配。显然,关键是要将ID正确地附加到不总是正确的公司名称上 下面是我正在匹配的数据集的一些非常简化的版本(我还没有使用zip部分,但稍后将返回): 我就是想不出一个办法。有什么想法吗?我有一些想法。如果不需要距离矩阵,可以这样求解。我使用dplyr是因为我更了解它。您可以将代码拆分为多个部分,而不是一个dplyr命令。或者使用data.table。
我就是想不出一个办法。有什么想法吗?我有一些想法。如果不需要距离矩阵,可以这样求解。我使用dplyr是因为我更了解它。您可以将代码拆分为多个部分,而不是一个dplyr命令。或者使用data.table。这可能更快 采取的步骤:
library(stringdist)
library(dplyr)
df <- data.frame(zip = c("4760","5445", "2200"), company = c("company x", "company y", "company z"))
corpus <- data.frame(zip = c("4760","5445", "2200", "2200", "2200"), company = c("company x inc.", "company y inc.", "company z inc.", "company a inc.", "company b inc."), id = c(12121212, 23232323, 34343434, 56565656, 67676767))
distance.method <- c("jw")
combined_min_distance <- inner_join(df, corpus, by = "zip" ) %>%
mutate(distance = stringdist(tolower(combined$company.x),
tolower(combined$company.y),
method = distance.method,
nthread = getOption("sd_num_thread"))) %>%
group_by(company.x) %>%
filter(distance == min(distance))
combined_min_distance
zip company.x company.y id distance
(fctr) (fctr) (fctr) (dbl) (dbl)
1 2200 company z company z inc. 34343434 0.1190476
2 4760 company x company x inc. 12121212 0.1190476
3 5445 company y company y inc. 23232323 0.1190476
库(stringdist)
图书馆(dplyr)
df我有一些想法。如果不需要距离矩阵,可以这样求解。我使用dplyr是因为我更了解它。您可以将代码拆分为多个部分,而不是一个dplyr命令。或者使用data.table。这可能更快
采取的步骤:
使用zip上的内部连接连接df和语料库。这将删除所有不需要的记录,并且您的公司名称彼此相邻
计算公司名称之间的距离
按原公司分组
基于最小距离的滤波器
这些步骤避免了先创建矩阵,然后寻找最小值或将其他值放入NA
library(stringdist)
library(dplyr)
df <- data.frame(zip = c("4760","5445", "2200"), company = c("company x", "company y", "company z"))
corpus <- data.frame(zip = c("4760","5445", "2200", "2200", "2200"), company = c("company x inc.", "company y inc.", "company z inc.", "company a inc.", "company b inc."), id = c(12121212, 23232323, 34343434, 56565656, 67676767))
distance.method <- c("jw")
combined_min_distance <- inner_join(df, corpus, by = "zip" ) %>%
mutate(distance = stringdist(tolower(combined$company.x),
tolower(combined$company.y),
method = distance.method,
nthread = getOption("sd_num_thread"))) %>%
group_by(company.x) %>%
filter(distance == min(distance))
combined_min_distance
zip company.x company.y id distance
(fctr) (fctr) (fctr) (dbl) (dbl)
1 2200 company z company z inc. 34343434 0.1190476
2 4760 company x company x inc. 12121212 0.1190476
3 5445 company y company y inc. 23232323 0.1190476
库(stringdist)
图书馆(dplyr)
df下面的方法使用dplyr
并从phiver的连接两个数据帧的方法开始,然后继续生成类似于string.dist.matrix
的数据帧,或者以压缩的“键值”形式生成数据帧。我已将另一家公司添加到您的df
数据框中,以包括多家公司使用相同的df-zip
的情况
距离矩阵版本
是:
但是,由于df
矩阵中有4000行,整个字符串距离矩阵非常大,有许多NA。更高效的版本使用tidyr
包中的gather
功能,以键值
格式生成结果。在这种方法中,一些变量形成唯一的键,然后这些键具有关联的值。tidyr
包的vignette更详细地解释了这一点。在您的例子中,语料库
公司名称和df
公司名称构成键
,它们名称之间的字符串距离是值
。这是针对每个邮政编码执行的,因此不会存储完整的字符串距离矩阵。您也可能会发现,在后续分析中使用此选项更容易。代码仅在最后一行与上一版本不同
library(tidyr)
dist_keyval <- inner_join(corpus, df, by = "zip") %>%
mutate(corpus_co=tolower(as.character(company.x)), df_co=tolower(as.character(company.y)), company.x=NULL, company.y=NULL) %>%
group_by(zip) %>%
do( { dist_df=data.frame(unique(.$corpus_co),
stringdistmatrix(unique(.$corpus_co), unique(.$df_co), method=distance.method), stringsAsFactors=FALSE);
colnames(dist_df) = c("corpus_co", unique(.$df_co));
gather(dist_df, key=df_co, value=str_dist, -corpus_co)})
已编辑
查找corpus_co
的代码是,它是每个df_co
的最小距离:
dist_min <- dist_keyval %>% group_by(zip, df_co) %>%
slice(which.min(str_dist))
最后一个select
显示了如何按特定顺序重新排列列。下面的方法使用dplyr
并从phiver的连接两个数据帧的方法开始,然后继续生成类似于string.dist.matrix
的数据帧或压缩格式的数据帧“关键值”表单。我已将另一家公司添加到您的df
数据框中,以包括多家公司使用相同df-zip
的情况
距离矩阵版本
是:
但是,由于df
矩阵中有4000行,许多NA的完整字符串距离矩阵非常大。更高效的版本使用tidyr
包中的gather
函数生成键值
格式的结果。在这种方法中,一些变量形成唯一的键,然后具有ass关联值。tidyr
包的渐晕图更详细地解释了这一点。在您的案例中,语料库
公司名称和df
公司名称构成键
,它们名称之间的字符串距离是值
。这是针对每个邮政编码完成的,因此完整的字符串距离矩阵是从未存储。您可能会发现,在后续分析中使用此代码更容易。代码仅在最后一行与以前的版本不同
library(tidyr)
dist_keyval <- inner_join(corpus, df, by = "zip") %>%
mutate(corpus_co=tolower(as.character(company.x)), df_co=tolower(as.character(company.y)), company.x=NULL, company.y=NULL) %>%
group_by(zip) %>%
do( { dist_df=data.frame(unique(.$corpus_co),
stringdistmatrix(unique(.$corpus_co), unique(.$df_co), method=distance.method), stringsAsFactors=FALSE);
colnames(dist_df) = c("corpus_co", unique(.$df_co));
gather(dist_df, key=df_co, value=str_dist, -corpus_co)})
已编辑
查找corpus_co
的代码是,它是每个df_co
的最小距离:
dist_min <- dist_keyval %>% group_by(zip, df_co) %>%
slice(which.min(str_dist))
最后一个select
显示如何将列重新排列为特定顺序。您可以使用stringdist::amatch
并避免计算完整的stringdist矩阵
df <- data.frame(zip = c("4760","5445", "2200"), company = c("company x", "company y", "company z"))
corpus <- data.frame(zip = c("4760","5445", "2200", "2200", "2200"), company = c("company x inc.", "company y inc.", "company z inc.", "company a inc.", "company b inc."), id = c(12121212, 23232323, 34343434, 56565656, 67676767))
i <- stringdist::amatch(df$company,corpus$company,maxDist=5)
merged <- data.frame(df$company,corpus$company[i])
merged
> merged
df.company corpus.company.i.
1 company x company x inc.
2 company y company y inc.
3 company z company z inc.
您可以使用stringdist::amatch
并避免计算完整的stringdist矩阵
df <- data.frame(zip = c("4760","5445", "2200"), company = c("company x", "company y", "company z"))
corpus <- data.frame(zip = c("4760","5445", "2200", "2200", "2200"), company = c("company x inc.", "company y inc.", "company z inc.", "company a inc.", "company b inc."), id = c(12121212, 23232323, 34343434, 56565656, 67676767))
i <- stringdist::amatch(df$company,corpus$company,maxDist=5)
merged <- data.frame(df$company,corpus$company[i])
merged
> merged
df.company corpus.company.i.
1 company x company x inc.
2 company y company y inc.
3 company z company z inc.
感谢你和Phiver。我仍在努力理解所有内容,但我得到了整体概念,并且它按照它应该的方式工作。我一直在尝试向最终结果添加更多列,但似乎不知道如何添加。我明白,在这么长的链中,最后五列丢失了,但具体在哪里?感谢b还有你和菲弗。我仍在努力理解所有内容,但我得到了整体概念,并且它按它应该的方式工作。我一直在尝试为最终结果添加更多的列,但似乎不知道如何添加。我知道,最后五列是在长链中丢失的,但具体在哪里?我需要
dist_min <- dist_keyval %>% group_by(zip, df_co) %>%
slice(which.min(str_dist))
final_result <- corpus %>% mutate(lower_co = tolower(as.character(company))) %>%
right_join(dist_min, by = c("zip", "lower_co" = "corpus_co") ) %>%
select(c(df_co, company, id), everything(), -lower_co)
df_co company id zip str_dist
1 company a company a inc. 56565656 2200 0.1190476
2 company z company z inc. 34343434 2200 0.1190476
3 company x company x inc. 12121212 4760 0.1190476
4 company y company y inc. 23232323 5445 0.1190476
df <- data.frame(zip = c("4760","5445", "2200"), company = c("company x", "company y", "company z"))
corpus <- data.frame(zip = c("4760","5445", "2200", "2200", "2200"), company = c("company x inc.", "company y inc.", "company z inc.", "company a inc.", "company b inc."), id = c(12121212, 23232323, 34343434, 56565656, 67676767))
i <- stringdist::amatch(df$company,corpus$company,maxDist=5)
merged <- data.frame(df$company,corpus$company[i])
merged
> merged
df.company corpus.company.i.
1 company x company x inc.
2 company y company y inc.
3 company z company z inc.
lookup <- gsub(" inc.$","",corpus$company)
i2 <- stringdist::amatch(df$company,lookup,maxDist=2)
merged2 <- data.frame(df$company,corpus$company[i2])