Documento de planejamento. Descreve a arquitetura proposta para integrar o backend Cloudflare R2 do projeto healthbr-data ao pacote healthbR, permitindo que o usuário escolha a fonte dos dados via parâmetro
source.Status: planejado — nenhuma linha de código foi alterada ainda. Criado em: 2026-03-07.
O pacote healthbR acessa dados do SI-PNI de duas formas
atualmente:
.dbf
baixados sob demanda por UF × ano.O projeto healthbr-data mantém os mesmos dados
pré-processados e publicados como Parquet no Cloudflare R2
(healthbr-data bucket), com as seguintes vantagens:
| Propriedade | Backend atual (FTP/CSV) | Backend R2 (Parquet) |
|---|---|---|
| Microdados 2020+ | CSV com artefatos (.0, zeros perdidos em
2020–2024) |
JSON origin — sem artefatos |
| Agregados 1994–2019 | .dbf via FTP (lento, instável em
BAs/MG/SP) |
Parquet prontos — leitura instantânea |
| Acesso offline | Não | Sim (após cache local) |
| Leitura parcial (Arrow lazy) | Limitada | Nativa via arrow::open_dataset() |
| Volume para múltiplos anos/UFs | Download sequencial | Scan colunar filtrado |
sipni_data()Adicionar dois novos parâmetros ao fim da assinatura existente:
sipni_data(
year, type = "DPNI", uf = NULL, month = NULL,
vars = NULL, parse = TRUE, col_types = NULL,
cache = TRUE, cache_dir = NULL,
lazy = FALSE, backend = c("arrow", "duckdb"),
source = c("datasus", "r2"), # NOVO
r2_credentials = NULL # NOVO
)
source
- "datasus" (padrão): comportamento atual, sem quebra de
compatibilidade.
- "r2": lê do Cloudflare R2 via Arrow S3 filesystem.
r2_credentials
- Lista com access_key_id e
secret_access_key.
- Se NULL, usa o token read-only público do
healthbr-data como padrão (o token é intencionalmente
publicado; só permite leitura do bucket).
- Permite que usuários com credenciais próprias apontem para buckets
alternativos.
sourcesource |
Dados 1994–2019 | Dados 2020+ | Dicionários |
|---|---|---|---|
"datasus" |
.dbf via FTP |
CSV via OpenDATASUS | internos (sipni_data_internal.R) |
"r2" |
sipni/agregados/doses/ ou
sipni/agregados/cobertura/ |
sipni/microdados/ |
sipni/dicionarios/ |
Parâmetro sipni_data() |
Comportamento no R2 |
|---|---|
year |
Filtro na partição ano= |
type = "DPNI" |
Prefixo sipni/agregados/doses/ |
type = "CPNI" |
Prefixo sipni/agregados/cobertura/ |
type = "API" (2020+) |
Prefixo sipni/microdados/ |
uf |
Filtro na partição uf= |
month |
Filtro na partição mes= (apenas 2020+) |
vars |
select() antes do collect() |
lazy = TRUE |
Retorna arrow::open_dataset() diretamente |
cache |
Cache local do dataset Arrow (mesmo mecanismo atual) |
R/sipni_r2.R — Funções internas de
acesso ao R2:
.sipni_r2_credentials() # resolve credenciais (padrão ou usuário)
.sipni_r2_filesystem() # cria arrow::S3FileSystem com as credenciais
.sipni_r2_open_dataset() # abre o dataset Arrow no prefixo correto
.sipni_r2_fetch() # aplica filtros e collect()
.sipni_r2_dictionary() # lê sipni/dicionarios/ do R2
R/sipni_data_internal.R — Adicionar
constantes R2:
# R2 backend constants
sipni_r2_endpoint <- "https://<account-id>.r2.cloudflarestorage.com"
sipni_r2_bucket <- "healthbr-data"
sipni_r2_access_key <- "<token-read-only-publico>" # Object Read only
sipni_r2_secret_key <- "<token-read-only-publico-secret>"
# R2 prefixes
sipni_r2_prefix_microdados <- "sipni/microdados"
sipni_r2_prefix_doses <- "sipni/agregados/doses"
sipni_r2_prefix_cobertura <- "sipni/agregados/cobertura"
sipni_r2_prefix_dicionarios <- "sipni/dicionarios"
R/sipni.R — Modificações em funções
exportadas:
sipni_data() # ramificação source == "r2" no início do fluxo
sipni_dictionary() # ramificação source == "r2" → lê do R2
sipni_info() # mencionar fonte R2 na saída
DESCRIPTION — Adicionar
arrow e paws.storage (ou aws.s3)
a Imports ou Suggests:
Suggests:
arrow,
paws.storage # alternativa mais leve para S3/R2
O backend R2 requer:
arrow (já em Suggests) —
para open_dataset() com S3FileSystem.paws.storage ou configuração direta do
arrow::S3FileSystem — para autenticação no R2 (endpoint
customizado, sem região AWS).O backend atual (FTP/CSV) não é afetado.
source = "datasus" continua funcionando sem
arrow.
O Cloudflare R2 expõe endpoint S3-compatível mas sem região AWS. A
configuração do arrow::S3FileSystem precisa de:
fs <- arrow::S3FileSystem$create(
endpoint_override = "https://<account-id>.r2.cloudflarestorage.com",
access_key = sipni_r2_access_key,
secret_key = sipni_r2_secret_key,
region = "auto"
)
Os Parquets estão particionados no estilo Hive:
sipni/microdados/ano=2024/mes=01/uf=AC/part-00000.parquet
sipni/agregados/doses/ano=2019/uf=SP/part-00000.parquet
sipni/dicionarios/imuno.parquet
sipni/dicionarios/imunocob.parquet
...
O Arrow detecta automaticamente as partições ao abrir o dataset,
permitindo filtros pushdown sem collect().
Os 6 Parquets de dicionário (imuno,
imunocob, dose, fxet,
ano, mes) são arquivos planos (sem
particionamento). Lidos com arrow::read_parquet()
diretamente. sipni_dictionary(source = "r2") lerá esses
arquivos em vez dos dados internos de
sipni_data_internal.R.
source = "datasus" é o padrão → comportamento atual
inalterado.arrow não estiver
instalado e source = "r2" for solicitado, a função emite
erro claro pedindo para instalar arrow.s3://healthbr-data/sipni/
microdados/ ← SI-PNI rotina 2020+
manifest.json
ano=2024/mes=01/uf=AC/
part-00000.parquet
covid/microdados/ ← SI-PNI COVID 2021+
manifest.json
ano=2024/mes=01/uf=AC/
part-00000.parquet
agregados/
doses/ ← DPNI 1994-2019
manifest.json
ano=2019/uf=SP/
part-00000.parquet
cobertura/ ← CPNI 1994-2019
manifest.json
ano=2019/uf=SP/
part-00000.parquet
dicionarios/
imuno.parquet
imunocob.parquet
dose.parquet
fxet.parquet
ano.parquet
mes.parquet
originais/ ← arquivos .cnv e .dbf originais do MS
Acesso público: token read-only (Account API token,
Object Read only) publicado intencionalmente nos dataset cards do
Hugging Face. O token só permite leitura do bucket
healthbr-data.
Dataset cards (Hugging Face): - Microdados:
https://huggingface.co/datasets/SidneyBissoli/sipni-microdados
- COVID:
https://huggingface.co/datasets/SidneyBissoli/sipni-covid -
Agregados Doses:
https://huggingface.co/datasets/SidneyBissoli/sipni-agregados-doses
- Agregados Cobertura:
https://huggingface.co/datasets/SidneyBissoli/sipni-agregados-cobertura
- Dicionários:
https://huggingface.co/datasets/SidneyBissoli/sipni-dicionarios
Decisões tomadas no projeto healthbr-data em 18/ago/2026 (ver lá
docs/contract-consumers-pt.md— fonte da verdade do que o bucket garante — edocs/sim/exploration-pt.md§9). Esta seção traduz o contrato para o pacote. O módulosimhoje lê o FTP direto e mantém uma lista fixa de anos (sim_available_years: final 1996–2022, preliminar 2023–2024), já defasada (FTP em ago/2026: final até 2024, preliminar 2025–2026). Comsource = "r2"essa informação passa a vir domanifest.json.
| Módulo | Prefixo R2 | Partição | Chave no manifesto | Observações |
|---|---|---|---|---|
sim (não fetais) |
sim/dores/ |
ano=YYYY/uf=XX/ |
YYYY-XX |
1979–presente; UF de residência; preliminares no mesmo prefixo |
sim (fetais) |
sim/dofet/ |
ano=YYYY/ |
YYYY |
arquivo nacional; sem uf= |
sinasc |
sinasc/ |
ano=YYYY/uf=XX/ |
YYYY-XX |
1994–2022+ |
sih |
sih/rd/, sih/sp/ |
ano=YYYY/mes=MM/uf=XX/ |
YYYY-MM-XX |
RD 1992+, SP 1997+ |
Todas as colunas são string; nenhum valor é transformado;
schema por arquivo-fonte (abrir com
unify_schemas = TRUE; colunas ausentes num ano vêm
NA). Cada Parquet carrega metadado healthbr
(JSON no schema metadata: source_url,
source_hash_md5, download_date,
pipeline_version, git_commit,
source_status).
O DATASUS publica o ano N fechado só em dez/N+1; até lá, N e N+1
estão em PRELIM/ e são regravados sem aviso. O
healthbr-data publica esses anos no mesmo prefixo,
marcados source_status = "preliminar" no manifesto e no
metadado de cada Parquet (ausente = final). Quando o ano fecha, a
partição é substituída (sem histórico). Cabe ao pacote dar visibilidade
— itens acordados:
preliminary = FALSE em
sim_data() (e nos demais módulos quando a fonte tiver
preliminares): com o default, anos marcados como preliminares no
manifesto são excluídos (e, se o usuário pediu
explicitamente um ano preliminar, erro/aviso claro sugerindo
preliminary = TRUE). Nome do argumento decidido:
preliminary (adjetivo, como lazy,
verbose).cli::cli_warn/cli_inform) sempre que o
retorno contiver dados preliminares: anos, data de processamento
(processing_timestamp) e a frase “podem ser regravados pelo
Ministério”.healthbr_status ("final"/
"preliminar") ou atributo
attr(x, "healthbr_source_status") — decidir na
implementação; a coluna facilita group_by.sim_status() (padrão para os outros
módulos): tabela ano × UF × status × processing_timestamp ×
source_hash_md5 lida do manifesto — substitui a lista fixa
sim_available_years; sim_years(status=) e
sim_info() passam a consultá-la quando
source = "r2".source_hash_md5/processing_timestamp, não por
nome).contador/CONTADOR (SIM:
minúsculo 1979–2005 e 2009–2010; maiúsculo 2006–2008 e 2011+): o R2
publica como na fonte (decisão A). O pacote coalesce as duas caixas em
CONTADOR ao retornar (conveniência mora aqui). O SINASC
hoje já vem unificado pelo pipeline, mas será revertido
para a caixa da fonte — coalescer desde já evita quebra.NUMERODO não existe nos arquivos públicos;
CODMUNRES 7 dígitos até 2005 e 6 a partir de 2006; CID-9
(1979–95) com nomes DATAOBITO (AAMMDD, dia 00
até 1990), MUNIRES, OCUPACAO,
INSTRUCAO, ESTCIVIL; decode_age
continua válido (IDADE é estável desde 1979).sim/dofet/
(TIPOBITO = 1); sim/dores/ só tem
TIPOBITO = 2. Sugestão de API:
sim_data(type = c("dores", "dofet"))..r2_filesystem(),
.r2_manifest(prefix) (cache curto),
source = c("datasus", "r2") nos *_data().sim: preliminary,
sim_status(), aviso, coalescência de CONTADOR,
type = "dofet".sinasc, sih, sipni (seção 2
acima).HEALTHBR_INTEGRATION=true
contra o bucket público; NEWS.md; anunciar a mudança de API
no thread rOpenSci (#751).Rastreio: uma issue por item de 7.2 e 7.4 (milestone “backend R2”), quando o mantenedor aprovar o texto.
Documento criado em 2026-03-07; seção 7 adicionada em 2026-08-18. Atualizar ao iniciar a implementação.