Как использовать огромный соответствующий файл ключевых слов, который обрабатывается слишком долго? - PullRequest
0 голосов
/ 24 февраля 2019

Я использую следующий код, в котором у меня есть файл словаря, Dictionary.txt, и текстовый файл поиска, SearchText.csv, и я использую регулярное выражение, чтобы найти и сохранить подходящие ключевые слова и сосчитать их.

У меня проблема: некоторые файлы содержат тысячи или сотни тысяч ключевых слов, и обработка их занимает слишком много времени.Я запускаю код в одном словаре, который содержит 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

Ответы [ 2 ]

0 голосов
/ 25 февраля 2019

Как комментирует пользователь Martineau, лучше всего профилировать код, чтобы определить, где оптимизация будет наиболее эффективной, и измерить эффект от любых попыток оптимизации.

При отсутствии каких-либо данных профилирования лучший кандидат наоптимизация, вероятно, будет следующим внутренним циклом:

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

Здесь код вызывает strip для каждой строки в allCities - что-то, что можно сделать только один раз, вне цикла, затем вызвать re.search для каждого города.

Может оказаться более эффективным объединить все города в одно регулярное выражение, используя метасимвол |, который обозначает альтернативные совпадения.Например, шаблон

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)

Поведение метасимвола | описано в документации .

RE, разделенные '|'пробуются слева направо.Когда один шаблон полностью совпадает, эта ветвь принимается.Это означает, что после совпадения A, B больше не будет проверяться, даже если это приведет к большему совпадению.

Таким образом, если есть потенциальные совпадения, которые являются подстроками других кандидатов - таких как «Дубай» и «Дубай Сити» - более длинный кандидат должен появиться раньше в шаблоне, в противном случае движок найдет более короткий ивернуть его как совпадение.

Чтобы предотвратить это, перед созданием шаблона сортируйте 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))
0 голосов
/ 25 февраля 2019

Вы можете использовать набор вместо списка названий городов и разделить описание на пробелы, чтобы выделить слова.Это может работать быстрее, чем регулярное выражение

Например:

...
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 ])
    ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...