Importar tabela de dentro um arquivo PDF para o R
É muito comum a necessidade de extrairmos algum dado de um PDF, sejam dados brutos ou presentes em tabelas. Não raras vezes, dá errado. O pacote tabulizer
é um pacote da linguagem R, disponível no CRAN desde 2018. O pessoal da Curso-R fez um post sobre este pacote também. Sugiro conferirem, até para entenderem a instalação. O pacote utiliza a biblioteca `Tabula` para extrair tabelas de PDF e depende do `rJava` e, por isso, podem ocorrer alguns errinhos marotos.
Bom, meu intuito aqui é apenas brincar com o pacote e ter uma lista de ferramentas úteis no dia-a-dia do epidemiologista, embora nosso exemplo seja para área ambiental. Prometo atualizar este post e corrigir este detalhe ou gerar mais um post utilizando algum arquivo de boletim do Ministério da Saúde ou Secretaria Estadual. Juro!
Meu primeiro passo é carregar algumas bibliotecas úteis.
library(dplyr)
library(magrittr)
library(ggplot2)
library(patchwork)
library(tabulizer)
Vou utilizar para o exercício, este pdf disponível anualmente no site descrito abaixo. Não abordarei a origem do site e nem o propósito da empresa. Apenas usarei como exemplo.
url <- 'https://www.bp.com/content/dam/bp/business-sites/en/global/corporate/pdfs/energy-economics/statistical-review/bp-stats-review-2021-co2-emissions.pdf'
Função do Tabulizer
: a extract_tables
. É aqui que a mágica acontece. Note que alguns warnings ocorrem. Talvez, se eu realizasse o download do arquivo antes e depois a importação com o Tabulizer
, isso não aconteceria.
crude_out <- tabulizer::extract_tables(url, pages = 2, output = "data.frame")
O objeto crude_out
é uma lista contendo a estrutura da tabela extraída do PDF. Vou transformar este objeto em um dataframe e analisá-la melhor. O primeiro item da lista já é nossa tabela. Logo, posso simplesmente ‘jogá-la’ em um outro objeto.
df_out <- as.data.frame(crude_out[[1]])
E aqui encontro um ponto interessante. Ao extrair a tabela, é possível perceber algumas configurações estranhas. Mas é normal, pois a formatação das tabelas, geralmente, incluem células mescladas, por exemplo. Ao tentar encaixá-la em uma estrutura de linhas e colunas, digamos, normal, estas dificuldades aparecem.
Vamos analisar a tabela. Percebemos que o cabeçalho está estranho (em amarelo). Vamos nos concentrar na segunda linha, pois faremos dela, o cabeçalho da nossa tabela. A primeira linha vamos excluí-la.
kableExtra::kbl(head(df_out[1:5]))|>
kableExtra::kable_paper(
bootstrap_options = c("striped",
"hover",
"condensed"),
full_width = F,
position = "center"
)|>
kableExtra::row_spec(c(1:2),
background = "yellow"
)
X | X.1 | X.2 | X.3 | X.4 |
---|---|---|---|---|
NA | NA | NA | NA | |
Million tonnes of carbon dioxide | 2010.0 | 2011.0 | 2012.0 | 2013.0 |
Canada | 550.1 | 554.7 | 551.1 | 564.6 |
Mexico | 454.8 | 473.0 | 476.7 | 483.2 |
US | 5495.0 | 5348.4 | 5101.5 | 5268.3 |
Total North America | 6499.9 | 6376.1 | 6129.4 | 6316.1 |
Para isso, vamos utilizar a função colnames
.
colnames(df_out) <- df_out[2,]
kableExtra::kbl(head(df_out[1:5])) |>
kableExtra::kable_paper(
bootstrap_options = c("striped",
"hover",
"condensed"),
full_width = F,
position = "center"
)
Million tonnes of carbon dioxide | 2010 | 2011 | 2012 | 2013 |
---|---|---|---|---|
NA | NA | NA | NA | |
Million tonnes of carbon dioxide | 2010.0 | 2011.0 | 2012.0 | 2013.0 |
Canada | 550.1 | 554.7 | 551.1 | 564.6 |
Mexico | 454.8 | 473.0 | 476.7 | 483.2 |
US | 5495.0 | 5348.4 | 5101.5 | 5268.3 |
Total North America | 6499.9 | 6376.1 | 6129.4 | 6316.1 |
Depois, deletamos as linhas e colunas indesejadas e arrumamos as colunas e formato.
df_co2_w <- df_out[3:85, 1:12]
df_var_co2 <- df_out[3:85, 13:14]
df_var_co2 <- tidyr::separate(
df_var_co2,
col = c(`2020 2009-19`),
into = c("2020", "2009-19"),
sep = " "
) |>
tibble::add_column(var_2020 = df_var_co2$`2020`)
A partir daqui, vamos focar em alguns países. De forma arbitrária.
grupo_pais <- c("Brazil","US","Canada","Sweden",
"Russian Federation",
"China","Japan","South Africa")
df_co2_l <- df_co2_w |>
tidyr::pivot_longer(cols = "2010":"2020",
names_to = "Ano",
values_to = "CO2_MT") |>
dplyr::rename(Pais = `Million tonnes of carbon dioxide`) |>
dplyr::filter(!stringr::str_detect(Pais, "Total|Non|which|Other|European Union")) |>
dplyr::mutate(Ano = as.integer(Ano)) |>
dplyr::arrange(desc(CO2_MT))
Vamos visualizar em um gráfico, o que conseguimos extrair da tabela.
df_co2_l |>
ggplot2::ggplot(ggplot2::aes(
Ano,
CO2_MT,
group = Pais,
color = Pais)) +
ggplot2::geom_line(show.legend = F) +
ggplot2::geom_point(size = 2, show.legend = F) +
ggplot2::geom_text(
data = dplyr::filter(df_co2_l, Ano == 2020),
ggplot2::aes(label = Pais, color = Pais),
hjust = 0,
nudge_x = 0.1,
fontface = "bold",
size = 3,
show.legend = F
) +
ggplot2::scale_y_log10() +
ggplot2::scale_x_continuous(breaks = seq(2010, 2020, 1),
expand = c(0.01, 0)) +
ggplot2::expand_limits(x = 2022) +
ggplot2::theme_minimal() +
ggplot2::theme(plot.margin = ggplot2::unit(c(0.35, 2, 0.3, 0.35), "cm"))
Não ficou muito bom. Vamos separar os cinco primeiros dos cinco últimos.
top_five <- df_co2_l |>
dplyr::filter(Ano == 2020) |>
head(5) |>
dplyr::pull(Pais)
top_bottom <- df_co2_l |>
dplyr::filter(Ano == 2020) |>
tail(5) |>
dplyr::pull(Pais)
gg1 <- ggplot2::ggplot(data = dplyr::filter(df_co2_l, Pais %in% top_five),
ggplot2::aes(Ano, CO2_MT, group = Pais, color = Pais)) +
ggplot2::geom_line() +
ggplot2::geom_point(size = 2) +
ggplot2::labs(color = "Top five") +
ggplot2::theme_minimal()
gg2 <- ggplot2::ggplot(data = dplyr::filter(df_co2_l, Pais %in% top_bottom),
ggplot2::aes(Ano, CO2_MT, group = Pais, color = Pais)) +
ggplot2::geom_line() +
ggplot2::geom_point(size = 2) +
ggplot2::labs(color = "Bottom five") +
ggplot2::theme_minimal()
gg1/gg2
Parece que agora ficou um pouco melhor. Contudo, a ideia era falar sobre o uso do pacote que extrai a tabela do PDF. Ele ainda tem várias funcionalidades bacanerrímas. Precisa explorá-lo.
Bom trabalho!