--- title: "Baixar, tratar e consolidar transferências por município e função" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Baixar, tratar e consolidar transferências por município e função} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` # Introdução O **transfRgov** reúne, em um único pacote, dois blocos de dados sobre transferências de recursos da União para municípios brasileiros: 1. os endpoints da API **Fundo a Fundo** (TransfereGov), acessados pelas funções com prefixo `ler_` e `get_`; 2. os arquivos de **dados abertos** publicados pelo Portal da Transparência e pelo Tesouro Transparente, acessados por `download_transferencias_uniao()`, `baixa_municipio_siafibge()` e `consultar_renuncias_fiscais()`. Esta vignette percorre o segundo bloco de ponta a ponta: como baixar os arquivos mensais de transferências, como acrescentar o código IBGE do município, como classificar o tipo de destinatário e como consolidar tudo em uma tabela anual por **município** e por **função orçamentária**. O mesmo fluxo existia apenas como script solto em `data-raw/consolida_transferencias_p_funcao_municipio.R`; aqui ele é apresentado como documento executável. ```{r pacotes} library(transfRgov) library(dplyr) library(tidyr) ``` # De onde vêm os dados O Portal da Transparência publica, para cada mês, um arquivo ``` https://dadosabertos-download.cgu.gov.br/PortalDaTransparencia/saida/transferencias/AAAA{MM}_Transferencias.zip ``` com uma linha por combinação de órgão, unidade gestora, município recebedor, função, subfunção, programa, ação e elemento de despesa. O `download_transferencias_uniao()` monta essa URL a partir de `ano` e `mes`, baixa o ZIP para um arquivo temporário, descompacta, lê o CSV resultante e apaga os temporários. O CSV usa `;` como separador, `,` como separador decimal e codificação ISO-8859-1. Os nomes das colunas chegam em maiúsculas, com espaços e acentos (`ANO / MÊS`, `NOME FUNÇÃO`, `VALOR TRANSFERIDO`); a função aplica `janitor::clean_names()` antes de devolver o `data.frame`, então em R os nomes aparecem como `ano_mes`, `nome_funcao` e `valor_transferido`. ```{r baixar, eval = FALSE} transferencias_201901 <- download_transferencias_uniao(ano = 2019, mes = 1) names(transferencias_201901) #> [1] "ano_mes" "tipo_transferencia" #> [3] "tipo_favorecido" "uf" #> [5] "codigo_municipio_siafi" "nome_municipio" #> ... #> [35] "nome_favorecido" "valor_transferido" ``` As colunas relevantes para a consolidação são: | Coluna | Conteúdo | |---|---| | `ano_mes` | competência no formato `AAAAMM` (por exemplo, `201901`) | | `tipo_transferencia` | `"Constitucionais e Royalties"` ou `"Legais, Voluntárias e Específicas"` | | `codigo_municipio_siafi` | código do município no SIAFI, **com zeros à esquerda** | | `uf`, `nome_municipio` | unidade da federação e nome do município | | `nome_funcao` | função orçamentária (`Educação`, `Saúde`, ...) | | `nome_modalidade_aplicacao_despesa` | modalidade de aplicação da despesa, que revela o tipo de recebedor | | `valor_transferido` | valor transferido no mês | ## Acrescentar o código IBGE O código SIAFI não é o identificador usual dos municípios em outras bases (IBGE, Censo, DATASUS). Passar `codigo_ibge = TRUE` (o padrão) faz a função acrescentar a coluna `codigo_ibge` por meio de `match()` contra a tabela de correspondência: ```{r join-ibge, eval = FALSE} transferencias_201901 <- download_transferencias_uniao(ano = 2019, mes = 1) # A coluna codigo_ibge é acrescentada casando os códigos SIAFI transferencias_201901[1, c("codigo_municipio_siafi", "nome_municipio", "codigo_ibge")] #> codigo_municipio_siafi nome_municipio codigo_ibge #> 1 4123 Belo Horizonte 3106200 ``` Por padrão a função usa o dataset `municipios_siafi_ibge`, que acompanha o pacote e fica disponível assim que `library(transfRgov)` é executado: ```{r mapeamento} head(municipios_siafi_ibge, 3) ``` Como `codigo_municipio_siafi` é `character`, os zeros à esquerda são preservados e o casamento funciona (`"0001"`, e não `1`). Se você tem uma tabela de correspondência mais recente, passe-a em `municipios_mapping =`; e se não precisar do código IBGE, use `codigo_ibge = FALSE` para economizar o casamento. A função `baixa_municipio_siafibge()` baixa a versão publicada hoje pelo Tesouro Transparente. # Consolidar um ano O arquivo mensal tem granularidade fina demais para a maior parte das análises. O objetivo aqui é responder: **quanto cada município recebeu, por função orçamentária, em um ano?** Isso exige quatro passos. ## Passo 1: empilhar os doze meses Cada mês é um arquivo separado, então o ano é obtido iterando de 1 a 12 e colando os resultados. `data.table::rbindlist()` faz o empilhamento tolerando diferenças de colunas entre os meses. Nesta etapa também derivamos o ano a partir de `ano_mes`, dividindo por 100: ```{r passos-1-2, eval = FALSE} baixar_ano <- function(ano) { data.table::rbindlist( lapply(1:12, \(mes) { mes_dados <- download_transferencias_uniao(ano, mes) if (is.null(mes_dados)) return(NULL) dplyr::mutate(mes_dados, ano = trunc(ano_mes / 100)) }), use.names = TRUE ) } ``` `download_transferencias_uniao()` nunca lança erro para falhas de rede ou de leitura: ela emite um `warning()` e devolve `NULL` (invisível). Por isso o `if (is.null(mes_dados))` acima: um mês indisponível simplesmente não entra na pilha, e `rbindlist()` descarta os `NULL`. ## Passo 2: separar destinatário público de instituição privada A coluna `nome_modalidade_aplicacao_despesa` distingue, entre outras possibilidades, `"Transferências a Municípios"` de `"Transferências a Instituições Privadas sem Fins Lucrativos"`. Uma única expressão regular separa os dois universos: ```{r privadopub, eval = FALSE} dados |> dplyr::mutate( privadopub = ifelse( grepl("Privadas", nome_modalidade_aplicacao_despesa), "inst_privadas", "publico" ) ) ``` Tudo o que contém `"Privadas"` vira `inst_privadas`; todo o restante (transferências a municípios, a estados, a consórcios públicos, ao exterior) vira `publico`. ## Passo 3: somar por município, tipo de transferência e função Como cada linha do arquivo original é um item de despesa, o próximo passo agrega: ```{r agregar, eval = FALSE} dados |> dplyr::group_by(ano, codigo_ibge, uf, tipo_transferencia, privadopub, nome_funcao) |> dplyr::summarise(valor_transferido = sum(valor_transferido, na.rm = TRUE), .groups = "drop") |> tidyr::pivot_wider( names_from = c(tipo_transferencia, privadopub), values_from = valor_transferido, values_fill = 0 ) ``` O `pivot_wider()` abre `tipo_transferencia` e `privadopub` em colunas, de modo que cada município e função aparece com os valores separados por tipo de transferência e por tipo de recebedor, no formato `"_"`. Como são dois tipos de transferência e dois tipos de recebedor, surgem no máximo quatro colunas. Vale registrar uma armadilha: **os nomes produzidos pelo pivot são dinâmicos**. Nada garante que exista uma coluna chamada literalmente `privadas`; o nome real é `"Legais, Voluntárias e Específicas_inst_privadas"`. Se a sua base não tiver nenhuma transferência a instituições privadas em um ano, a coluna simplesmente não é criada. Por isso o próximo passo seleciona as colunas por padrão (`ends_with()`), e não por nome fixo. ## Passo 4: abrir por função O `data.frame` anterior é longo: uma linha por município, função e par de colunas. Para comparar municípios entre si, some as variantes públicas de cada função e abra as funções como colunas: ```{r wide, eval = FALSE} transfpubswide <- transfpubs_14_24 |> dplyr::rename_with(janitor::make_clean_names, dplyr::ends_with("_publico")) |> dplyr::select(ano, codigo_ibge, uf, nome_funcao, dplyr::ends_with("_publico")) |> dplyr::mutate( governo_e_publico = rowSums(dplyr::across(dplyr::ends_with("_publico"))) ) |> dplyr::select(ano, codigo_ibge, uf, nome_funcao, governo_e_publico) |> tidyr::pivot_wider( names_from = nome_funcao, values_from = governo_e_publico, values_fill = 0 ) |> dplyr::mutate( transftotal = rowSums(dplyr::across(dplyr::where(is.numeric) & -c(ano, codigo_ibge))) ) ``` O `rename_with(janitor::make_clean_names, ...)` normaliza nomes como `"Legais, Voluntárias e Específicas_publico"` para `legais_voluntarias_e_especificas_publico`, o que evita espaços, vírgulas e acentos como nomes de coluna. O resultado tem uma linha por município e ano, uma coluna por função orçamentária e a coluna `transftotal` com a soma das funções. # Um exemplo executável, sem rede Os blocos acima usam `eval = FALSE` porque dependem de download e não podem rodar durante a construção do pacote. Para demonstrar o encadeamento completo sem rede, construímos abaixo um `data.frame` pequeno com as **mesmas colunas do arquivo real** (já depois de `janitor::clean_names()`) e os mesmos valores categóricos: dois tipos de transferência, duas modalidades de aplicação e as funções `Educação`, `Saúde` e `Assistência Social` do município de Belo Horizonte (SIAFI `4123`, IBGE `3106200`). ```{r exemplo-dados} transferencias_exemplo <- data.frame( ano_mes = c(201901L, 201901L, 201901L, 201902L, 201902L, 201902L), tipo_transferencia = c( "Constitucionais e Royalties", "Legais, Voluntárias e Específicas", "Legais, Voluntárias e Específicas", "Constitucionais e Royalties", "Legais, Voluntárias e Específicas", "Legais, Voluntárias e Específicas" ), uf = "MG", codigo_municipio_siafi = "4123", nome_municipio = "Belo Horizonte", codigo_ibge = 3106200, nome_funcao = c( "Educação", "Saúde", "Assistência Social", "Educação", "Saúde", "Assistência Social" ), nome_modalidade_aplicacao_despesa = c( "Transferências a Municípios", "Transferências a Municípios - Fundo a Fundo", "Transferências a Instituições Privadas sem Fins Lucrativos", "Transferências a Municípios", "Transferências a Municípios", "Transferências a Municípios" ), valor_transferido = c(50000, 80000, 40000, 70000, 100001, 30000) ) knitr::kable(transferencias_exemplo) ``` Repare que, em fevereiro, `Assistência Social` aparece com a modalidade `"Transferências a Municípios"` enquanto em janeiro aparecia como `"Transferências a Instituições Privadas sem Fins Lucrativos"`: a mesma função recebe recursos dos dois universos, e é justamente essa separação que o passo 2 preserva. O "baixador" abaixo substitui `download_transferencias_uniao()` apenas filtrando o exemplo pelo mês: ```{r exemplo-baixador} baixar_mes <- function(ano, mes) { transferencias_exemplo[transferencias_exemplo$ano_mes == ano * 100 + mes, , drop = FALSE] } ``` Com isso, a mesma rotina dos passos 1 a 3 pode ser escrita uma única vez, recebendo o baixador como argumento. Em uso real, basta omitir `baixar` para cair no `download_transferencias_uniao()`: ```{r exemplo-consolida} consolida_tr_funcao <- function(ano, baixar = download_transferencias_uniao) { dados <- data.table::rbindlist( lapply(1:12, \(mes) { mes_dados <- baixar(ano, mes) if (is.null(mes_dados)) return(NULL) dplyr::mutate(mes_dados, ano = trunc(ano_mes / 100)) }), use.names = TRUE ) dados |> dplyr::mutate( privadopub = ifelse( grepl("Privadas", nome_modalidade_aplicacao_despesa), "inst_privadas", "publico" ) ) |> dplyr::group_by(ano, codigo_ibge, uf, tipo_transferencia, privadopub, nome_funcao) |> dplyr::summarise( valor_transferido = sum(valor_transferido, na.rm = TRUE), .groups = "drop" ) |> tidyr::pivot_wider( names_from = c(tipo_transferencia, privadopub), values_from = valor_transferido, values_fill = 0 ) } transfpubs_2019 <- consolida_tr_funcao(2019, baixar = baixar_mes) knitr::kable(transfpubs_2019) ``` O formato longo mostra exatamente o que o pivot produziu: três colunas de valores (`Constitucionais e Royalties_publico`, `Legais, Voluntárias e Específicas_publico` e `Legais, Voluntárias e Específicas_inst_privadas`), uma linha por função. Duas observações aparecem aqui: - `Assistência Social` tem `0` em `Constitucionais e Royalties_publico`: a função não recebeu transferências constitucionais, e o `values_fill = 0` preencheu a ausência com zero, em vez de deixar `NA`; - não existe coluna `Constitucionais e Royalties_inst_privadas`, porque transferências constitucionais não vão para instituições privadas. É o caso de "coluna que não existe" discutido no passo 3. Por fim, o passo 4 abre as funções em colunas: ```{r exemplo-wide} transfpubswide <- transfpubs_2019 |> dplyr::rename_with(janitor::make_clean_names, dplyr::ends_with("_publico")) |> dplyr::select(ano, codigo_ibge, uf, nome_funcao, dplyr::ends_with("_publico")) |> dplyr::mutate( governo_e_publico = rowSums(dplyr::across(dplyr::ends_with("_publico"))) ) |> dplyr::select(ano, codigo_ibge, uf, nome_funcao, governo_e_publico) |> tidyr::pivot_wider( names_from = nome_funcao, values_from = governo_e_publico, values_fill = 0 ) |> dplyr::mutate( transftotal = rowSums(dplyr::across(dplyr::where(is.numeric) & -c(ano, codigo_ibge))) ) knitr::kable(transfpubswide) ``` `transftotal` é a soma das funções: 120000 + 180001 + 30000 = 330001. Note que os 40000 recebidos por instituições privadas em `Assistência Social` ficam fora do total, porque o passo 4 soma apenas as variantes `_publico`. Se o interesse for o total geral, some também as colunas `_inst_privadas`. # Repetir para vários anos Consolidar uma série histórica é só repetir a chamada. O `rbindlist()` externo cuida de empilhar os anos: ```{r serie, eval = FALSE} transfpubs_14_24 <- data.table::rbindlist( lapply(2014:2024, \(ano) consolida_tr_funcao(ano)) ) ``` Dois cuidados ao ampliar o período: - **o conjunto de colunas muda ao longo do tempo.** `pivot_wider()` cria as colunas conforme os valores presentes em cada ano, e `values_fill = 0` resolve as ausências, mas o formato de `nome_funcao` e de `tipo_transferencia` pode variar entre anos. Prefira começar a partir de 2014, primeiro ano com a estrutura atual dos arquivos mensais; - **são 12 downloads por ano.** Cada arquivo tem dezenas de milhares de linhas. Rodar vários anos em sequência é demorado, e o servidor do Portal limita rajadas de requisições: se vários meses falharem seguidos, aguarde alguns minutos antes de repetir, em vez de insistir. # Limitações - **Não há um `data.frame` pré-consolidado no pacote.** O pacote entrega a camada de aquisição; a consolidação fica com quem analisa, porque as escolhas de agregação (público versus privado, total versus por função, municipal versus estadual) dependem da pergunta. - **Os valores de `valor_transferido` são nominais**, tal como publicados. Para séries históricas, deflate pelo IPCA ou outro índice antes de comparar anos. - **O casamento SIAFI-IBGE pode falhar** para códigos que não constam da tabela de correspondência: a coluna `codigo_ibge` recebe `NA` nesses casos, e não há erro. Confira `sum(is.na(dados$codigo_ibge))` depois do download. - **O pacote não é uma ferramenta oficial do governo federal.** Veja a nota de isenção de responsabilidade no `README` do projeto.