Импорт нескольких счетов-фактур (.PDF) в R. Преобразование их из строк в тиббл

Итак, я делаю проект, в котором мне нужно загрузить большое количество файлов .pdf в R. Эта часть в некоторой степени покрыта. Проблема в том, что при импорте PDF-файлов в R каждая строка представляет собой строку. Не вся информация в строке актуальна. А в некоторых случаях информация отсутствует. Итак, я хочу выбрать нужную мне информацию и поместить ее в тибет для дальнейшего анализа.

Импорт PDF-файлов выполняется с помощью pdftools. Работает, подсказки и подсказки приветствуются.

invoice_pdfs = list.files(pattern="*.pdf")                   # gather all the .pdf in current wd. 

invoice_list <- map(invoice_pdfs, .f = function(invoices){   # Using the purrr::map function .
                            pdf_text(invoices) %>%           # extracting text from listed pdf file(s)            
                            readr::read_lines() %>%          # read all text from pdf
                            str_squish() %>%                 # clear all white space in text.
                            str_to_lower                     # convert string to lower case
                         })

воспроизводимый пример:

invoice_example <- c("invoice",                                                                         
"to: rade ris",                                                                    
"cane nompany",                                                                   
"kakber street 23d",                                                               
"nork wey",                                                                        
"+223 (0)56 015 6542",                                                             
"invoice id: 85600023",                                                            
"date reference product product reference weigth amount",                           
"01-02-2016 840000023 product a 24.45.6 de6583621 14.900 kg a 50 per tonne 745,00",
"07-02-2016 840000048 product b 24.45.7 qf8463641 19.000 kg a 50 per tonne 950,00", 
"03-02-2016 840000032 product b 24.34.2 qf8463641 4.000 kg per tonne 250,00",      
"02-02-2016 840000027 ke7801465 1.780 kg per tonne 89,00",                         
"subtotal 2.034,00",                                                               
"sales tax 183,06",                                                                
"total 2.217,06")

Итак, вот где начинается проблема. Я пробовал использовать stringr и rebus для выделения определенных частей текста. Я сделал следующую функцию для поиска в документе определенной строки, она возвращает номер строки:

word_finder <- function(x, findWord){
                word_hit <- x %>%                           # temp for storing TRUE or FALSE
                  str_detect(pattern = fixed(findWord)) 
                 which(word_hit == TRUE)                    # give rownumber if TRUE
                }

И следующие шаблоны поиска:

detect_date <- dgt(2) %R% "-" %R% dgt(2) %R% "-" %R% dgt(2)
detect_money <-  optional(DIGIT) %R% optional(".") %R% one_or_more(DIGIT) %R% "," %R% dgt(2) 
detect_invoice_num <- str_trim(SPC %R% dgt(8) %R% optional(SPC))

Следующим шагом должно быть создание тиббла (или фрейма данных) с именами столбцов c("date", "reference", "product", "product reference", "weight", "amount") Я также пробовал сделать тиббл всей invoice_example. Проблема заключается в том, что в некоторых полях отсутствует информация, а имена столбцов не соответствуют соответствующему значению. .

Поэтому я хотел бы создать некоторую функцию, которая использует шаблон поиска и помещает это конкретное значение в определенный столбец. Я понятия не имею, как это сделать. Или, может быть, мне следует разобраться с этим совсем по-другому?

конечный результат должен быть примерно таким.

воспроизводимый пример:

invoice_nr <- c("85600023", "85600023", "85600023", "85600023" )
date <- c( "01-02-2016", "07-02-2016", "03-02-2016", "02-02-2016")
reference <- c( "840000023", "840000048", "840000032", "840000027")
product_id <- c( "de6583621", "qf8463641", "qf8463641", "ke7801465")
weight <- c("14.900", "19.000", "4.000", "1.780")
amount <- c("745.00", "950.00", "250.00", "89.00")

example_tibble <- tibble(invoice_nr, date, reference, product_id, weight, amount)

Результат:

# A tibble: 4 x 6
  invoice_nr date       reference product_id weight amount
  <chr>      <chr>      <chr>     <chr>      <chr>  <chr> 
1 85600023   01-02-2016 840000023 de6583621  14.900 745.00
2 85600023   07-02-2016 840000048 qf8463641  19.000 950.00
3 85600023   03-02-2016 840000032 qf8463641  4.000  250.00
4 85600023   02-02-2016 840000027 ke7801465  1.780  89.00 

Мы будем благодарны за любые предлагаемые способы решения этой проблемы!


person JHJH    schedule 01.04.2019    source источник


Ответы (2)


На самом деле вы можете использовать функции library(stringr) для достижения своей цели (я пропустил часть rebus, так как в любом случае кажется, что это просто помощник для создания регулярного выражения, что я сделал вручную):

library(tidyverse)
parse_invoice <- function(in_text) {
  ## define regex, some assumptions:
  ## product id is 2 lower characters followed by 7 digits
  ## weight is some digits with a dot followed by kg
  ## amount is some digits at the end with a comma
  all_regex <- list(date       = "\\d{2}-\\d{2}-\\d{4}",
                    reference  = "\\d{9}",
                    product_id = "[a-z]{2}\\d{7}",
                    weight     = "\\d+\\.\\d+ kg",
                    amount     = "\\d+,\\d+$")
  ## look only at lines where there is invoice data
  rel_lines <- str_subset(in_text, all_regex$date)
  ## extract the pieces from the regex
  ret <- as_tibble(map(all_regex, str_extract, string = rel_lines))
  ## clean up the data
  ret %>%
    mutate(invoice_nr = str_extract(str_subset(in_text, "invoice id:"), "\\d{8}"),
           date       = as.Date(date, "%d-%m-%Y"),
           weight     = as.numeric(str_replace(weight, "(\\d+.\\d+) kg", "\\1")),
           amount     = as.numeric(str_replace(amount, ",", "."))
    ) %>%
    select(invoice_nr,
           date,
           reference,
           product_id,
           weight,
           amount)

}
str(parse_invoice(invoice_example))
# Classes ‘tbl_df’, ‘tbl’ and 'data.frame':       4 obs. of  6 variables:
#  $ invoice_nr: chr  "85600023" "85600023" "85600023" "85600023"
#  $ date      : Date, format: "2016-02-01" "2016-02-07" ...
#  $ reference : chr  "840000023" "840000048" "840000032" "840000027"
#  $ product_id: chr  "de6583621" "qf8463641" "qf8463641" "ke7801465"
#  $ weight    : num  14.9 19 4 1.78
#  $ amount    : num  745 950 250 89
person thothal    schedule 01.04.2019
comment
rebus действительно помощник; это более понятный для человека код, если можно так выразиться. Легче подать заявку на такого человека, как я, у которого нет опыта работы с регулярными выражениями. Код @thothal показывает мне свою ценность для изучения, а регулярное выражение, кажется, дает больше контроля при выборе правильных частей строки . Спасибо, что показали мне и предоставили этот ответ, это именно то, что я искал. - person JHJH; 02.04.2019

Поскольку я не знаком с rebus, я переписал ваш код. Предполагая, что счета-фактуры, по крайней мере, в некоторой степени структурированы так же, как я мог бы создать tibble из вашего примера. Вам просто нужно применить это ко всему вашему списку, а затем purrr::reduce к большому tibble:

df <- tibble(date=na.omit(str_extract(invoice_example,"\\d{2}-\\d{2}-\\d{4}")))
df %>% mutate(invoice_nr=na.omit(sub("invoice id: ","",str_extract(invoice_example,"invoice id: [0-9]+"))),
              reference=na.omit(sub("\\d{2}-\\d{2}-\\d{4} ","",str_extract(invoice_example,"\\d{2}-\\d{2}-\\d{4} \\d{9}"))),
              product_id=na.omit(str_extract(invoice_example,"[:lower:]{2}\\d{7}")),
              weight=na.omit(sub(" kg","",str_extract(invoice_example,"[0-9\\.]+ kg"))),
              amount=na.omit(sub("tonne ","",str_extract(invoice_example,"tonne [0-9,]+"))))
person Julian_Hn    schedule 01.04.2019