Category

Data Science

Como retirar dados de contas públicas municipais via API do SICONFI

By | Hackeando o R

Neste texto, iremos mostrar como podemos retirar os dados dos demonstrativos contábeis de entes federativos do Brasil pelo Sistema de Informações Contábeis e Fiscais do Setor Público Brasileiro (SICONFI) via API no R.

Primeiramente, é necessário estar a par dos parâmetros que devem ser colocados como entradas para obter os dados dos diversos tipos de demonstrativos. O site http://apidatalake.tesouro.gov.br/docs/siconfi/ fornece detalhadamente quais parâmetros devem ser fornecidos para cada tipo de demonstrativos, bem como a url base para realizar a requisição do API.

Aqui iremos trabalhar como exemplo a Declaração de Contas Anuais (DCA) Anexo I-D do município de Varginha - Minas Gerais, no qual nos fornecerá as Despesas Orçamentárias por Natureza.

Para o DCA, há 3 parâmetros que devem ser inseridos: an_exercicio (Ano de exercício do demonstrativo); no_anexo (Qual anexo do relatório deseja obter) e id_ente (O código IBGE do ente em questão). Sendo an_exercicio e id_ente obrigatórios para esse demonstrativo em questão.

É fundamental a utilização dos pacotes a seguir.


library(httr)
library(jsonlite)
library(magrittr)
library(tibble)

Em seguida vamos realizar a chamada da API criando uma URL.

# URL da DCA de Varginha no ano de 2020 
url_dca <- paste("https://apidatalake.tesouro.gov.br/ords/siconfi/tt/dca?", # URL base para a chamada 
"an_exercicio=", 2020, "&", # Insere o parâmetro de Ano do exercício 
"no_anexo", "DCA-Anexo+I-D","&", # Insere o parâmetro do Anexo que se deseja obter 
"id_ente=", "3170701", sep = "") # Insere o parâmetro do Ente de acordo com o código IBGE do mesmo

Após isso, devemos realizar a requisição da API usando a função GET do pacote httr, bem como realizar a extração do conteúdo com as funções content e fromJSON dos pacotes httr e jsonlite, respectivamente.


api_dca <- GET(url_dca) 
 
# A chamada irá nos retornar os dados requisitados. Agora só precisamos extrair o conteúdo que nos interessa

json_dca <- api_dca %>%

content(as = "text", encoding = "UTF-8") %>%

fromJSON(flatten = FALSE)

# E após isso transforma-los em um tibble

dca_tb <- as.tibble(json_dca[["items"]])

Desta forma podemos obter os dados do DCA Anexo I-D do município de Varginha. O método pode ser replicado para outros anexos e demonstrativos, bem como para qualquer outro Ente do Brasil.

Lidando com erros de funções que podem falhar usando o purrr

By | Data Science

Funções de importação e coleta de dados são pontos críticos de qualquer script, e é esperado que erros e falhas sejam gerados em algum momento. Neste texto iremos mostrar como melhorar essas funções usando o purrr para lidar com possíveis erros que podem ser uma dor de cabeça pro usuário de R.

Se você costuma coletar dados de bases públicas, seja através de uma API ou um link para um arquivo, provavelmente já ficou no vácuo aguardando sua requisição de dados ser concluída ou, pior, obteve um erro na requisição. Isso é muito comum ao se utilizar pacotes de R para coleta de dados via API's, mas também pode acontecer com links diretamente para arquivos. As origens dessas falhas podem ser diversas: sua conexão de internet instável, API de dados com instabilidade, site/link fora do ar, você está fazendo muitas requisições sucessivas, sua sessão de R está com conexões abertas conflitantes, etc.

Exemplo: importar planilha de dados através de um link

Como exemplo, vamos supor que você esteja (no pior cenário) tentando coletar dados de uma planilha de Excel disponibilizada através de um link de um determinado site. Este link as vezes funciona, as vezes demora a carregar e as vezes falha completamente, mas você precisa desses dados para rodar um relatório diariamente. Portanto, sua missão é tornar seu script de coleta desses dados mais resistente a essas possíveis falhas.

Neste contexto, uma estratégia possível é continuar tentando a requisição dos dados pelo link por um número x de vezes até, definitivamente, falhar. Com o pacote purrr podemos criar essa estratégia de forma muito fácil usando a função insistently. Ainda é possível determinar um valor de retorno padrão (por exemplo, um texto "Falha da requisição") para caso a falha aconteça, bastando combinar a função anterior com a função possibly. Vamos aos exemplos!

Os pacotes utilizados neste exercício estão descritos a seguir. Você pode instalar os mesmos a partir do CRAN.

library(purrr) # CRAN v0.3.4
library(rio) # CRAN v0.5.27

Neste exemplo definimos um link para uma planilha de dados do Banco Central do Brasil (BCB) que possivelmente pode estar indisponível ou instável em algum momento. Então criamos uma configuração (purrr::rate_delay) de número de tentativas para a importação da planilha e tempo de espera em segundos entre cada tentativa.

Em seguida usamos a função rio::import() para importar a planilha para o R com a diferença de que modificaremos a função incluindo a configuração criada anteriormente (pausa & tentativas). Para incluir essa modificação usamos a função insistently, bastando especificar uma função a ser modificada (f), um configuração de pausa & tentativas (rate) e, opcionalmente, indicar se devem ser exibidas mensagens no Console (quiet). No final teremos uma nova função chamada insist_import no Environment e usamos ela para coletar os dados da planilha.Perceba que todos os possíveis argumentos da função original (rio::import) são preservados e podemos usá-los na função modificada (insist_import).Podemos ir um passo além e determinar o que deve ser retornado caso a importação dos dados falhe nas 3 tentativas. Para isso usamos a função purrr::possibly(), que retorna um valor padrão quando a falha acontece na função que criamos.O mesmo procedimento pode ser feito para qualquer outra função de importação e coleta de dados, seguindo a mesma lógica.

Espero que esses exemplos amenizem suas possíveis dores de cabeça com coleta de dados no R!

 

Exibindo observações como imagens no ggplot2

By | Data Science, Hackeando o R

Algumas vezes inserir imagens pode enriquecer um gráfico e atrair maior atenção. No R essa personalização da visualização de dados é relativamente simples usando o ggplot2 e suas extensões, conforme demonstramos nesse tutorial com exemplos inserindo imagens em pontos de observações.

Pacotes

Os pacotes utilizados neste exercício estão descritos a seguir. Você pode instalar os mesmos a partir do CRAN.


library(magrittr) # CRAN v2.0.1
library(purrr) # CRAN v0.3.4
library(glue) # CRAN v1.4.2
library(OECD) # CRAN v0.2.4
library(dplyr) # CRAN v1.0.7
library(lubridate) # CRAN v1.7.10
library(ggplot2) # CRAN v3.3.5
library(forcats) # CRAN v0.5.1
library(ggtext) # CRAN v0.1.1

Dados

Primeiro vamos coletar alguns dados e imagens de exemplo para construir uma visualização, objetivo é criar um gráfico da taxa de inflação de alguns países selecionados.

Os dados estão disponíveis na base da OECD e as imagens são provenientes do site flaticon.com e disponibilizados em nosso site. Importante: o script abaixo cria uma pasta temporária em seu computador e salva os arquivos de imagens (PNG) que utilizaremos neste exercício. Não execute o script se não deseja que isso seja feito.


# Link para imagens de bandeiras/país (Créditos: https://www.flaticon.com/)
url_imgs <- "https://analisemacro.com.br/wp-content/uploads/2021/09/"

# Nomes de arquivos PNG
country_names <- c(
"BRA",
"CHN",
"EU27_2020",
"IND",
"MEX",
"RUS",
"SAU",
"TUR",
"USA",
"ZAF"
)

# Criar pasta temporária
temp_folder <- tempdir()

# Baixar PNGs
purrr::walk2(
.x = url_imgs,
.y = country_names,
~download.file(
url = glue::glue("{.x}{.y}.png"),
destfile = glue::glue("{temp_folder}/{.y}.png"),
mode = "wb"
)
)

# Coletar e tratar dados de inflação para países selecionados
df_inflation <- OECD::get_dataset(
dataset = "MEI",
filter = "MEX+TUR+USA+BRA+CHN+IND+RUS+SAU+ZAF.CPALTT01.GY.M",
start_time = 2021
) %>%
dplyr::select(
"date" = `obsTime`,
"country" = `LOCATION`,
"value" = `obsValue`
) %>%
dplyr::mutate(
# Caminho do arquivo PNG
path_to_imgs = glue::glue("{temp_folder}/{country}.png"),
# Tag de imagem para ser usada no ggtext
country_imgs = glue::glue("<img src='{path_to_imgs}' width='30'/>"),
date = lubridate::ym(date),
fill_col = dplyr::if_else(country == "BRA", "#b22200", "#282f6b")
) %>%
dplyr::filter(date == lubridate::as_date("2021-07-01"))

df_inflation

## # A tibble: 10 x 6
## date country value path_to_imgs country_imgs fill_col
## <date> <chr> <dbl> <glue> <glue> <chr>
## 1 2021-07-01 MEX 5.81 "C:\\Users\\ferna\~ "<img src='C:\\User~ #282f6b
## 2 2021-07-01 TUR 19.0 "C:\\Users\\ferna\~ "<img src='C:\\User~ #282f6b
## 3 2021-07-01 USA 5.37 "C:\\Users\\ferna\~ "<img src='C:\\User~ #282f6b
## 4 2021-07-01 BRA 8.99 "C:\\Users\\ferna\~ "<img src='C:\\User~ #b22200
## 5 2021-07-01 CHN 1 "C:\\Users\\ferna\~ "<img src='C:\\User~ #282f6b
## 6 2021-07-01 IND 5.26 "C:\\Users\\ferna\~ "<img src='C:\\User~ #282f6b
## 7 2021-07-01 RUS 6.47 "C:\\Users\\ferna\~ "<img src='C:\\User~ #282f6b
## 8 2021-07-01 ZAF 4.65 "C:\\Users\\ferna\~ "<img src='C:\\User~ #282f6b
## 9 2021-07-01 SAU 0.433 "C:\\Users\\ferna\~ "<img src='C:\\User~ #282f6b
## 10 2021-07-01 EU27_2020 2.5 "C:\\Users\\ferna\~ "<img src='C:\\User~ #282f6b

Criamos duas colunas importantes: a primeira (path_to_imgs) indica o caminho para o arquivo PNG salvo no computador e a segunda (country_imgs) é uma tag HTML para ser usada com o pacote ggtext, que interpreta o código lendo a imagem para ser renderizada no gráfico.

Um gráfico simples

Com os dados pronto podemos gerar uma simples visualização com o ggplot2, conforme abaixo:


# Gráfico simples
plot_inflation <- df_inflation %>%
ggplot2::ggplot(
ggplot2::aes(
x = forcats::fct_reorder(country, value, .desc = TRUE),
y = value,
fill = fill_col,
color = fill_col,
label = format(round(value, 2), nsmall = 2, decimal.mark = ","),
width = 0.6
)
) +
ggplot2::geom_col() +
ggplot2::geom_text(vjust = -0.5, fontface = "bold", size = 4.5) +
ggplot2::geom_hline(yintercept = 0, color = "black", size = 1) +
ggplot2::scale_color_identity(aesthetics = c("fill", "color")) +
ggplot2::scale_y_continuous(breaks = c(0, 5, 10, 15, 20), limits = c(0, 20)) +
ggplot2::theme_minimal() +
ggplot2::labs(
title = "**Inflação**",
subtitle = glue::glue("Países selecionados, {format(max(df_inflation$date), '%B/%Y')}"),
y = "% a/a",
x = NULL,
caption = "<br>**Dados:** OECD | **Gráfico:** analisemacro.com.br"
) +
ggplot2::theme(
panel.grid.minor = ggplot2::element_blank(),
plot.title = ggtext::element_markdown(size = 20),
plot.subtitle = ggplot2::element_text(size = 16),
plot.caption = ggtext::element_markdown(size = 12),
axis.text = ggplot2::element_text(size = 14, face = "bold"),
axis.title.y = ggplot2::element_text(size = 14, face = "bold")
)

plot_inflation

Gráfico com imagens: ggplot2 + ggtext

Vamos supor que queiramos enriquecer a visualização anterior inserindo imagens das bandeiras dos países no topo de cada coluna. Isso pode ser feito facilmente com o geom_richtext que, como o próprio nome diz, é muito útil para melhorar renderização e formatação de textos em gráficos do ggplot2.

Dessa forma, basta criar uma tag HTML img indicando a fonte do arquivo PNG e uma largura, conforme a coluna country_imgs do nosso objeto de dados, para incorporar uma imagem em vez de apenas formatar texto.


# Gráfico com imagens
plot_inflation +
ggtext::geom_richtext(
ggplot2::aes(x = country, y = value, label = country_imgs),
size = 1,
fill = NA,
label.color = NA
)

Também podemos inserir as imagens no eixo X com o código abaixo, e observe que estamos replicando praticamente o mesmo código, mas mudando a estética principal aes() para ter x = country_imgs e o elemento do theme para axis.text.x = ggtext::element_markdown().


# Gráfico com imagens no eixo
df_inflation %>%
ggplot2::ggplot(
ggplot2::aes(
x = forcats::fct_reorder(country_imgs, value, .desc = TRUE),
y = value,
fill = fill_col,
color = fill_col,
label = format(round(value, 2), nsmall = 2, decimal.mark = ","),
width = 0.6
)
) +
ggplot2::geom_col() +
ggplot2::geom_text(vjust = -0.5, fontface = "bold", size = 4.5) +
ggplot2::geom_hline(yintercept = 0, color = "black", size = 1) +
ggplot2::scale_color_identity(aesthetics = c("fill", "color")) +
ggplot2::scale_y_continuous(breaks = c(0, 5, 10, 15, 20), limits = c(0, 20)) +
ggplot2::theme_minimal() +
ggplot2::labs(
title = "**Inflação**",
subtitle = glue::glue("Países selecionados, {format(max(df_inflation$date), '%B/%Y')}"),
y = "% a/a",
x = NULL,
caption = "<br>**Dados:** OECD | **Gráfico:** analisemacro.com.br"
) +
ggplot2::theme(
panel.grid.minor = ggplot2::element_blank(),
plot.title = ggtext::element_markdown(size = 20),
plot.subtitle = ggplot2::element_text(size = 16),
plot.caption = ggtext::element_markdown(size = 12),
axis.text = ggplot2::element_text(size = 14, face = "bold"),
axis.text.x = ggtext::element_markdown(margin = ggplot2::margin(t = -5, unit = "pt")),
axis.title.y = ggplot2::element_text(size = 14, face = "bold")
)

Espero que esses exemplos tenham sido úteis para você!

Como padronizar gráficos sem repetir código

By | Data Science

Criar gráficos padronizados usando pipelines com o tidyverse no R não precisa ser uma tarefa tediosa de Ctrl+C / Ctrl+V mudando a variável de interesse a ser plotada no seu código de ggplot2. Isso torna o seu script caótico e ineficiente, além de ser potencialmente mais trabalhoso fazer atualização/manutenção desse código.

Uma maneira mais elegante de criar gráficos padronizados, para um relatório por exemplo, pode ser através da escrita de uma função que generaliza o resultado que se quer alcançar. Vamos supor que você queira gerar gráficos de linha padronizados em um relatório extenso. Cada uma das várias visualizações de dados do seu relatório terá um gráfico de linha do ggplot2 para as n variáveis do seu conjunto de dados. Nesta situação, a criação de uma função se encaixa perfeitamente, pois irá unificar em um único comando tudo o que você precisa fazer para todas as visualizações a serem geradas, sem precisar repetir código.

Em termos mais práticos, um exemplo desta situação pode ser código abaixo, onde demonstramos um exemplo bom (usando boas práticas de programação em R) e um exemplo ruim (repetindo manualmente o código).

 

A diferença é bastante clara e significativa, o que você acha melhor? Ambas opções produzem os mesmos resultados gráficos:

O "pulo do gato" aqui, no caso da criação da função plot_line, é utilizar o operador chamado curly-curly para passar os nomes das colunas do objeto data frame diretamente nos argumentos da função que criamos, usando a sintaxe {{ func_arg }} no corpo da função (neste caso em aes). O trabalho "sujo" de identificar corretamente o nome de coluna passado no argumento da função é feito todo internamente por esse operador.

Esse operador se originou em 2019 no pacote rlang e é bastante utilizado internamente nas funções do tidyverse. Se você quiser entender mais a fundo seu funcionamento sugiro começar por este post do blog do tidyverse.

Se mesmo após este exemplo básico você não se convenceu, dê uma olhada na diferença de performance (execução em milissegundos) dos dois códigos após 1000 execuções:

As inovações do tidyverse são maravilhosas e facilitam o dia a dia do usuário de R. Espero que este tenha sido um exercício que instigue curiosidade em investigar se seus códigos performam bem e seguem boas práticas.

 

Hackeando o R: Rodando uma regressão com métodos bayesianos

By | Hackeando o R

No Hackeando o R de hoje, vamos finalizar a série sobre estatística bayesiana mostrando como pode ser feita a estimação de um modelo linear simples com MCMC. Diferentemente do método padrão, onde consideramos que a variável de resposta tem distribuição normal após controlarmos para efeitos lineares, os métodos bayesianos permitem maior liberdade quanto às hipóteses tomadas, dado que essencialmente tornam a estimação de parâmetros um problema de seleção de crenças iniciais. No caso a seguir, iremos fazer uma regressão linear robusta, supondo que a distribuição é na verdade uma t de Student.

A distribuição t em sua forma generalizada possui três parâmetros, de média, escala e seus graus de liberdade. Destes, a média é o de maior interesse, pois deve corresponder à previsão do modelo, que, conforme conhecemos, possui dois parâmetros (constante e linear). Note que, com isso, a estimação de y depende de um parâmetro que depende de outros dois, o que torna esse um modelo hierarquizado. Para rodarmos o algoritmo de MCMC, geramos crenças a priori dos parâmetros 'de ponta', e construímos a condicional em passos. Abaixo, geramos o modelo com distribuições usuais:

modelString = "
data {
Ntotal = length(y)
xm = mean(x)
ym = mean(y)
xsd = sd(x)
ysd = sd(y)
for ( i in 1:length(y) ) {
zx[i] = ( x[i] - xm ) / xsd
zy[i] = ( y[i] - ym ) / ysd
}
}

model {
for ( i in 1:Ntotal ) {
zy[i] ~ dt( zbeta0 + zbeta1 * zx[i] , 1/zsigma^2 , nu )
}

zbeta0 ~ dnorm( 0 , 1/(10)^2 ) 
zbeta1 ~ dnorm( 0 , 1/(10)^2 )
zsigma ~ dunif( 1.0E-3 , 1.0E+3 )
nu ~ dexp(1/30.0)

beta1 = zbeta1 * ysd / xsd 
beta0 = zbeta0 * ysd + ym - zbeta1 * xm * ysd / xsd 
sigma = zsigma * ysd
}
"
writeLines( modelString , con="TEMPmodel.txt" )

É importante notar que fizemos a padronização dos dados para rodar o modelo. Isso não é necessário, porém facilita a convergência do MCMC. Após isso, basta carregar os dados, disponíveis aqui,
e rodar o modelo.

myData = read.csv(file="HtWtData30.csv" )
xName = "height" ; yName = "weight"

y = myData[,yName]
x = myData[,xName]

dataList = list(
x = x ,
y = y
)

library(rjags)

parameters = c("beta0", "beta1", "sigma", "nu" )

jagsModel = jags.model( "TEMPmodel.txt" , data=dataList ,
n.chains=4 , n.adapt=500)

codaSamples = coda.samples(jagsModel, variable.names = parameters,
n.iter= 50000, thin=1)

plot(codaSamples)

Receba diretamente em seu e-mail gratuitamente nossas promoções especiais
e conteúdos exclusivos sobre Análise de Dados!

Assinar Gratuitamente
{"cart_token":"","hash":"","cart_data":""}