寻找一种优化的方式来替换长文档中的列表模式
Looking for an optimized way of replacing list patterns in long documents
使用 tm 包,我有一个包含 10,900 个文档的语料库 (docs)。
docs = Corpus(VectorSource(abstracts$abstract))
我还有一个术语列表 (termslist) 及其所有同义词和不同的拼写。我用它来将每个同义词或拼写转换为一个术语。
Term, Synonyms
term1, synonym1
term1, synonym2
term1, synonym3
term2, synonym1
... etc
我现在的做法是遍历所有文档,另一个嵌套者循环遍历所有术语以查找和替换。
for (s in 1:length(docs)){
for (i in 1:nrow(termslist)){
docs[[s]]$content<-gsub(termslist[i,2], termslist[i,1], docs[[s]])
}
print(s)
}
目前,对于一个文档(在 termslist 中有大约 1000 行),这需要一秒钟,这意味着 10,900 秒,大约 6 小时!
在 tm 包或 R 中一般有更优化的方法吗?
更新:
mathematical.coffee的回答实际上很有帮助。我不得不重新创建一个 table 行,第二列是用 '|' 分隔的同义词,然后循环遍历它们。现在花费的时间比以前少了很多。
**[乱七八糟]创建新代码table:
newtermslist<-list()
authname<-unique(termslist[,1])
newtermslist<- cbind(newtermslist,authname)
syns<-list()
for (i in seq(authname)){
syns<- rbind(syns,
paste0('(',
paste(termslist[which(termslist[,1]==authname[i]),2],collapse='|')
, ')')
)
}
newtermslist<-cbind(newtermslist,syns)
newtermslist<-cbind(unlist(newtermslist[,1]),unlist(newtermslist[,2]))
我认为当你希望执行多次替换时,这可能是唯一的方法(即顺序地,将替换的输出保存为下一次替换的输入)。
但是,您可能会尝试获得一些速度(您必须进行一些基准测试才能进行比较):
- 使用
fixed=T
(因为您的同义词不是正则表达式而是字面拼写),useBytes=T
(**请参阅 ?gsub
- 如果您有多字节语言环境,这可能是也可能不是好主意)。或者
- 压缩您的术语列表 - 如果
blue
有同义词 cerulean
、cobalt
和 sky
,那么您的正则表达式可能是 (cerulean|cobalt|sky)
替换 blue
,这样 blue
的所有同义词都在一次迭代中被替换,而不是在 3 个单独的迭代中被替换。为此,您需要预处理您的术语列表 - 例如newtermslist <- ddply(terms, .(term), summarize, regex=paste0('(', paste(synonym, collapse='|'), ')'))
然后执行当前循环。您将拥有 fixed=F
(默认值,即使用正则表达式)。
- 另请参阅
?tm_map
和 ?content_transformer
。我不确定这些是否会加快速度,但您可以试试。
(重新基准测试 - 尝试 library(rbenchmark); benchmark(expression1, expression2, ...)
,或者好的 ol' system.time
用于计时,Rprof
用于分析)
我在这里回答了我自己的问题,这是在通过一个可以并行处理事情的并行化解决方案之后得出的。它应该 运行 代码更快,但我还没有比较这两个解决方案。
library(doParallel)
library(foreach)
cl<-makeCluster(detectCores())
registerDoParallel(cl)
system.time({ # this one to print how long it takes after it evaluate the expression
foreach(s=1:length(docs)) %:% foreach(i=1:nrow(newtermslist)) %dopar% {
docs[[s]]$content<-gsub(newtermslist[i,2], newtermslist[i,1], docs[[s]]$content)
}
})
stopCluster(cl)
使用 tm 包,我有一个包含 10,900 个文档的语料库 (docs)。
docs = Corpus(VectorSource(abstracts$abstract))
我还有一个术语列表 (termslist) 及其所有同义词和不同的拼写。我用它来将每个同义词或拼写转换为一个术语。
Term, Synonyms
term1, synonym1
term1, synonym2
term1, synonym3
term2, synonym1
... etc
我现在的做法是遍历所有文档,另一个嵌套者循环遍历所有术语以查找和替换。
for (s in 1:length(docs)){
for (i in 1:nrow(termslist)){
docs[[s]]$content<-gsub(termslist[i,2], termslist[i,1], docs[[s]])
}
print(s)
}
目前,对于一个文档(在 termslist 中有大约 1000 行),这需要一秒钟,这意味着 10,900 秒,大约 6 小时!
在 tm 包或 R 中一般有更优化的方法吗?
更新:
mathematical.coffee的回答实际上很有帮助。我不得不重新创建一个 table 行,第二列是用 '|' 分隔的同义词,然后循环遍历它们。现在花费的时间比以前少了很多。
**[乱七八糟]创建新代码table:
newtermslist<-list()
authname<-unique(termslist[,1])
newtermslist<- cbind(newtermslist,authname)
syns<-list()
for (i in seq(authname)){
syns<- rbind(syns,
paste0('(',
paste(termslist[which(termslist[,1]==authname[i]),2],collapse='|')
, ')')
)
}
newtermslist<-cbind(newtermslist,syns)
newtermslist<-cbind(unlist(newtermslist[,1]),unlist(newtermslist[,2]))
我认为当你希望执行多次替换时,这可能是唯一的方法(即顺序地,将替换的输出保存为下一次替换的输入)。
但是,您可能会尝试获得一些速度(您必须进行一些基准测试才能进行比较):
- 使用
fixed=T
(因为您的同义词不是正则表达式而是字面拼写),useBytes=T
(**请参阅?gsub
- 如果您有多字节语言环境,这可能是也可能不是好主意)。或者 - 压缩您的术语列表 - 如果
blue
有同义词cerulean
、cobalt
和sky
,那么您的正则表达式可能是(cerulean|cobalt|sky)
替换blue
,这样blue
的所有同义词都在一次迭代中被替换,而不是在 3 个单独的迭代中被替换。为此,您需要预处理您的术语列表 - 例如newtermslist <- ddply(terms, .(term), summarize, regex=paste0('(', paste(synonym, collapse='|'), ')'))
然后执行当前循环。您将拥有fixed=F
(默认值,即使用正则表达式)。 - 另请参阅
?tm_map
和?content_transformer
。我不确定这些是否会加快速度,但您可以试试。
(重新基准测试 - 尝试 library(rbenchmark); benchmark(expression1, expression2, ...)
,或者好的 ol' system.time
用于计时,Rprof
用于分析)
我在这里回答了我自己的问题,这是在通过一个可以并行处理事情的并行化解决方案之后得出的。它应该 运行 代码更快,但我还没有比较这两个解决方案。
library(doParallel)
library(foreach)
cl<-makeCluster(detectCores())
registerDoParallel(cl)
system.time({ # this one to print how long it takes after it evaluate the expression
foreach(s=1:length(docs)) %:% foreach(i=1:nrow(newtermslist)) %dopar% {
docs[[s]]$content<-gsub(newtermslist[i,2], newtermslist[i,1], docs[[s]]$content)
}
})
stopCluster(cl)