Поиск правильного слова и строки в текстовом файле финансового отчета

#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. Извините, что не так с моим ответом? Почему минус?