【问题标题】:Search and replace multiple strings in list of strings: improve R code在字符串列表中搜索和替换多个字符串:改进 R 代码
【发布时间】:2016-03-04 09:07:46
【问题描述】:

我正在寻找 R 中以下问题的简化解决方案:我有一个用逗号分隔的名称列表 - 但是,其中一些名称中也包含逗号。为了分隔名称,我想先用逗号替换所有名称,然后用逗号分隔。我的问题是我有大约 26 000 个字符串,每个字符串都有几个名字,我有一个大约 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 循环的结果是一个列表,但如果我尝试保存列表或替换原始数据框中的列它需要永远。如何更改我的代码以使其更快?

感谢所有阅读本文并能够提供帮助的人!

【问题讨论】:

标签: regex r string


【解决方案1】:

原则

您可以使用包stringi 中的Reducestri_replace_all 的组合。

代码

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,目的是替换所有出现的第二行,依此类推。这段代码应该运行得相当快,你不需要并行。很想听听您对执行时间的反馈。

基准测试

只是一个包含 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

【讨论】:

  • 太棒了!非常感谢!在我的 2 个 foreach 循环中:# user system elapsed #a 132.39 106.37 728.22 而你的解决方案只用了不到一秒钟!再次感谢!这么好的答案!
  • Ditch wrapper & Reduce & 只是这样做:List_of_names$name = stri_replace_all_fixed(List_of_names$name, Comma_names[,1], Comma_names[,2], vectorize_all=T))
  • @webb 实际上,这是行不通的,因为这样您将替换第一行中的第一个名称,第二行中的第二个名称,依此类推。这也是一个巧合,该行没有对当前示例发出警告,因为两个数据框都包含 3 行。试试stri_replace_all_fixed(List_of_names$name[c(1, 2, 2, 3)], Comma_names[,1], Comma_names[,2], vectorize_all = TRUE),你会看到警告。
猜你喜欢
  • 2018-01-02
  • 2015-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-02
相关资源
最近更新 更多