R 按条件的字符串距离矩阵

R 按条件的字符串距离矩阵,r,distance,stringdist,R,Distance,Stringdist,我写了一个脚本来对公司名称进行模糊匹配。我将一些不总是完全正确的公司名称(即可能有小的拼写错误或“inc.”后缀缺失)与“正确”的公司名称和ID进行匹配。显然,关键是要将ID正确地附加到不总是正确的公司名称上 下面是我正在匹配的数据集的一些非常简化的版本(我还没有使用zip部分,但稍后将返回): 我就是想不出一个办法。有什么想法吗?我有一些想法。如果不需要距离矩阵,可以这样求解。我使用dplyr是因为我更了解它。您可以将代码拆分为多个部分,而不是一个dplyr命令。或者使用data.table。

我写了一个脚本来对公司名称进行模糊匹配。我将一些不总是完全正确的公司名称(即可能有小的拼写错误或“inc.”后缀缺失)与“正确”的公司名称和ID进行匹配。显然,关键是要将ID正确地附加到不总是正确的公司名称上

下面是我正在匹配的数据集的一些非常简化的版本(我还没有使用zip部分,但稍后将返回):


我就是想不出一个办法。有什么想法吗?

我有一些想法。如果不需要距离矩阵,可以这样求解。我使用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是因为我更了解它。您可以将代码拆分为多个部分,而不是一个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])