【问题标题】:Regex replace is taking time for millions of documents, how to make it faster?正则表达式替换数百万个文档需要时间,如何使其更快?
【发布时间】:2017-05-25 10:38:56
【问题描述】:

我有如下文件:

documents = [
    "I work on c programing.",
    "I work on c coding.",
]

我有同义词文件,例如:

synonyms = {
    "c programing": "c programing",
    "c coding": "c programing"
}

我想替换我编写此代码的所有同义词:

# added code to pre-compile all regex to save compilation time. credits alec_djinn

compiled_dict = {}
for value in synonyms:
    compiled_dict[value] = re.compile(r'\b' + re.escape(value) + r'\b')

for doc in documents:
    document = doc
    for value in compiled_dict:
        lowercase = compiled_dict[value]
        document = lowercase.sub(synonyms[value], document)
    print(document)

输出:

I work on c programing.
I work on c programing.

但是由于文档的数量是几百万,同义词的数量是成千上万,所以这段代码完成的预期时间大约是 10 天。

有更快的方法吗?

PS:输出我想训练 word2vec 模型。

非常感谢任何帮助。我正在考虑编写一些 cpython 代码并将其放入并行线程中。

【问题讨论】:

  • skills_mapping[val] 指的是什么?
  • @AmeyDahale 更正了代码。感谢您指出..
  • valvalue 不一样。
  • 让我们详细说明:像c++ 这样的关键字应该放在on 单词之后和字符串的末尾吗?如果格式是固定的,我可以提出一些解决方案
  • 密切相关:stackoverflow.com/a/48600345/4909087 向下滚动到 unutbu 的答案,详细说明了完全相同的事情。

标签: python parallel-processing word2vec cpython


【解决方案1】:

我以前做过这样的字符串替换工作,也用于在非常大的文本语料库上训练 word2vec 模型。当要替换的术语(您的“同义词”)非常多时,使用Aho-Corasick algorithm 进行字符串替换而不是循环多个单个字符串替换是有意义的。您可以查看我的 fsed 实用程序(用 Python 编写),它可能对您有用。

【讨论】:

【解决方案2】:

我会采取的步骤:

  1. 创建一个没有正则表达式的直接算法。也许事件直接根据同义词生成代码。
  2. 在文档上对工作进行分区,以便您可以直接在 N/x 个文档上运行此算法,并进行拆分以充分利用并行资源(例如,如果您有 4 个内核,则 x = 4)并使用 @987654321 运行@(注意:避免使用线程)
  3. 如果您在多个节点上拥有资源(例如使用 spark),则可以使用库来帮助并行运行它。

【讨论】:

  • 还要在此处添加警告:关于 #2 - 不要为此使用线程,无论您有多少内核,它都会比单个线程运行得慢。
  • 我根据@wildwilhelm 的推荐github.com/vi3k6i5/synonym-extractor 编写了一个实现。哪个运行得更快。感谢您的所有帮助。
【解决方案3】:

我会预编译所有正则表达式字符串并将它们放入一个字典中。通过这种方式,您可以避免一遍又一遍地编译相同的值。这样可以节省很多时间。

你的主循环会变成:

compiled_dict = {}
for value in synonyms:
        compiled_dict[value] = re.compile(r'\b' + re.escape(value) + r'\b')


for document in documents:
    for value in synonyms:
        lowercase = compiled_dict[value]
        document = lowercase.sub(synonyms[value], document)

【讨论】:

  • 我已经做到了。但为了简化问题,我将其从代码中删除。但有效的一点。节省时间。
  • 我根据@wildwilhelm 的推荐github.com/vi3k6i5/synonym-extractor 编写了一个实现。哪个运行得更快。感谢您的所有帮助。
【解决方案4】:

首先,您提供的代码在c++. 中找不到c++,因为终止点与\b 不匹配。你可以使用(?!\w),而不是\b

假设大多数同义词都是针对单个单词(没有空格和特殊字符),您可以通过查看文档中的每个实际单词并在它出现在同义词列表中时替换它来获得一些优化。

所有剩余的同义词键(希望是少数)然后可以像您一样处理,但首先将键转换为它们的正则表达式等效项。

这就是它的样子:

import re

# Get those synonyms that are not single words and turn them into regexes:
# Don't use \b to end a pattern; just require that no \w should follow 
complex_synonyms = [(r'\b' + re.escape(key) + r'(?!\w)', synonyms[key]) for key in synonyms if not re.match(r'[\w+]+$', key)]

for i, document in enumerate(documents):
    # Deal with the easy cases (words) in one go, by checking each word in the document
    document = re.sub(r'[\w+]+', lambda word: synonyms[word[0]] if word[0] in synonyms else word[0], document)
    # Replace the remaining synonyms by using regular expressions
    for find, repl in complex_synonyms:
        document = re.sub(find, repl, document)
    # Store the result back into the document
    documents[i] = document

【讨论】:

  • 更新了我的示例以使其正常工作。我为此编写了一个自定义示例。问题不在于正则表达式,而是术语的缩放匹配并替换它。
  • 当然,缩放是我从你的问题中得到的。你试过这种方法吗?如果您的大多数同义词键都是单词(没有空格,没有标点符号),我希望这会更快。
  • 所以我明白一个词的同义词可以这样优化。然后我猜 2 词的同义词可以通过 2 克优化,3 词的同义词可以通过 3 克优化。那部分很酷。但不会像我需要的那样快。我还是会尝试的。使用良好的标记器。
  • 我根据@wildwilhelm 的推荐github.com/vi3k6i5/synonym-extractor 编写了一个实现。哪个运行得更快。感谢您的所有帮助。
猜你喜欢
  • 2021-12-06
  • 2017-08-02
  • 2011-05-27
  • 2011-11-09
  • 2021-09-17
  • 2015-09-16
  • 1970-01-01
  • 2014-08-23
相关资源
最近更新 更多