#python #regex #nlp #findall
Вопрос:
Я использовал tesseract OCR в Python для преобразования PDF-файлов финансового отчета в текстовые файлы, при этом длинные пробелы преобразуются в «;». Таким образом, текстовый файл выглядит довольно красиво, а таблицы выглядят хорошо.
Используя пример, найденный здесь https://cdn.corporatefinanceinstitute.com/assets/AMZN-Cash-Flow.png
The table would be like the following:
Stock-based compensation;2,119;2,975;4,215
Other operating expense, net;155;160;202
Other expense (income), net;250;(20);(292)
Deferred income taxes;81;(246);(29)
...
Хорошо, итак, задача состоит в том, чтобы найти первую сумму после, например, «Компенсация на основе акций» —> 2,119. Я столкнулся как минимум с 3 проблемами с этим:
1-я проблема заключается в том, что у меня всегда есть полный PDF-файл финансового отчета для начала, который содержит, например, 20 страниц и может содержать слово «Компенсация на основе акций» несколько раз в предложениях типа «.. это дата, на которую предоставляется компенсация на основе акций …«.
2-я проблема заключается в том, чтобы найти правильную таблицу в финансовом отчете. Могут существовать различные таблицы меньшего размера, в которых может использоваться «Компенсация на основе запасов». Однако в этом случае допустим, что мы ищем таблицу под названием «Консолидированные отчеты о движении денежных средств», а не, например, «Предполагаемый бюджет на следующий финансовый год» и т. Д.
3-я проблема — это само слово. «Компенсация на основе акций» может варьироваться в разных формах, таких как «Компенсация на основе акций», «Компенсация», «Компенсация на основе акций и другая компенсация» и т. Д. Однако, поскольку мы теперь знаем, что эта «компенсация на основе акций» в той или иной форме в любом случае должна быть в нужной таблице, найти правильную строку не должно быть серьезной проблемой.
Я использовал, например, регулярное выражение, чтобы сузить параметры для нужного слова, которое я ищу, вот так
def find_sum(word_to_look_for):
txt_file = r"fina.txt"
find = pattern
digitals = "d |;" #trying to find if any digits or ";" can be found on the row
with open(txt_file, "r") as in_text:
for row in in_text:
if re.findall(find, row, flags=re.IGNORECASE):
if re.findall(digitals, row):
expense_row = row.split(";")[1].strip("-")
expenses = re.sub("[^d,]", "", expense_row) #if e.g 2.512,00
return expenses
else:
pass
Это решает некоторые проблемы, но в настоящее время я думаю о том, следует ли мне внедрять технологии ML или NLP в этом случае, или это будет достаточно легко решить с помощью регулярных выражений, просто сузив возможные строки с помощью n-количества if-операторов?
Комментарии:
1. Регулярное выражение должно быть достаточно мощным для этого. я предлагаю использовать что-то вроде regex101.com , где вы можете сбросить свой текст и повозиться с регулярным выражением, пока не получите то, что ищете.
2. Вам нужна дополнительная помощь или для вас достаточно опубликованного в данный момент решения? Если вы помните, мои комментарии были такими: «Вы всегда можете указать контекст в регулярном выражении и просто получить первое совпадение, если это все, что вам нужно, например
with open(txt_file, "r") as in_text: m = re.search(r'^s*Stock-baseds compensationW (d[d,]*)', in_text.read(), re.M); if m: print(m.group(1))
«.
Ответ №1:
Используйте слова для поиска с пробелами, регулярное выражение может быть создано в функции с .*
помощью соединителей, чтобы разрешить сопоставление чего-либо между этими словами.
def find_sum(word_to_look_for):
txt_file = r"fina.txt"
find = ".*".join([re.escape(w) for w in word_to_look_for.split()]) r"D*(d (?:[,.]d )*)"
with open(txt_file, "r") as in_text:
for row in in_text:
match = re.search(find, row, flags=re.IGNORECASE)
if match:
return match.group(1)
return ""
<a rel=»noreferrer noopener nofollow» href=»https:///Try it online!» rel=»nofollow noreferrer»>Код на Python:
import re
def find_sum(word_to_look_for):
txt_file = r"fina.txt"
find = ".*".join([re.escape(w) for w in word_to_look_for.split()]) r"D*(d (?:[,.]d )*)"
in_text = ['...', 'XXX', 'Stock-based compensation;2,234.55']
#with open(txt_file, "r") as in_text:
for row in in_text:
match = re.search(find, row, flags=re.IGNORECASE)
if match:
return match.group(1)
return ""
wtlf = "Stock based compensation"
print(find_sum(wtlf))
Результаты: 2,234.55
Регулярное выражение:
Stock.*based.*compensationD*(d (?:[,.]d )*)
ОБЪЯСНЕНИЕ
--------------------------------------------------------------------------------
Stock 'Stock'
--------------------------------------------------------------------------------
.* any character except n (0 or more times
(matching the most amount possible))
--------------------------------------------------------------------------------
based 'based'
--------------------------------------------------------------------------------
.* any character except n (0 or more times
(matching the most amount possible))
--------------------------------------------------------------------------------
compensation 'compensation'
--------------------------------------------------------------------------------
D* non-digits (all but 0-9) (0 or more times
(matching the most amount possible))
--------------------------------------------------------------------------------
( group and capture to 1:
--------------------------------------------------------------------------------
d digits (0-9) (1 or more times (matching
the most amount possible))
--------------------------------------------------------------------------------
(?: group, but do not capture (0 or more
times (matching the most amount
possible)):
--------------------------------------------------------------------------------
[,.] any character of: ',', '.'
--------------------------------------------------------------------------------
d digits (0-9) (1 or more times
(matching the most amount possible))
--------------------------------------------------------------------------------
)* end of grouping
--------------------------------------------------------------------------------
) end of 1
Ответ №2:
Спасибо за ответы, это
find = ".*".join([re.escape(w) for w in word_to_look_for.split()]) r"D*(d (?:[,.]d )*)"
сработало хорошо.
Однако у меня все еще есть проблема, поскольку в моих итоговых отчетах есть две почти идентичные таблицы: одна для составления бюджета, а другая для отображения цифр за прошлый год. Обе эти таблицы отмечены четким заголовком в верхней части страницы.
С этим ответом выше я получаю оба этих совпадения, поскольку сама строка выглядит похожей, но мне нужно только другое совпадение.
Я добавил текст, указывающий начало каждой страницы в процессе распознавания текста, а затем искал нужную страницу, включая нужную таблицу, но, похоже, это сложнее, чем предполагалось. Есть идеи для этого?
Комментарии:
1. Извините, что не так с моим ответом? Почему минус?