Я пытаюсь использовать пакет pdftools для извлечения таблицы данных из PDF. Мой исходный файл находится здесь: https: // hypo. org / app / uploads / sites / 2/2021/11 / HYPOSTAT-2021_vdef.pdf. Скажем, я хочу извлечь данные из Таблицы 20 на странице 170 (Изменение номинальной цены дома)

Я использую следующий код:

install.packages("pdftools")
library(pdftools)

report <- pdftools::pdf_data("https://hypo.org/app/uploads/sites/2/2021/11/HYPOSTAT-2021_vdef.pdf")

tab20 <- as.data.frame(report[170])

Чтобы получить правильную таблицу, мне пришлось вручную указать, что я хочу извлечь 170-й элемент списка (как таблица на странице 170). Если в следующем году в отчет добавится новая страница с таблицей, мне придется модифицировать код для извлечения 171-го элемента. Есть ли способ сделать это более автоматизированным способом?

По сути, мне нужно найти элемент списка, который содержит строку «Изменение номинальной цены дома». Есть предложения, как это сделать?

2
Chris 26 Ноя 2021 в 16:28
Какой формат вам нужен для data.frame? В загружаемом вами data.frame есть строка, но таблица имеет совершенно другой формат, чем то, что отображается в pdf.
 – 
Gowachin
26 Ноя 2021 в 16:34
1
В итоге хотелось бы, чтобы ДФ выглядел как таблица в отчете. Я могу продолжить работу над его очисткой и преобразованием в правильный формат с помощью dplyr и т. Д. Но мне интересно, как получить DF в первую очередь, не указывая вручную "170" в качестве параметра
 – 
Chris
26 Ноя 2021 в 16:41

2 ответа

Лучший ответ

Другое решение, основанное на purrr::map_lgl:

library(tidyverse)
library(pdftools)

report <- pdftools::pdf_data("https://hypo.org/app/uploads/sites/2/2021/11/HYPOSTAT-2021_vdef.pdf")

map_lgl(
  report,
  ~ str_detect(
    str_c(.x$text, collapse = " "),
    "Change in Nominal house price")) %>% report[.]

#> [[1]]
#> # A tibble: 606 × 6
#>    width height     x     y space text       
#>    <int>  <int> <int> <int> <lgl> <chr>      
#>  1    59     14    39    38 TRUE  STATISTICAL
#>  2    35     14   102    38 FALSE TABLES     
#>  3    25     26    33    81 TRUE  20.        
#>  4    60     26    65    81 TRUE  Change     
#>  5    15     26   129    81 TRUE  in         
#>  6    67     26   149    81 TRUE  Nominal    
#>  7    47     26   221    81 TRUE  house      
#>  8    41     26   272    81 FALSE price      
#>  9    30     14    65   103 TRUE  Annual     
#> 10     7     14    98   103 TRUE  %          
#> # … with 596 more rows
0
Paul Smith 26 Ноя 2021 в 17:30

Вы можете найти строку с соответствующим рисунком. Используя несколько фильтров, вы можете собрать эту единственную таблицу.

table <- report[grepl('Change', report) & grepl('Nominal', report) &
                grepl('house', report)]

Я думаю, может работать более тонкое регулярное выражение. Также это работает только потому, что ни одна другая таблица не имеет такого же заголовка, но было бы лучше проверить, возвращает ли она только значение, подобное ниже:

place <- grepl('Change', report) &
            grepl('Nominal', report) &
            grepl('house', report)
if(sum(place) != 1){
  stop("There is not only one pattern that match. Adjust pattern.")
} else {
  table <- report[place]
}

РЕДАКТИРОВАТЬ: Чтобы ускорить это, вам лучше использовать решение @Paul Smith. Я адаптировал его с помощью grepl и lapply, и он быстрее !!! Однако вам нужно убедиться, что заголовок вообще не меняется.

system.time(
place <- unlist(lapply(report, function(x) grepl("Change in Nominal house price",
                                        paste(x$text, collapse = " "))))
)
#        user      system       spent 
#        0.07        0.00        0.08 
system.time(
place <- grepl('Change', report) & grepl('Nominal', report) &
            grepl('house', report)
)
#        user      system       spent 
#        1.99        0.01        2.03 

1
Gowachin 26 Ноя 2021 в 17:51
Я не могу найти способ использовать несколько слов без трехкратного вызова функции (что может занять много времени с большим набором данных). Любая идея ускорить это приветствуется!
 – 
Gowachin
26 Ноя 2021 в 17:40