搜索并替换字符串列表中的多个字符串:改进R代码

时间:2016-03-04 09:07:47

标签: regex r string

我正在寻找R中以下问题的简化解决方案:我有一个用逗号分隔的名称列表 - 但是,其中一些名称中也有逗号。为了分隔名称,我想首先用逗号替换所有名称,然后用逗号分隔。我的问题是我有大约26000个字符串,每个字符串有几个名字,我有一个大约130个带逗号的名字列表。我已经编写了一个嵌套的foreach循环(为了使用多个核心来加快速度),它可以工作,但速度非常慢。有没有更快的方法来搜索字符串并替换相关的名称?这是我的示例代码:

List_of_names<-as.data.frame(c("Fred, Heiko, Franz, Jr., Nice, LLC, Meike","Digital, Mike, John, Sr","Svenja, Sven"))
Comma_names<-as.data.frame(c("Franz, Jr.","Nice, LLC","John, Sr"))
colnames(Comma_names)<-"name"
Comma_names$replace_names<-gsub(",", "",Comma_names[,"name"])

library(doParallel)
library(foreach)
cl<-makeCluster(4) # Create cluster with desired number of cores
registerDoParallel(cl) # Register cluster


names_new<-foreach (i=1:nrow(List_of_names),.errorhandling="pass",.packages=c("foreach")) %dopar% {
  name_2<-List_of_names[i,]
  foreach (j=1:nrow(Comma_names),.combine=rbind,.errorhandling="pass") %do% {
    if(length(grep(Comma_names[j,1],name_2))>0){
      name_2<-gsub(Comma_names[j,1], Comma_names[j,2],name_2)
    }
  }
  name_2
}

此外,foreach循环的结果是一个列表,但如果我尝试保存列表或替换原始数据框中的列,则需要永久。如何更改代码以使其更快?

感谢所有读过这篇文章的人,并且能够提供帮助!

1 个答案:

答案 0 :(得分:2)

<强>原理

您可以使用来自Reduce包的stri_replace_allstringi的组合。

<强>代码

library(stringi)
Comma_names <- structure(list(name = c("Franz, Jr.", "Nice, LLC", "John, Sr"), 
                              replace_names = c("Franz Jr.", "Nice LLC", "John Sr")), 
                              .Names = c("name", "replace_names"), 
                              row.names = c(NA, -3L), class = "data.frame")


List_of_names <- structure(list(name = c("Fred, Heiko, Franz, Jr., Nice, LLC, Meike",
                                         "Digital, Mike, John, Sr", "Svenja, Sven")), 
                                .Names = "name", 
                                row.names = c(NA, -3L), class = "data.frame")

wrapper <- function(str, ind) stri_replace_all(str, Comma_names$replace_names[ind], 
                                               fixed = Comma_names$name[ind])

ind <- 1:NROW(Comma_names)
Reduce(wrapper, ind, init = List_of_names$name)
# [1] "Fred, Heiko, Franz Jr., Nice LLC, Meike"
# [2] "Digital, Mike, John Sr"                 
# [3] "Svenja, Sven" 

<强>解释

stri_replace_all是一个快速函数,它替换字符串中的所有匹配项。使用Reduce,您可以将函数应用于上一个函数调用的结果。因此,我们将wrapper应用于包含所有名称的列,并替换Comma_names第一行中的字符串。我们现在再次将此字符串提供给wrapper,目的是替换第二行的所有匹配项,依此类推。这段代码应该运行得很快,你不需要parallezie。很想听听您对执行时间的反馈。

<强>基准

只有一个基准,有300万行:

List_of_names <- List_of_names[rep(1:NROW(List_of_names), 1e6), , drop = FALSE]
system.time(invisible(Reduce(wrapper, ind, init = List_of_names$name)))
# user  system elapsed 
# 1.95    0.00    1.96