Применение регулярок к большому тексту
Есть .csv файл с регулярками, а также несколько больших текстов. Мне нужно посчитать число вхождений в текст нужных слов по этим регуляркам.
for strings in file:
temp = reg_file.drop(np.where(reg_file['section'] != "Основные элементы")[0])
for index, row in temp.iterrows():
t1 = t1 + len(re.findall(row.loc['regex'], strings))
temp = reg_file.drop(np.where(reg_file['section'] != "Элементы2")[0])
for index, row in temp.iterrows():
t2 = t2 + len(re.findall(row.loc['regex'], strings))
Т.е. беру пандами из файла нужный комплект регулярок (всего их 4) и считаю количество вхождений через findall в каждой строке текста на каждый regexp. Учитывая, что и регэкспов много (около 10 каждого типа), если это работало на мелком тексте, то для большого время обработки неприемлемо большое. Как можно это выполнить быстрее?
Ответы (1 шт):
Не очень много данных приведено в вопросе, кое-что пришлось додумывать.
В глаза бросается повторяющаяся выборка из датафрейма (я думаю что это датафрейм). Повторять эту выборку многократно не нужно, её можно вынести за пределы цикла.
re.findallкомпилирует регулярные выражения снова и снова. Тоже можно вынести из цикла.
Функция get_regexes извлекает из датафрейма регулярные выражения и компилирует их. Функция count_regexes применяет выражения к строкам:
def get_regexes(reg_file, section):
return tuple(map(re.compile, reg_file[reg_file['section'] == section]['regex']))
def count_regexes(str_file, reg_file):
regexes1 = get_regexes(reg_file, 'Основные элементы')
regexes2 = get_regexes(reg_file, 'Элементы2')
t1 = 0
t2 = 0
for s in str_file:
t1 += sum(len(e.findall(s)) for e in regexes1)
t2 += sum(len(e.findall(s)) for e in regexes2)
return t1, t2
Ещё одна возможная оптимизация состоит в объединении различных регулярных выражений в одно. Не уверен что будет выигрыш, но можно попробовать. Но должен предупредить что может измениться результат если различные регулярные выражения могут находить перекрывающиеся фрагменты в тексте:
# поиск по отдельным фрагментам найдёт два вхождения:
re.findall('ab', 'aba') # одно
re.findall('ba', 'aba') # ещё одно
# поиск по общему выражению найдёт только одно вхождение:
re.findall('ab|ba', 'aba') # только одно
Если это не проблема, то ниже вариант с общим регулярным выражением. get_compound_regex собирает регулярки в единое выражение и компилирует его. count_regexes теперь содержит на один цикл меньше:
def get_compound_regex(reg_file, section):
return re.compile('|'.join(
'(?:{})'.format(e) for e in reg_file[reg_file['section'] == section]['regex']
))
def count_regexes(str_file, reg_file):
e1 = get_compound_regex(reg_file, 'Основные элементы')
e2 = get_compound_regex(reg_file, 'Элементы2')
t1 = 0
t2 = 0
for s in str_file:
t1 += len(e1.findall(s))
t2 += len(e2.findall(s))
return t1, t2
Если тексты помещаются в память, то можно убрать цикл по строкам. В примере добавлен код, который собирает все строки из str_file в единый текст, к которому затем применяются регулярные выражения:
def count_regexes(str_file, reg_file):
text = '\n'.join(str_file)
e1 = get_compound_regex(reg_file, 'Основные элементы')
e2 = get_compound_regex(reg_file, 'Элементы2')
t1 = len(e1.findall(text))
t2 = len(e2.findall(text))
return t1, t2