如何使用使用太长时间处理的巨大匹配关键字文件



我正在使用以下代码,其中我有一个字典文件, Dictionary.txt和一个搜索文本文件 SearchText.csv,并且我正在使用Regex查找和存储匹配的关键字并计数它们并计数它们。

我有一个问题:其中一些文件是成千上万的关键字,并且需要太多时间来处理。我在一个具有300,000个关键字的字典上运行代码,一个小时后没有写一排。

那么,我该怎么做才能减少此过程的运行时间?

import csv
import time
import re
allCities = open('Dictionary.txt', encoding="utf8").readlines()
timestr = time.strftime("%Y-%m-%d-(%H-%M-%S)")
with open('SearchText.csv') as descriptions,open('Result---' + str(timestr) + '.csv', 'w', newline='') as output:
    descriptions_reader = csv.DictReader(descriptions)
    fieldnames = ['Sr_Num', 'Search', 'matched Keywords', 'Total matches']
    output_writer = csv.DictWriter(output, delimiter='|', fieldnames=fieldnames)
    output_writer.writeheader()
    line=0
    for eachRow in descriptions_reader:
        matches = 0
        Sr_Num = eachRow['Sr_Num']
        description = eachRow['Text']
        citiesFound = set()
        for eachcity in allCities:
            eachcity=eachcity.strip()
            if re.search('\b'+eachcity+'\b',description,re.IGNORECASE):
                citiesFound.add(eachcity)
                matches += 1
        if len(citiesFound)==0:
            output_writer.writerow({'Sr_Num': Sr_Num, 'Search': description, 'matched Keywords': " - ", 'Total matches' : matches})
        else:
            output_writer.writerow({'Sr_Num': Sr_Num, 'Search': description, 'matched Keywords': " , ".join(citiesFound), 'Total matches' : matches})
        line += 1
        print(line)
print(" Process Complete ! ")

这是Dictionary.txt的一些行的示例:

les Escaldes
Andorra la Vella
Umm al Qaywayn
Ras al Khaimah
Khawr Fakkn
Dubai
Dibba Al Fujairah
Dibba Al Hisn
Sharjah
Ar Ruways

作为用户Martineau评论,最好配置代码以确定优化在哪里最有效,并测量任何尝试优化的效果。

在没有任何分析数据的情况下,最佳优化候选者可能是这种内部循环:

for eachcity in allCities:
    eachcity=eachcity.strip()
    if re.search('\b'+eachcity+'\b',description,re.IGNORECASE):
        citiesFound.add(eachcity)

在这里,代码在allCities中的每个字符串上调用strip-可以在循环外完成一次,然后为每个城市调用re.search

使用| Metacharacter将所有城市组合到单个正则态度可能更有效,该元时间表示替代匹配。例如模式

r'foo|bar'

将匹配'foo' 'bar'

这是一个简单的示例:

>>> text = 'I am flying from les Escaldas to Dubai via Sharjah on Monday'
>>> cities = ['Sharjah', 'Dubai', 'les Escaldas', 'Ar Ruways']
>>> pattern = r'|'.join(r'b{}b'.format(re.escape(city)) for city in cities)
>>> pattern
'\bSharjah\b|\bDubai\b|\bles Escaldas\b|\bAr Ruways\b'
>>> matches = re.findall(pattern, text)
>>> print(matches)
['les Escaldas', 'Dubai', 'Sharjah']

在每个城市名称上调用re.escape,如果城市名称之一包含一个也是正则表达式的字符,则可以防止更改搜索模式。

将此技术应用于问题:

# We only need to create the regex pattern once,
# so do it outside the loop.
pattern = r'|'.join(r'b{}b'.format(re.escape(city.strip())) for city in allCities)
for eachRow in descriptions_reader:
    Sr_Num = eachRow['Sr_Num']
    description = eachRow['Text']
    citiesFound = set()
    found = re.findall(pattern, description, re.IGNORECASE)
    citiesFound.update(found)
    matches = len(found)

文档中描述了 | metacharacter的行为。

由'|'分开从左到右尝试。当一种模式完全匹配时,该分支被接受。这意味着,一旦A匹配,B也不会进一步测试,即使它会产生更长的整体匹配。

因此,如果有潜在的匹配项是其他候选人的子字符串(例如"迪拜"one_answers"迪拜市"),则候选人必须较早出现在模式中,否则发动机将找到较短的候选人并将其作为比赛归还。

为了防止这种情况,请在创建模式之前按长度顺序排序allCities

allCities.sort(key=len, reverse=True)

或使用sorted如果必须保留allCities的顺序:

pattern = r'|'.join(r'b{}b'.format(re.escape(city.strip())) for
                    city in sorted(allCities, key=len, reverse=True))

您可以使用集合而不是城市名称列表,然后在空间上拆分描述以隔离单词。这可能比正则表达式更快

例如:

...
allCities = open('Dictionary.txt', encoding="utf8").readlines()
citySet = set([city.lower().strip() for city in allCities]
...
    ...
    citiesFound = set([ word for word in description.split(" ") if word.lower() in citySet ])
    ...

最新更新