Referência da API
Todos os nomes exportados, organizados por categoria.
MicroSUS.MicroSUS — Module
MicroSUSMicrodados do DATASUS em Julia, com leitura streaming de arquivos .dbc (PKWare DCL) e .dbf: memória constante do arquivo comprimido até o sink, seleção de colunas e filtro de linhas no leitor, transcodificação CP850/Latin-1 → UTF-8, schemas tipados por sistema (SIM, SINASC, SIH, SIA, CNES) e interface Tables.jl com partições.
Uso básico:
using MicroSUS, DataFrames
caminho = baixar(:sim, "PE"; ano = 2023) # cache local (Scratch.jl)
df = DataFrame(ler(caminho)) # tudo tipado
# nacional sem estourar RAM: colunas + filtro no leitor
t = ler(caminho; colunas = [:DTOBITO, :CAUSABAS, :CODMUNRES, :IDADE, :SEXO],
filtro = r -> eh_agressao(r[:CAUSABAS])) # CVLI: X85–Y09
# streaming direto para Arrow (requer `using Arrow`)
converter(caminho, "do_pe_2023.arrow"; colunas = [:DTOBITO, :CAUSABAS, :CODMUNRES])Formato .dbc = cabeçalho DBF em claro + 4 bytes de CRC + registros comprimidos com PKWare DCL ("implode"). O descompressor é um porte puro Julia do blast.c de Mark Adler, com janela de 4 KiB emitida em chunks — é isso que permite a leitura em memória constante.
Leitura
MicroSUS.ler — Function
ler(caminho; colunas = nothing, filtro = nothing,
tamanho_lote = 100_000, schema = :auto, encoding = :auto,
pool = true, ignorar_ausentes = false) -> TabelaDBCAbre um .dbc ou .dbf do DATASUS como tabela preguiçosa (Tables.jl, com Tables.partitions). Nada é lido até a iteração.
colunas:Vector{Symbol}com os campos desejados — os demais nem são materializados.nothing= todos.filtro: funçãoRegistroDBF -> Boolaplicada antes do parse das colunas;r[:CAMPO]devolve o texto do campo sob demanda. Ex.:r -> r[:CODMUNRES] == "261110".haskey(r, :CAMPO)diz se o campo existe neste arquivo — útil em leituras de vários anos, em quer[:LINHAA]dariaKeyErrornos layouts sem as linhas da DO.schema::auto(deduz pelo prefixo do arquivo: DO→SIM, DN→SINASC, RD→SIH, PA→SIA, ST→CNES), umSymbol(:sim, ...), umDict{Symbol,Symbol}próprio, ounothing(só a tipagem do DBF).encoding::auto(language driver do cabeçalho; DATASUS ⇒ cp850), ou:cp850,:latin1,:cp1252,:utf8.ignorar_ausentes: quandotrue, colunas decolunasque não existem no layout deste arquivo são descartadas em vez de lançarArgumentError. É o que torna prática a leitura multi-ano do SIH, cujo layout ganhou campos em 2011, 2013 e 2014 — sem isso, pedir:DIAGSEC1derruba a leitura de 2010. As colunas descartadas saem por@debug; se nenhuma das pedidas existir, ainda assim é erro, porque aí o problema é outro (arquivo errado ou nome digitado errado);pool: usaPooledArraynas colunas categóricas do schema (equivalente ao factor do R, opt-in).
Uso: DataFrame(ler(caminho)) materializa tudo; for lote in Tables.partitions(ler(caminho)) processa em lotes; Arrow.write(saida, ler(caminho)) converte em streaming.
ler(caminhos::AbstractVector; uniao = false, origem = :ARQUIVO,
kwargs...) -> TabelaConcatenadaLê vários arquivos como uma tabela só, em streaming: os lotes saem de um arquivo depois do outro e a memória continua O(tamanho_lote). Os demais kwargs (colunas, filtro, schema, ignorar_ausentes, …) valem para cada arquivo como em ler(caminho).
caminhos = baixar(:sim, "PE"; anos = 2014:2023)
t = ler(caminhos; colunas = [:DTOBITO, :CAUSABAS, :CODMUNRES],
filtro = r -> eh_agressao(r[:CAUSABAS]))
DataFrame(t) # uma linha por óbito, coluna :ARQUIVO = "DOPE2014.dbc", …
Arrow.write("cvli_pe.arrow", t) # um record batch por lote, schema únicouniao: comfalse, todos os arquivos precisam produzir as mesmas colunas — layouts diferentes entre anos são erro, com a lista do que difere. Comtrue, a saída tem a união das colunas, e as que faltam num arquivo vêm comomissingnas linhas dele.origem: nome da coluna acrescentada ao fim com o nome do arquivo de cada linha ("DOPE2023.dbc");nothingpara não acrescentar. Também pode ser uma funçãocaminho -> NamedTuple, que vira uma coluna constante por chave — é assim quefetch_datasusacrescentaUF_ARQUIVO,ANO_ARQUIVOePRELIMINAR:origem = c -> (ANO = parse(Int, basename(c)[5:8]),).
Os tipos são unificados por coluna: texto de larguras diferentes vira a InlineString mais larga, inteiro e decimal viram Float64, texto e categórico viram categórico. Uma coluna que mude de tipo de outro jeito entre os arquivos (data num ano, texto noutro) vira String em todos, com um aviso.
MicroSUS.TabelaDBC — Type
TabelaDBCTabela preguiçosa sobre um .dbc/.dbf, criada por ler. Nada é lido do disco até a iteração. Implementa a interface Tables.jl: Tables.partitions produz lotes de tamanho_lote linhas (cada lote é um NamedTuple de vetores) e Tables.columns materializa tudo via materializar — então DataFrame(t), Arrow.write(io, t) e afins funcionam diretamente.
MicroSUS.TabelaConcatenada — Type
TabelaConcatenadaVários .dbc/.dbf lidos como uma tabela preguiçosa só, criada por ler com um vetor de caminhos. Mesma interface Tables.jl de TabelaDBC: Tables.partitions produz os lotes de cada arquivo em sequência, todos com as mesmas colunas e os mesmos tipos, e DataFrame(t) materializa tudo.
MicroSUS.materializar — Function
materializar(t::TabelaDBC) -> NamedTupleConsome todas as partições e concatena as colunas. É o que DataFrame(t) chama por baixo via Tables.columns.
materializar(t::TabelaConcatenada) -> NamedTupleConsome as partições de todos os arquivos e concatena as colunas.
Conversão
MicroSUS.converter — Function
converter(entrada, saida; kwargs...)Converte um .dbc/.dbf para Arrow em streaming (um record batch por lote), sem materializar o arquivo inteiro. Requer using Arrow na sessão (extensão condicional). Aceita os mesmos kwargs de ler.
entrada também pode ser um vetor de caminhos: os arquivos vão para um único .arrow, com schema unificado (ver ler(caminhos::AbstractVector)).
As colunas categóricas são gravadas como texto simples, sem dicionário: o leitor do Arrow.jl falha de forma intermitente em arquivos cujo dicionário cresce de um lote para outro. Por isso converter é o caminho recomendado em vez de Arrow.write(saida, ler(caminho)).
MicroSUS.descomprime_dbc_para_dbf — Function
descomprime_dbc_para_dbf(entrada, saida) -> saidaConverte .dbc → .dbf em streaming (equivalente ao dbc2dbf do read.dbc, memória constante).
Download
MicroSUS.baixar — Function
baixar(sistema, uf; ano = nothing, mes = nothing,
forcar = false, quieto = false) -> StringBaixa (com cache local via Scratch.jl) um arquivo do DATASUS e devolve o caminho no disco. Chamadas repetidas não rebaixam; forcar = true ignora o cache.
baixar(sistema, uf; anos, meses = nothing) -> Vector{String}Forma plural: baixa vários períodos em paralelo (asyncmap).
MicroSUS.url_arquivo — Function
url_arquivo(sistema, uf; ano = nothing, mes = nothing,
prelim = false) -> StringURL FTP do arquivo .dbc no DATASUS. Sistemas anuais (:sim, :sinasc) pedem ano; mensais (:sih, :sia, :cnes) pedem ano e mes. prelim = true aponta para a pasta de dados preliminares (anos ainda não consolidados; só :sim e :sinasc).
Ex.: url_arquivo(:sim, "PE"; ano = 2023) → .../SIM/CID10/DORES/DOPE2023.dbc.
MicroSUS.baixar_sinan — Function
baixar_sinan(agravo; ano, prelim = false, forcar = false,
quieto = false) -> String
baixar_sinan(agravo; anos, kwargs...) -> Vector{String}Baixa (com cache) o arquivo nacional do SINAN de um agravo. Se prelim não for informado e o arquivo FINAIS não existir, tenta automaticamente a pasta PRELIM (com aviso). Forma plural baixa vários anos em paralelo.
MicroSUS.url_sinan — Function
url_sinan(agravo; ano, prelim = false) -> StringURL FTP do arquivo NACIONAL do SINAN para um agravo. Os arquivos do SINAN cobrem o Brasil inteiro (DENGBR20.dbc), então não há UF — filtre por residência no ler (SG_UF/ID_MN_RESI). prelim = true aponta para a pasta de dados preliminares.
Agravos: :dengue, :chikungunya, :zika, :meningite, :tuberculose, :hanseniase, :sifilis_congenita, :violencia, … — a lista completa, com o ano inicial de cada um, é agravos_sinan.
Ex.: url_sinan(:dengue; ano = 2020) → .../SINAN/DADOS/FINAIS/DENGBR20.dbc.
MicroSUS.agravos_sinan — Function
agravos_sinan() -> Vector{NamedTuple}Agravos do SINAN disponíveis no pacote: agravo (o símbolo aceito por baixar_sinan e url_sinan), fonte (o identificador em fetch_datasus), prefixo do arquivo nacional (DENG → DENGBR24.dbc), nome e ano_inicial publicado no FTP.
using DataFrames
DataFrame(agravos_sinan())MicroSUS.eh_preliminar — Function
eh_preliminar(caminho) -> Booltrue se o arquivo veio de uma pasta PRELIM/ do DATASUS — dado ainda não consolidado, sujeito a revisão. baixar, baixar_sinan e fetch_datasus guardam esses arquivos em PRELIM/ dentro do cache, separados dos consolidados de mesmo nome.
c = baixar(:sim, "PE"; ano = 2025)
eh_preliminar(c) # true enquanto o DATASUS não consolidar 2025MicroSUS.limpar_cache — Function
limpar_cache()Remove todos os .dbc baixados do cache local, inclusive os preliminares (subpasta PRELIM/).
MicroSUS.ErroDeRede — Type
MicroSUS.ErroDeRedeFalha ao falar com o servidor — timeout, DNS, conexão recusada, canal de dados do FTP bloqueado —, distinta de "o arquivo não existe". É lançada em vez de seguir com um resultado incompleto. causa é o Downloads.RequestError original.
MicroSUS.verificar_cache — Function
verificar_cache(; arquivos = nothing, verbose = true) -> Vector{NamedTuple}Compara os arquivos do cache com o FTP do DATASUS, sem baixá-los, e diz o que fazer com cada um. A situacao de cada arquivo:
:atualizado— o FTP tem o mesmo arquivo (mesmo tamanho);:mudou— o DATASUS republicou o arquivo;baixar(...; forcar = true)oufetch_datasus(...; cache = false)traz a versão nova;:era_preliminar— o arquivo está no cache como consolidado, mas o consolidado não existe no FTP e o preliminar sim: é um preliminar guardado por versões até a 0.3.1 do pacote, que o tratavam como definitivo. Rebaixe comforcar = true;:consolidado_disponivel— preliminar no cache, e o DATASUS já publicou o consolidado; o próximofetch_datasuspassa a usá-lo;:ausente_no_ftp— nenhuma URL candidata existe mais no servidor;:sem_url— o nome não corresponde a nenhuma fonte do catálogo;:sem_resposta— o servidor não respondeu (cada consulta é tentada três vezes); a verificação desse arquivo fica por fazer.
Colunas: arquivo, situacao, bytes_cache, bytes_ftp, url, baixado_em (do registro de origem; para arquivos baixados antes desta versão, a data do arquivo). arquivos restringe a verificação (vetor de nomes ou Regex, como r"^DOPE"). Consulta dois arquivos por vez pelo canal de controle do FTP, que responde mesmo onde o firewall bloqueia as transferências.
A comparação é por tamanho: uma republicação que mantenha exatamente o mesmo número de bytes passa como :atualizado. O FTP do DATASUS não informa a data de modificação por esta via.
v = DataFrame(verificar_cache())
filter(r -> r.situacao in (:mudou, :era_preliminar), v)MicroSUS.proveniencia — Function
proveniencia(df) -> Vector{NamedTuple}Os arquivos de que um resultado de fetch_datasus veio: arquivo, url, baixado_em, bytes, sha256 e preliminar. É o que uma nota de método precisa para que a análise possa ser refeita sobre os mesmos dados — o DATASUS republica bases retroativamente, e a URL sozinha não identifica a versão. Acompanha o DataFrame em cópias e recortes (metadado de estilo :note).
df = fetch_datasus(:SIM_DO; uf = "PE", anos = 2019:2023)
DataFrame(proveniencia(df))Para arquivos baixados antes da 0.4.1, sem registro de origem, baixado_em é a data do arquivo no cache e o SHA-256 é calculado na primeira vez.
MicroSUS.exportar_espelho — Function
exportar_espelho(destino; arquivos = nothing) -> Vector{String}Copia os arquivos do cache para destino, na árvore de pastas do FTP do DATASUS (destino/SIM/CID10/DORES/DOPE2023.dbc), e devolve os caminhos criados. A pasta pode então servir de espelho para outras pessoas — numa rede compartilhada (file://), num bucket ou num servidor HTTP —, via MICROSUS_ESPELHOS:
exportar_espelho("/mnt/grupo/datasus") # quem tem os arquivos
ENV["MICROSUS_ESPELHOS"] = "file:///mnt/grupo/datasus" # quem não alcança o FTP
fetch_datasus(:SIM_DO; uf = "PE", anos = 2023)Por padrão, um espelho só é usado quando o FTP do DATASUS falha por rede; MICROSUS_ESPELHO_PRIMEIRO=true o põe na frente. O DATASUS continua decidindo se um arquivo existe, e o registro de origem de cada download diz se ele veio de um espelho (obtido_de em proveniencia). Um espelho não é verificado contra o DATASUS: o SHA-256 em proveniencia é o que permite conferir que os bytes são os mesmos de quem os baixou — e restaurar_dados faz essa conferência.
arquivos restringe a cópia (nomes, ou um Regex), como em verificar_cache. Arquivos cuja URL de origem não se conhece ficam de fora.
MicroSUS.travar_dados — Function
travar_dados(arquivo, resultados...) -> Vector{NamedTuple}Grava em arquivo (TOML) de quais arquivos do DATASUS vieram os resultados — DataFrames de fetch_datasus, ou o que proveniencia devolve: nome, URL, SHA-256, tamanho, data da extração, se era preliminar e, se veio de um espelho, de onde. É o Manifest.toml dos dados: versione-o junto com o script.
df = fetch_datasus(:SIM_DO; uf = "PE", anos = 2019:2023)
pop = fetch_datasus(:SINASC; uf = "PE", anos = 2019:2023)
travar_dados("dados.toml", df, pop)
# meses depois, ou em outra máquina:
restaurar_dados("dados.toml")
df = fetch_datasus(:SIM_DO; uf = "PE", anos = 2019:2023) # os mesmos bytesVer restaurar_dados.
MicroSUS.restaurar_dados — Function
restaurar_dados(arquivo = "dados.toml"; estrito = true, ativar = true)
-> Vector{NamedTuple}Devolve ao cache os arquivos de um manifest de travar_dados, conferindo cada um pelo SHA-256, e ativa a trava na sessão. Para cada arquivo, a situacao:
:no_cache— já estava no cache com o mesmo hash; nada foi baixado;:restaurado— baixado deorigem(o DATASUS ou um espelho deMICROSUS_ESPELHOS) com o mesmo hash;:diferente— o arquivo existe, mas nenhuma origem tem os mesmos bytes: o DATASUS o republicou e nenhum espelho guardou a versão travada (sha256_obtidoé o hash do que existe hoje);:indisponivel— nenhuma origem respondeu com o arquivo.
O DATASUS não guarda versões antigas: a versão travada de um arquivo republicado só volta de um espelho que a tenha (ver exportar_espelho), ou não volta. Com estrito = true (padrão), qualquer arquivo que não volte com o mesmo hash é erro — a análise não roda sobre outros dados sem que você decida. Com estrito = false, a versão atual do DATASUS entra no cache no lugar e a lista diz quais mudaram.
Com a trava ativa, um arquivo travado vem sempre do cache — fetch_datasus, baixar e baixar_sinan não o buscam no FTP, nem trocam o preliminar travado pelo consolidado — e a proveniencia traz a data da extração original. soltar_dados desativa.
MicroSUS.soltar_dados — Function
soltar_dados()Desativa a trava de restaurar_dados: os downloads voltam a seguir o FTP do DATASUS (e o cache) normalmente.
MicroSUS.baixar_url — Function
baixar_url(url; cache = true, verbose = true) -> Union{String,Nothing}Baixa url para o cache local e devolve o caminho do arquivo, ou nothing se o arquivo não existir no servidor. Erros de rede (timeout, DNS, conexão recusada) viram MicroSUS.ErroDeRede; só a ausência do arquivo é nothing, porque a existência de partições (PA...b.dbc) e de arquivos preliminares só pode ser descoberta tentando.
MicroSUS.UFS — Constant
UFSSiglas das 27 unidades federativas aceitas por url_arquivo e baixar.
Fetch (interface de alto nível)
MicroSUS.fetch_datasus — Function
fetch_datasus(fonte::Symbol; uf = :all, anos, meses = nothing,
colunas = nothing, filtro = nothing,
processar = true, cache = true, verbose = true) -> DataFrameBaixa, descomprime, lê e concatena microdados públicos do DATASUS.
Argumentos
fonte: identificador da fonte — verfontes. Ex.::SIM_DO,:SINASC,:SIH_RD,:SIA_PA,:CNES_ST,:SINAN_DENGUE;uf: sigla ("PE"), vetor de siglas (["PE", "BA"]) ou:allpara as 27 unidades federativas. Ignorado em fontes de abrangência nacional (SINAN);anos: ano (2023) ou coleção de anos (2019:2023);meses: mês ou coleção de meses (1:12), obrigatório apenas para fontes mensais (SIH, SIA, CNES);colunas:Vector{Symbol}com os campos desejados, como emler— os demais nem são lidos. Campos que não existem no layout de algum ano vêmmissingnas linhas dele;filtro: funçãoRegistroDBF -> Boolaplicada a cada registro antes do parse, como emler. Vê os códigos crus do arquivo (r[:SEXO] == "2"), não os rótulos da padronização;processar: aplica a padronização da fonte quando disponível (process_sim,process_sinasc);cache: reutiliza arquivos já baixados (verMicroSUS.limpar_cache);verbose: registra progresso via@info/@warn.
Os arquivos são lidos em streaming, um depois do outro (ver ler(caminhos::AbstractVector)), e a padronização roda no próprio resultado, sem cópia: o pico de memória fica perto do tamanho do DataFrame devolvido. Com colunas e filtro, só o que foi pedido chega a existir. Até 4 arquivos são baixados ao mesmo tempo, e com mais de uma thread (julia -t auto) vários são lidos em paralelo; a ordem das linhas não muda.
Arquivos ausentes no FTP (ano ainda não publicado para uma UF, mês sem partição extra) geram um @warn e são pulados; o resultado concatena tudo que foi encontrado, unindo colunas por nome. As colunas UF_ARQUIVO, ANO_ARQUIVO e, se aplicável, MES_ARQUIVO identificam a origem de cada linha, e PRELIMINAR diz se ela veio de um arquivo ainda não consolidado pelo DATASUS (pasta PRELIM/, ver eh_preliminar) — quando houver algum, um @warn lista quais.
Exemplos
# Óbitos de Pernambuco, 2019–2023, já padronizados
do_pe = fetch_datasus(:SIM_DO; uf = "PE", anos = 2019:2023)
# Nascidos vivos, PE e BA, sem padronização (códigos brutos)
dn = fetch_datasus(:SINASC; uf = ["PE", "BA"], anos = 2022, processar = false)
# Internações hospitalares de PE no primeiro semestre de 2024
rd = fetch_datasus(:SIH_RD; uf = "PE", anos = 2024, meses = 1:6)
# Dengue no Brasil inteiro (fonte nacional: uf é ignorada)
dengue = fetch_datasus(:SINAN_DENGUE; anos = 2024)
# Só o que interessa: óbitos por agressão em PE, três colunas, dez anos
cvli = fetch_datasus(:SIM_DO; uf = "PE", anos = 2014:2023,
colunas = [:DTOBITO, :CAUSABAS, :CODMUNRES],
filtro = r -> eh_agressao(r[:CAUSABAS]))MicroSUS.fontes — Function
fontes() -> Vector{NamedTuple}Lista as fontes de microdados disponíveis no pacote, com identificador, descrição, periodicidade, abrangência e faixa de anos (ano_final é missing para fontes ainda publicadas).
Exemplo
using MicroSUS, DataFrames
DataFrame(fontes())MicroSUS.fonte — Function
fonte(id::Symbol) -> FonteDATASUSDevolve a especificação registrada da fonte id (ver fontes para a lista de identificadores disponíveis). Lança ArgumentError para um identificador desconhecido.
MicroSUS.process_sim — Function
process_sim(df::DataFrame; copiar = true) -> DataFramePadroniza microdados do SIM (:SIM_DO): converte datas (DTOBITO, DTNASC, ...) para Date, rotula variáveis categóricas (sexo, raça/cor, estado civil, escolaridade, local de ocorrência, circunstância do óbito etc.) e cria IDADE_ANOS a partir do campo codificado IDADE.
Colunas ausentes no layout do ano são simplesmente ignoradas; a coluna original IDADE é preservada. Chamado automaticamente por fetch_datasus quando processar = true.
copiar = false padroniza df no lugar, sem a cópia inicial — o que fetch_datasus faz, já que o DataFrame é dele.
MicroSUS.process_sinasc — Function
process_sinasc(df::DataFrame; copiar = true) -> DataFramePadroniza microdados do SINASC (:SINASC): converte datas (DTNASC, DTNASCMAE, ...) para Date, rotula variáveis categóricas (sexo, tipo de parto, gravidez, escolaridade e estado civil da mãe, raça/cor, consultas de pré-natal, local de nascimento) e converte numéricos armazenados como texto (PESO, IDADEMAE, APGAR1, APGAR5, SEMAGESTAC, ...).
Colunas ausentes no layout do ano são ignoradas. Chamado automaticamente por fetch_datasus quando processar = true.
copiar = false padroniza df no lugar, sem a cópia inicial — o que fetch_datasus faz, já que o DataFrame é dele.
MicroSUS.process_sih — Function
process_sih(df::DataFrame; copiar = true) -> DataFramePadroniza microdados do SIH/SUS (:SIH_RD): rotula as variáveis categóricas de domínio fechado (SEXO, RACA_COR, IDENT, CAR_INT, MORTE) e cria IDADE_ANOS a partir de IDADE + COD_IDADE.
Colunas ausentes no layout do ano são ignoradas — o layout do SIH mudou em 2011, 2013 e 2014, e a rotina é escrita para sobreviver a isso. As colunas originais são preservadas.
COBRANCA (motivo de saída) e ESPEC (especialidade do leito) têm domínios extensos que variam entre versões da tabela da AIH. Como rotular! converte código não mapeado em missing, um dicionário incompleto apagaria dados válidos em silêncio — pior que devolver o código cru. Ficam como estão; use as tabelas oficiais da AIH se precisar deles rotulados.
RACA_COR = "99" (sem informação) vira missing, que é o que ele significa.
Chamada automaticamente por fetch_datasus quando processar = true.
copiar = false padroniza df no lugar, sem a cópia inicial — o que fetch_datasus faz, já que o DataFrame é dele.
MicroSUS.process_sinan — Function
process_sinan(df::DataFrame; agravo = :auto, copiar = true) -> DataFramePadroniza microdados do SINAN. Rotula o núcleo comum às fichas de todos os agravos — TP_NOT, CS_SEXO, CS_RACA, CS_GESTANT, CS_ESCOL_N, HOSPITALIZ — e cria IDADE_ANOS (anos completos) a partir de NU_IDADE_N.
CLASSI_FIN, CRITERIO e EVOLUCAO mudam de significado entre agravos e só são rotulados quando o agravo é conhecido: agravo pode ser :dengue, :chikungunya, :zika ou nothing (só o núcleo). Com :auto, o agravo é inferido de ID_AGRAVO quando todas as linhas apontam para um dos três; nos demais (malária, tuberculose, violência…) esses campos ficam como estão.
Códigos com zero à esquerda ("01") valem o mesmo que sem ("1") — os arquivos trazem as duas formas. Códigos de "ignorado" ("9", "I") e não documentados ("0" em CLASSI_FIN) viram missing.
Colunas ausentes são ignoradas e as originais, preservadas. Chamado automaticamente por fetch_datasus quando processar = true, com o agravo da fonte (:SINAN_DENGUE → :dengue).
Os dicionários do núcleo são os do SINAN NET (2007 em diante). Arquivos mais antigos usam outras codificações em alguns campos — TUBEBR01 traz CS_RACA = "0" em 83% dos registros, campo que a ficha da época não coletava. Rotular esses anos exige o dicionário da ficha correspondente.
copiar = false padroniza df no lugar, sem a cópia inicial — o que fetch_datasus faz, já que o DataFrame é dele.
MicroSUS.process_cnes — Function
process_cnes(df::DataFrame; copiar = true) -> DataFramePadroniza microdados do CNES — estabelecimentos (:CNES_ST) e profissionais (:CNES_PF): rotula os campos categóricos presentes (tipo de unidade TP_UNID, esfera administrativa ESFERA_A, natureza NATUREZA/NAT_JUR, nível de hierarquia NIV_HIER, tipo de gestão TPGESTAO, vínculo com o SUS VINC_SUS, turno TURNO_AT, clientela CLIENTEL, os indicadores sim/não de serviços, e mais — 119 campos ao todo). Códigos com e sem zero à esquerda valem o mesmo.
Os dicionários vêm do microdatasus (licença MIT, ver data/LICENSE-microdatasus) e só incluem campos cujos códigos observados em arquivos reais estão todos cobertos. Um código fora do dicionário — que pode aparecer em anos ou UFs não verificados — vira missing com um @warn que diz qual é e em quantos registros.
copiar = false padroniza df no lugar. Chamado automaticamente por fetch_datasus para :CNES_ST e :CNES_PF.
Qualidade dos dados
MicroSUS.auditar — Function
auditar(df; ano = nothing, limiar = 20) -> MicroSUS.AuditoriaChecagens de qualidade de um resultado de fetch_datasus (ou de ler) — o que olhar antes de analisar. Mostra um resumo; os detalhes ficam nos campos de MicroSUS.Auditoria.
df = fetch_datasus(:SIM_DO; uf = "PE", anos = 2014:2023)
a = auditar(df)
a.descontinuidades # campos que pararam (ou começaram) a ser preenchidos
a.causas # % de causas mal definidas por ano
filter(r -> r.ano == 2023, a.completude)- Completude por coluna e ano. Ausente é
missingou texto vazio. Os códigos de "ignorado" (9,0…) só contam como ausência nos dados padronizados (processar = true, o padrão), onde virammissing; nos brutos, são um código como outro. - Descontinuidades: o preenchimento muda
limiarpontos percentuais (padrão 20) ou mais entre um ano e o seguinte — um campo que deixa de existir no layout aparece aqui como queda para 0%. Anos com menos de 10% dos registros do maior (as notificações de 2024 dentro do arquivo do SINAN de 2023) ficam fora da comparação. - Causas básicas (SIM): mal definidas (R00–R99) e códigos que a CID-10 marca como não utilizáveis como causa básica — medidas clássicas da qualidade da declaração de óbito (
cid10) —, e códigos que nem estão na CID-10. - Valores implausíveis: idade acima de 120 anos ou negativa, datas de evento no futuro ou antes de 1900 (de nascimento, só no futuro), nascimento depois do óbito, peso fora de 100–7.000 g, idade da mãe fora de 10–60, semanas de gestação fora de 20–45 (o 99 de "ignorado" não conta), causa básica incompatível com o sexo. São marcas para revisar, não erros certos:
exemplostraz as primeiras linhas de cada regra.
O ano vem de ano (uma coluna), de ANO_ARQUIVO ou da primeira data de evento tipada (DTOBITO, DTNASC, DT_NOTIFIC…); ano = false audita tudo junto.
MicroSUS.Auditoria — Type
Resultado de auditar. Cada campo é um DataFrame:
completude— por coluna e ano:n,preenchidos,pct_preenchido;descontinuidades— colunas cujo preenchimento muda de um ano para o seguinte emlimiarpontos percentuais ou mais (campo que parou ou começou a ser preenchido, ou mudança de layout);causas— por ano, só comCAUSABAS(SIM): causas mal definidas (capítulo XVIII, R00–R99), códigos que não valem como causa básica e códigos fora da CID-10;implausiveis— por regra: quantos registros, a fração e alguns exemplos.
Decodificação de schemas
MicroSUS.decodifica_idade_sim — Function
decodifica_idade_sim(s) -> Union{Missing,Float64}Idade do SIM (campo IDADE, 3 dígitos: 1º = unidade, 2º–3º = valor) convertida para anos:
| 1º dígito | unidade |
|---|---|
| 0 | minutos |
| 1 | horas |
| 2 | dias |
| 3 | meses |
| 4 | anos (0–99) |
| 5 | 100 + valor |
| 9 | ignorada |
Ex.: "425" → 25.0; "501" → 101.0; "310" → 10/12; "999" → missing.
MicroSUS.decodifica_idade_sinan — Function
decodifica_idade_sinan(s) -> Union{Missing,Float64}Idade do SINAN (campo NU_IDADE_N, 4 dígitos: 1º = unidade, 3º–4º = valor 0–99) convertida para anos. Mesma escala de unidades do SIM, mas com 4 dígitos: "4025" → 25.0; "3006" → 0.5 (6 meses); "2015" → 15/365.25 (15 dias); "5010" → 110.0; "999"/inválido → missing.
MicroSUS.idade_sih — Function
idade_sih(idade, cod_idade) -> Union{Int,Missing}Idade em anos completos a partir do par IDADE + COD_IDADE do SIH. O COD_IDADE é a unidade: 0 minutos, 1 horas, 2 dias, 3 meses, 4 anos, 5 anos acima de 100 (IDADE = 5, COD_IDADE = 5 ⇒ 105 anos). Idades abaixo de um ano resultam em 0.
Diferente do SIM, onde unidade e valor vêm no mesmo campo de 3 dígitos (decodifica_idade_sim), aqui são duas colunas — por isso a função recebe dois argumentos.
Aceita tanto os valores já tipados pelo schema quanto o texto cru.
MicroSUS.SCHEMAS — Constant
SCHEMAS :: Dict{Symbol,Dict{Symbol,Symbol}}Registro de schemas por sistema (:sim, :sinasc, :sih, :sia, :cnes): campo → tipo lógico (:texto, :pool, :inteiro, :float, :data_ddmmyyyy, :data_yyyymmdd, :idade_sim). Campos ausentes caem na tipagem do DBF. Mutável de propósito — dá para estender em runtime: MicroSUS.SCHEMAS[:sim][:OCUP] = :texto.
MicroSUS.detecta_sistema — Function
detecta_sistema(caminho) -> Union{Nothing,Symbol}Deduz o sistema pelo prefixo do nome do arquivo (DOPE2023.dbc → :sim, DNPE2023.dbc → :sinasc, RDPE2301.dbc → :sih, ...).
Indicadores
MicroSUS.mortalidade_infantil — Function
mortalidade_infantil(obitos, nascidos; nivel = :uf) -> DataFrameTaxa de mortalidade infantil e seus componentes (RIPSA C.1 a C.1.3), por 1.000 nascidos vivos, por território de residência e ano:
obitos_infantis/taxa— óbitos de menores de 1 ano;neonatal_precoce/taxa_neonatal_precoce— 0 a 6 dias;neonatal_tardia/taxa_neonatal_tardia— 7 a 27 dias;pos_neonatal/taxa_pos_neonatal— 28 dias a menos de 1 ano.
obitos vem do SIM (fetch_datasus(:SIM_DO, …) ou :SIM_DOINF) e nascidos do SINASC (fetch_datasus(:SINASC, …)), com as mesmas UFs e anos. nivel: :brasil, :uf, :municipio, :regiao_saude, :macrorregiao_saude, :regiao_imediata ou :regiao_intermediaria.
do_ = fetch_datasus(:SIM_DO; uf = "PE", anos = 2022)
dn = fetch_datasus(:SINASC; uf = "PE", anos = 2022)
mortalidade_infantil(do_, dn; nivel = :regiao_saude)Método direto, sem os fatores de correção de sub-registro que o Ministério da Saúde aplica onde a cobertura do SIM e do SINASC é incompleta (parte do Norte e do Nordeste): ali a taxa fica abaixo da oficial. Óbitos com idade ignorada ficam fora. Em município pequeno a taxa oscila muito de um ano para o outro — a RIPSA recomenda agregar anos ou usar nível maior.
MicroSUS.razao_mortalidade_materna — Function
razao_mortalidade_materna(obitos, nascidos; nivel = :uf) -> DataFrameRazão de mortalidade materna (RIPSA C.3): óbitos maternos por 100.000 nascidos vivos, por território de residência e ano. Óbito materno é o de causa básica em O00–O95, O98–O99 ou A34 (tétano obstétrico) — o capítulo XV da CID-10 sem as mortes maternas tardias e as sequelas (O96, O97).
razao_mortalidade_materna(do_, dn; nivel = :uf)Como na mortalidade_infantil: método direto, sem fatores de correção — o Ministério da Saúde corrige a RMM pelo sub-registro e pela má classificação das causas, e a razão oficial costuma ficar acima desta. A RIPSA inclui ainda, sob condições (morte durante a gravidez ou o puerpério), F53, M83.0, D39.2, E23.0 e B20–B24; elas não entram aqui. Com poucos óbitos por território, a razão é instável: agregue anos.
MicroSUS.proporcao_mal_definidas — Function
proporcao_mal_definidas(obitos; nivel = :uf) -> DataFrameProporção de óbitos por causas mal definidas: causa básica no capítulo XVIII da CID-10 (R00–R99), em % do total de óbitos, por território de residência e ano. É a medida mais usada da qualidade da declaração da causa de morte: onde ela é alta, qualquer análise por causa subestima as causas de fato. auditar mostra, além disso, os códigos que não valem como causa básica.
MicroSUS.mortalidade_prematura_dcnt — Function
mortalidade_prematura_dcnt(obitos; nivel = :uf, populacao = nothing) -> DataFrameTaxa de mortalidade prematura (30 a 69 anos) pelas quatro principais doenças crônicas não transmissíveis — circulatórias (I00–I99), neoplasias (C00–C97), respiratórias crônicas (J30–J98) e diabetes (E10–E14) —, por 100.000 habitantes de 30 a 69 anos: o indicador do Plano de DANT 2021–2030 e da meta 3.4 dos ODS. Por território de residência e ano.
O denominador vem de populacao_por_idade (SIDRA, com cache): Censos de 2010 e 2022 em qualquer nível; nos demais anos, a projeção do IBGE, só para :uf e :brasil. populacao aceita uma tabela pronta, com a coluna de território do nivel (codigo_uf, codigo6, codigo_regiao_saude…), ano e populacao (já de 30 a 69 anos).
Taxa bruta, como no Plano de DANT; para comparar lugares com estruturas etárias diferentes, padronize (taxa_padronizada).
Dimensões
MicroSUS.dv_ibge — Function
dv_ibge(cod6) -> IntDígito verificador do código de município IBGE (algoritmo módulo 10 com pesos alternados 1,2 e redução de produtos ≥ 10). Aceita Integer ou string de 6 dígitos. Ex.: dv_ibge(261110) == 1 (Petrolina → 2611101).
MicroSUS.codigo7_ibge — Function
codigo7_ibge(cod6) -> IntCódigo de 7 dígitos a partir do de 6 (SIM/SINASC usam 6; IBGE moderno usa 7). codigo7_ibge(261110) == 2611101. O dígito verificador vem da tabela oficial (municipios) — nove municípios fogem do algoritmo de dv_ibge — e do algoritmo para códigos fora dela.
MicroSUS.codigo6_ibge — Function
codigo6_ibge(cod7; validar = true) -> IntCódigo de 6 dígitos a partir do de 7, opcionalmente validando o dígito verificador (contra a tabela oficial, como em codigo7_ibge).
MicroSUS.uf_de — Function
uf_de(cod) -> Union{String,Missing}Sigla da UF de um código IBGE — município de 6 ou 7 dígitos, ou o próprio código de 2 dígitos da UF. uf_de("261160") == "PE", uf_de(53) == "DF". Código vazio, não numérico ou de UF inexistente dá missing.
MicroSUS.regiao — Function
regiao(x) -> Union{String,Missing}Grande região ("Norte", "Nordeste", "Sudeste", "Sul", "Centro-Oeste") a partir da sigla da UF (regiao("PE")) ou de um código IBGE de UF ou município (regiao(2611606)). Desconhecido dá missing.
MicroSUS.municipio — Function
municipio(cod) -> Union{NamedTuple,Nothing}Município do código IBGE cod, de 6 dígitos (como no SIM, SINASC e SIH) ou de 7 (com dígito verificador), inteiro ou texto:
m = municipio("261160") # (codigo7 = 2611606, codigo6 = 261160, nome = "Recife",
# uf = "PE", regiao = "Nordeste", …)
m.regiao_saude # "I Região de Saúde"
m.regiao_imediata # "Recife"Devolve nothing para código vazio, ignorado ou fora da tabela (ver municipios). Um código de 7 dígitos com dígito verificador errado também dá nothing.
MicroSUS.municipios — Function
municipios() -> Vector{NamedTuple}Tabela dos municípios brasileiros embarcada no pacote (IBGE, 5.571 linhas contando Brasília e Fernando de Noronha), sem acesso à rede: colunas codigo7, codigo6, nome, uf, regiao e as divisões abaixo da UF, cada uma com código e nome:
codigo_regiao_saude,regiao_saude— a região de saúde (CIR) do SUS, onde se pactua a rede de atenção (439, com o Distrito Federal como uma só);codigo_macrorregiao_saude,macrorregiao_saude— a macrorregião de saúde, que agrupa regiões de saúde (121);codigo_regiao_imediata,regiao_imediataecodigo_regiao_intermediaria,regiao_intermediaria— a divisão regional do IBGE de 2017 (510 e 133), que substituiu micro e mesorregiões.
Nomes se repetem entre UFs (há região de saúde "Norte" e "Central" em várias, e região imediata "Valença" na BA e no RJ): agrupe pelo código. É uma tabela Tables.jl — DataFrame(municipios()) — pronta para leftjoin com codigo6 contra CODMUNRES (SIM, SINASC) ou MUNIC_RES (SIH) convertidos para inteiro:
mun = DataFrame(municipios())
df.codigo6 = parse.(Int, df.CODMUNRES)
leftjoin!(df, mun[:, [:codigo6, :codigo_regiao_saude, :regiao_saude]]; on = :codigo6)
obitos = combine(groupby(df, [:codigo_regiao_saude, :regiao_saude]), nrow => :obitos)e populacao dá o denominador no mesmo nível (nivel = :regiao_saude).
Fontes: IBGE (API de localidades) para nomes e regiões geográficas; DATASUS (as tabelas territoriais do TabNet) para regiões e macrorregiões de saúde, que mudam por pactuação nas CIBs — a tabela é a de outubro de 2026.
Reflete a divisão territorial atual: municípios extintos ou desmembrados em anos antigos, e os códigos "ignorado" do DATASUS ("260000", "000000"), não estão nela.
MicroSUS.capitulo_cid10 — Function
capitulo_cid10(cod) -> Union{Nothing,NamedTuple}Capítulo CID-10 de um código como "X954" ou "I219": (numeral = "XX", nome = "Causas externas ..."), ou nothing se o código for inválido/vazio.
MicroSUS.cid10 — Function
cid10(cod) -> Union{NamedTuple,Nothing}O registro da CID-10 de cod ("I219", "I21.9", "I21", "I10X"):
codigo,descricao— do código pedido ou, se ele não está na tabela, da sua categoria;nivel—:subcategoria,:categoria, ou:categoria_da_subcategoriaquando o código é posterior à tabela e a descrição veio do nível acima;categoria,descricao_categoria,grupo(o agrupamento, como "Doenças isquêmicas do coração"),capitulo(algarismo romano);sexo—'M'/'F'para códigos restritos a um sexo,missingse não;causa_basica_valida—falsepara os 1.291 códigos que a tabela marca como não utilizáveis como causa básica de óbito (úteis para medir a qualidade da declaração);origem—"DATASUS 2008"ou, para a dengue (A97), o suplemento da OMS de 2016.
nothing para código vazio ou desconhecido.
cid10("I21.9").descricao # "Infarto agudo do miocárdio não especificado"
cid10("I21.9").grupo # "Doenças isquêmicas do coração"
cid10("R99").causa_basica_valida # true — mas veja `capitulo`: XVIII, mal definidasA tabela é a última publicada pelo DATASUS em CSV (versão 2008). Códigos criados depois caem na descrição da categoria; ver descricao_cid.
MicroSUS.descricao_cid — Function
descricao_cid(cod) -> Union{String,Missing}A descrição em português de um código da CID-10, para rotular tabelas: descricao_cid.(df.CAUSABAS). Códigos posteriores à tabela de 2008 do DATASUS recebem a descrição da categoria (ver cid10, campo nivel); código vazio ou desconhecido dá missing.
MicroSUS.normaliza_cid — Function
normaliza_cid(cid) -> StringForma canônica de um código CID-10: maiúsculas, sem ponto, espaço, * ou -. normaliza_cid(" a81.0 ") == "A810"; missing vira "".
MicroSUS.cid_casa — Function
cid_casa(cid, alvos) -> Booltrue se o código cid (um campo como CAUSABAS ou DIAG_PRINC) cai em algum dos alvos. Cada alvo é um prefixo ("A81" pega A81.0–A81.9; "A810" só A81.0) ou uma faixa inclusiva de categorias ("X85" => "Y09"). O código é normalizado antes (normaliza_cid); missing e vazio dão false.
Feito para o filtro de ler:
dcj = ["A810", "F021"]
ler(caminho; filtro = r -> cid_casa(r[:CAUSABAS], dcj))MicroSUS.cids_em — Function
cids_em(texto) -> Vector{String}Códigos CID-10 contidos num campo de texto livre com vários códigos, como as linhas da Declaração de Óbito do SIM (LINHAA…LINHAD, LINHAII), onde vêm concatenados: cids_em("*I219*E149") == ["I219", "E149"].
MicroSUS.menciona_cid — Function
menciona_cid(texto, alvos) -> Booltrue se algum código de cids_em(texto) casa com alvos (mesmas regras de cid_casa). Serve para causas múltiplas: um óbito em que a doença aparece na cadeia de causas, mas não como causa básica.
linhas = (:LINHAA, :LINHAB, :LINHAC, :LINHAD, :LINHAII)
ler(caminho; colunas = [:CAUSABAS, linhas...],
filtro = r -> cid_casa(r[:CAUSABAS], dcj) ||
any(l -> menciona_cid(r[l], dcj), linhas))Os códigos são separados um a um antes de comparar: um alvo nunca casa com um pedaço formado pela junção de dois códigos vizinhos.
MicroSUS.eh_agressao — Function
eh_agressao(cid) -> Booltrue se a causa básica é agressão (homicídio): X85–Y09, mais Y87.1 (sequelas de agressões) — o recorte usual de CVLI a partir do SIM. Equivale a cid_casa(cid, ("X85" => "Y09", "Y871")).
MicroSUS.populacao — Function
populacao(anos; nivel = :municipio, interpolar = false, cache = true)
-> Vector{NamedTuple}População residente do IBGE, por município (nivel = :municipio), UF (:uf) ou Brasil (:brasil), para um ano ou coleção de anos — o denominador para transformar contagens em taxas. Vem da API SIDRA na primeira chamada e fica em cache local.
Colunas: codigo7 e codigo6 (município; o de 6 dígitos casa com CODMUNRES do SIM/SINASC e MUNIC_RES do SIH) ou codigo_uf (UF), mais nome, ano, populacao e fonte. É uma tabela Tables.jl: DataFrame(populacao(2019:2021)).
Também por região de saúde (:regiao_saude), macrorregião de saúde (:macrorregiao_saude) e regiões imediata e intermediária do IBGE (:regiao_imediata, :regiao_intermediaria), somando os municípios pela tabela de municipios: a coluna de código tem o mesmo nome que lá (codigo_regiao_saude…), para o join, e vem com nome e uf. A composição das regiões é a atual em qualquer ano pedido.
pop = DataFrame(populacao(2021))
obitos = combine(groupby(df, :CODMUNRES), nrow => :obitos)
obitos.codigo6 = parse.(Int, obitos.CODMUNRES)
t = innerjoin(obitos, pop; on = :codigo6)
t.taxa = 100_000 .* t.obitos ./ t.populacaoA série não é homogênea
Cada ano vem da fonte que o IBGE publicou para ele, registrada na coluna fonte: Censo (2000, 2010, 2022), Contagem (2007) ou estimativa anual (os demais). As estimativas de 2011–2021 superestimaram a população — o Censo 2022 achou 203,1 milhões contra 213,3 milhões estimados para 2021, e −10,4% em Recife —, então uma taxa calculada com elas cai artificialmente de 2021 para 2022 sem que nada tenha mudado no numerador. Séries longas pedem retroprojeção (que o IBGE ainda não publicou para os municípios) ou, no mínimo, uma nota.
2023
O IBGE não publicou população para 2023. Com interpolar = false (padrão) pedir 2023 é erro; com true, sai a interpolação geométrica entre os anos publicados vizinhos (Censo 2022 e estimativa 2024), com a fonte dizendo isso. Atenção: a estimativa de 2024 não está na mesma base que o Censo 2022 (Brasil: 203,1 → 212,6 milhões em dois anos), e a interpolação herda a diferença.
Anos antes de 2000 não estão disponíveis. cache = false consulta a SIDRA de novo (o IBGE revisa estimativas de tempos em tempos).
MicroSUS.populacao_por_idade — Function
populacao_por_idade(ano; nivel = :uf, largura = 5, aberta = 80, cache = true)
-> Vector{NamedTuple}População residente do IBGE por sexo e faixa etária — o denominador das taxas específicas por idade e da padronização (ver taxa_padronizada).
| ano | nível | fonte |
|---|---|---|
| 2010, 2022 | :municipio, :uf, :brasil e os regionais | Censo |
| demais, 2000–2060 | :uf, :brasil | projeção da população, revisão 2018 |
Os níveis regionais — :regiao_saude, :macrorregiao_saude, :regiao_imediata, :regiao_intermediaria — somam os municípios, como em populacao, e por isso só existem nos anos de Censo.
Colunas: o código do território (codigo7/codigo6, codigo_uf, codigo_regiao_saude e afins com uf, ou nenhum), nome, ano, sexo ("Masculino"/"Feminino"), faixa ("0 a 4 anos" … "80 anos ou mais", como em faixa_etaria), idade_min, populacao e fonte.
Fora dos anos de Censo, os números vêm da projeção do IBGE revista em 2018, que superestima a população como as estimativas de 2011–2021 (Brasil: 213,3 milhões projetados para 2021 contra 203,1 milhões recenseados em 2022). Para padronizar — onde importam as proporções entre faixas — o efeito é menor que para contar habitantes; a coluna fonte registra de onde veio cada número.
largura e aberta definem as faixas; aberta não pode passar do grupo aberto da fonte (90 na projeção, 100 nos Censos). A consulta à SIDRA é por idade simples e fica em cache; no nível municipal, uma UF por vez.
MicroSUS.faixa_etaria — Function
faixa_etaria(idade; largura = 5, aberta = 80) -> Union{String,Missing}Faixa etária de uma idade em anos completos, no formato de populacao_por_idade: "0 a 4 anos", "5 a 9 anos", …, "80 anos ou mais". Use as mesmas largura e aberta dos dois lados para que o numerador (os casos) e o denominador (a população) casem.
faixa_etaria(37) # "35 a 39 anos"
faixa_etaria(91) # "80 anos ou mais"
faixa_etaria.(df.IDADE_ANOS) # a coluna IDADE_ANOS de process_sim/sih/sinanMicroSUS.taxa_padronizada — Function
taxa_padronizada(casos, populacao, padrao; por = 100_000) -> NamedTupleTaxa padronizada por idade pelo método direto: a taxa que a população estudada teria se tivesse a estrutura etária de padrao. Os três vetores são por faixa, na mesma ordem: casos (eventos), populacao (a do denominador) e padrao (a população-padrão, ou só os seus pesos).
Devolve taxa (padronizada), bruta (casos/população), erro_padrao (aproximação de Poisson: por² · Σ wᵢ² · casosᵢ / populaçãoᵢ²) e pesos.
pop = DataFrame(populacao_por_idade(2022; nivel = :uf))
br = DataFrame(populacao_por_idade(2022; nivel = :brasil)) # padrão: Brasil, Censo 2022
# casos por faixa (com faixa_etaria nos microdados), alinhados às faixas de `pop`…
taxa_padronizada(casos, pe.populacao, br.populacao)Para eventos raros, com poucos casos por faixa, o erro-padrão de Poisson subestima a incerteza; intervalos exatos (gama, Fay & Feuer 1997) pedem outra ferramenta.
Estruturas DBF
MicroSUS.CabecalhoDBF — Type
CabecalhoDBFCabeçalho de um DBF/DBC: contagem de registros, tamanhos do cabeçalho e do registro, language driver (ldid, decide o encoding) e os CampoDBF na ordem do arquivo (+ um índice por nome). Obtenha com MicroSUS.cabecalho sem ler nenhum dado.
MicroSUS.CampoDBF — Type
CampoDBFDescritor de um campo DBF: nome, tipo ('C' texto, 'N' numérico, 'D' data aaaammdd, 'F', 'L'), largura, decimais e offset (0-based dentro do registro; o byte 0 é a flag de deleção).
MicroSUS.cabecalho — Function
cabecalho(caminho) -> CabecalhoDBFLê apenas o cabeçalho de um .dbc ou .dbf (campos, larguras, contagem de registros), sem descomprimir nem tocar nos dados.
É o primeiro comando de qualquer análise multi-ano: o layout do DATASUS muda entre anos, e conferir treze cabeçalhos custa menos que uma leitura.
cab = cabecalho("RDPE2201.dbc")
cab.n_registros # 48562
[c.nome for c in cab.campos] # nomes disponíveis
:DIAGSEC1 in keys(cab.indice) # o campo existe neste ano?Para ler apenas as colunas que existem em cada arquivo de uma série, combine com ler(...; ignorar_ausentes = true).
Baixo nível
MicroSUS.dcl_descomprime — Function
dcl_descomprime(io::IO, sink) -> IntDescomprime um fluxo PKWare DCL ("implode") lido de io, chamando sink(chunk::AbstractVector{UInt8}) com blocos de até 4096 bytes à medida que a janela enche. Retorna o total de bytes descomprimidos. Memória constante (janela de 4 KiB). Porte do blast.c.
dcl_descomprime(io::IO) -> Vector{UInt8}Versão de conveniência que materializa tudo num vetor (para arquivos pequenos ou testes). Prefira a versão com sink em produção.