长时间的正则表达式替换需要多次通过才能完成-为什么?

时间:2019-02-13 17:21:09

标签: python regex data-analysis

我已经在网站上找到了该问题的答案,并用尽了Google的精力和耐心尝试自己回答的问题,这里就是。如果这是骗子,很高兴指出答案。

所以我有一个很长的正则表达式-没什么复杂的,只是一堆简单的条件而已。我正在使用它从我从新闻文章数据中提取的命名实体的开头和结尾中删除管道词。用例是,许多名称中都包含这些简短的单词(请考虑疾病控制和预防中心),但是我想删除出现在名称开头或结尾的单词。例如,出于明显的原因,我不希望“疾病控制中心”与“ 疾病控制中心”的计数不同。

我使用以下代码(file here)在Python 3.7.2中的大量(> 1M)命名实体列表上使用了此正则表达式字符串:

with open('pnames.csv','r') as f:
    named_entities = f.read().splitlines()

print(len([i for i in named_entities if i == 'the wall street journal']))
# 146

short_words = "^and\s|\sand$|^at\s|\sat$|^by\s|\sby$|^for\s|\sfor$|^in\s|\sin$|^of\s|\sof$|^on\s|\son$|^the\s|\sthe$|^to\s|\sto$"

cleaned_entities = [re.sub(short_words,"",i) 
for i 
in named_entities]

print(len([i for i in cleaned_entities 
if i == 'the wall street journal']))
# 80 (huh, should be 0. Let me try again...)

cleaned_entities2 = [re.sub(short_words,"",i) 
for i 
in cleaned_entities]

print(len([i for i in cleaned_entities2 
if i == 'the wall street journal']))
# 1 (better, but still unexpected. One more time...)

cleaned_entities3 = [re.sub(short_words,"",i) 
for i 
in cleaned_entities2]

print(len([i for i in cleaned_entities3 
if i == 'the wall street journal']))
# 0 (this is what I expected on the first run!)

我的问题是,为什么正则表达式不能一次删除所有匹配的子字符串?即len([i for i in cleaned_entities if i == 'the wall street journal'])为什么不等于0?为什么需要多次运行才能完成工作?

我尝试过的事情:

  • 重新启动Spyder
  • 在Python 3.7.2,Python 3.6.2中运行相同的代码,并在R 3.4.2中运行等效的代码(Python给出了完全相同的结果,而R给出了不同的数字,但是我仍然不得不多次运行它才能达到零)
  • 仅在与正则表达式匹配的子字符串上运行代码(结果相同)
  • 仅在等于“华尔街日报”的字符串上运行代码(一次通过)
  • 用上面的代码替换正则表达式"^the "(一次性修复所有匹配项)

是的,任何想法都会有所帮助。

1 个答案:

答案 0 :(得分:1)

您的正则表达式每次通过只会删除一层不需要的单词。所以如果你有一个 句子为:

and and at by in of the the wall street journal at the by on the

它需要很多遍才能完全删除所有内容。

可以重新排列该表达式以使用+来表示一个或多个事件,如下所示:

import re

with open('pnames2.csv','r') as f:
    named_entities = f.read().splitlines()

print(len([i for i in named_entities if i == 'the wall street journal']))
# 146

short_words = "^((and|at|by|for|in|of|on|the|to)\s)+|(\s(and|at|by|for|in|of|on|the|to))+$"
re_sw = re.compile(short_words)

cleaned_entities = [re_sw.sub("", i) for i in named_entities]

print(len([i for i in cleaned_entities if i == 'the wall street journal']))
# 0

可以通过预编译正则表达式来稍微加快该过程。如果将它应用于 整个文件,而不是逐行应用。