Capítulo 30 Dados do DATASUS
Este capítulo é dirigido principalmente a quem é da área da saúde, mas vale a leitura para todo mundo: ele mostra como se chega a um banco de dados nacional, real e gratuito, sem coletar nada.
O DATASUS é o departamento de informática do SUS, e ele publica os microdados de vários sistemas de informação em saúde. “Microdado” quer dizer que cada linha é um caso — um nascimento, um óbito, uma internação — e não um total já resumido. É isso que permite fazer as próprias análises em vez de depender das tabelas que alguém já montou.
30.1 Os principais sistemas
| Sigla | O que registra | Uma pergunta que ele responde |
|---|---|---|
| SINASC | Nascidos vivos | O peso ao nascer varia com a escolaridade da mãe? |
| SIM | Óbitos | Qual a taxa de mortalidade por causa externa entre jovens? |
| SIH | Internações hospitalares do SUS | Quanto tempo dura, em média, uma internação por AVC? |
| SIA | Procedimentos ambulatoriais | Quantas consultas especializadas são feitas por região? |
| SINAN | Doenças de notificação compulsória | Como a dengue se distribui ao longo do ano? |
| CNES | Estabelecimentos de saúde | Quantos leitos de UTI existem por município? |
Todos são de acesso público e cobrem o país inteiro, com séries que em alguns casos começam nos anos 1990.
30.2 O pacote microdatasus
Os arquivos do DATASUS vêm em um formato antigo e compactado (.dbc), que não abre direto no R. O pacote microdatasus, criado por pesquisadores brasileiros, resolve isso: ele baixa, descompacta e ainda traduz os códigos numéricos para rótulos legíveis.
O pacote foi desenvolvido por Raphael Saldanha, Ronaldo Bastos e Christovam Barcellos, e descrito em artigo nos Cadernos de Saúde Pública (2019). Se você usá-lo no seu trabalho, cite o artigo — é assim que quem constrói ferramenta gratuita para a comunidade recebe crédito. A referência está no fim do livro, e o próprio R mostra a forma correta com
citation("microdatasus").
O uso tem sempre dois passos. Primeiro fetch_datasus() baixa os dados brutos:
bruto <- fetch_datasus(
year_start = 2022,
year_end = 2022,
uf = "MG",
information_system = "SINASC"
)Depois, uma função process_*() correspondente ao sistema traduz os códigos:
Sem esse segundo passo, o sexo do recém-nascido vem como 1 e 2, e o tipo de parto como 1 e 2, em vez de “Masculino”/“Feminino” e “Vaginal”/“Cesáreo”.
Existe uma função
process_para cada sistema:process_sinasc(),process_sim(),process_sih(),process_sia(),process_cnes()e uma para cada agravo do SINAN, comoprocess_sinan_dengue().
30.3 Duas armadilhas do SINASC
As duas apareceram ao montar o banco de nascimentos que usamos no livro, e nenhuma delas está descrita de forma clara na documentação.
30.3.1 O código de “ignorado”
O DATASUS não deixa campos em branco: ele preenche o desconhecido com códigos como 9999, 99 ou 9. Se você calcular uma média sem filtrar isso, o resultado sai completamente errado — imagine um peso ao nascer de 9.999 gramas entrando na conta.
Por isso, antes de qualquer análise, restrinja cada variável a valores plausíveis:
Tudo que estiver fora da faixa vira NA, e aí você trata como aprendemos no capítulo de importação, com na.rm = TRUE.
30.3.2 O process_sinasc() zera o peso
Esta é específica e traiçoeira: a função process_sinasc() devolve a coluna PESO inteiramente vazia. O dado está perfeito no banco bruto, mas se perde no processamento.
A solução é ler o peso do bruto e o resto do rotulado:
A lição vale além deste caso: depois de qualquer processamento, confira se as suas variáveis continuam lá. Um
summary()ou umcolSums(is.na(seu_banco))mostram na hora se alguma coluna virou sóNA. Foi assim que este problema apareceu.
30.4 O banco de nascimentos deste livro
O nascimentos.csv que usamos nos exemplos foi montado exatamente desse jeito: 3.602 nascimentos registrados em Uberaba em 2022, filtrados a partir dos 235.063 de Minas Gerais.
Todo esse caminho está reunido em um script pronto: dados-nascimentos.R. Nós o percorreremos passo a passo na próxima seção. Para usá-lo direto, basta trocar duas linhas:
Trocando para o seu estado e o seu município, você tem em minutos um banco da sua realidade local — o que costuma render trabalhos muito mais interessantes que analisar dados de outro lugar.
A filtragem por município usa a coluna
munResNome, que oprocess_sinasc()acrescenta com o nome do município de residência da mãe.
30.5 Do DATASUS até o seu arquivo
Até aqui vimos os comandos soltos. Agora vamos juntar tudo: baixar do DATASUS, organizar e salvar em um arquivo que você abre depois, no R ou no Excel, sem precisar baixar de novo.
É este o caminho que produziu o nascimentos.csv usado neste livro. Os blocos abaixo não são executados quando o livro é gerado, para não baixar 235 mil registros a cada vez, mas você pode copiá-los e rodar.
30.5.1 Passo 1: baixar
library(microdatasus)
bruto <- fetch_datasus(
year_start = 2022,
year_end = 2022,
uf = "MG",
information_system = "SINASC"
)
nrow(bruto)
#> [1] 235063São todos os nascimentos de Minas Gerais em 2022. O download leva alguns minutos.
30.5.3 Passo 3: ficar só com o seu município
# a coluna munResNome vem do process_sinasc()
sel <- which(rotulado$munResNome == "Uberaba")
bruto <- bruto[sel, ]
rotulado <- rotulado[sel, ]
nrow(rotulado)
#> [1] 3602Troque "Uberaba" pelo nome do seu município. Se quiser o estado inteiro, é só pular este passo.
30.5.4 Passo 4: montar a tabela com as variáveis que interessam
Aqui escolhemos as colunas, damos nomes em português e limpamos os códigos de “ignorado” que vimos há pouco. Repare que o peso vem do banco bruto, pelo motivo explicado na seção das armadilhas.
library(dplyr)
nascimentos <- tibble(
peso_g = as.numeric(bruto$PESO), # do bruto: o rotulado zera
idade_mae = as.numeric(bruto$IDADEMAE),
sem_gestacao = as.numeric(bruto$SEMAGESTAC),
consultas_pn = as.numeric(bruto$CONSPRENAT),
apgar5 = as.numeric(bruto$APGAR5),
sexo = rotulado$SEXO,
parto = rotulado$PARTO,
escol_mae = rotulado$ESCMAE,
raca_mae = rotulado$RACACOR
) |>
mutate( # fora destas faixas é código de ignorado, então vira NA
peso_g = ifelse(peso_g %in% 250:7000, peso_g, NA),
idade_mae = ifelse(idade_mae %in% 10:60, idade_mae, NA),
sem_gestacao = ifelse(sem_gestacao %in% 20:45, sem_gestacao, NA),
consultas_pn = ifelse(consultas_pn %in% 0:40, consultas_pn, NA),
apgar5 = ifelse(apgar5 %in% 0:10, apgar5, NA)
)
dim(nascimentos)
#> [1] 3602 930.5.5 Passo 5: salvar o arquivo
Agora o que interessa: gravar em disco. Há dois formatos úteis, e vale salvar nos dois.
Em CSV, que é o formato universal, lido por qualquer programa:
Em Excel, que é o que a maioria das pessoas do seu grupo de pesquisa vai querer abrir:
# install.packages("writexl") se ainda não tiver
library(writexl)
write_xlsx(nascimentos, "nascimentos.xlsx")O writexl é a opção mais simples para gravar Excel: não exige Java nem nenhuma instalação fora do R.
Onde os arquivos foram parar? Na pasta do seu Projeto do RStudio, a mesma de que falamos no capítulo de importação. Eles aparecem na aba Files, no canto inferior direito da tela. Se você não estiver dentro de um Projeto, vão para a pasta de trabalho atual, que você descobre com
getwd().
30.5.6 Passo 6: ler de volta
Feito isso, o download não precisa ser repetido nunca mais. Da próxima vez, comece daqui:
# do CSV
nascimentos <- read_csv("nascimentos.csv")
# ou do Excel
library(readxl)
nascimentos <- read_excel("nascimentos.xlsx")Os dois devolvem exatamente a mesma tabela: 3.602 linhas e 9 colunas, com média de peso ao nascer de 3.095,8 g.
Por que salvar em vez de baixar toda vez? Por três motivos. É muito mais rápido. Funciona sem internet. E, principalmente, congela os dados: o DATASUS revisa suas bases periodicamente, então um download feito daqui a seis meses pode não dar exatamente os mesmos números. Guardar o arquivo com o qual você fez as análises é parte da reprodutibilidade da sua pesquisa — assunto do capítulo anterior.
30.6 Um exemplo do começo ao fim
Vamos percorrer uma análise inteira, do jeito que você faria em um trabalho de verdade. A pergunta é simples e tem importância de saúde pública:
Bebês de mães adolescentes nascem com peso menor?
É uma hipótese que parece óbvia, e é justamente por isso que vale testá-la.
30.6.1 Passo 1: trazer os dados
library(readr)
library(dplyr)
nascimentos <- read_csv("https://ana-mat-br.github.io/dados/nascimentos.csv")
# quantos registros temos?
nrow(nascimentos)## [1] 3602
30.6.2 Passo 2: criar o grupo que vamos comparar
O banco traz a idade da mãe em anos, mas a nossa pergunta é sobre dois grupos. Precisamos, então, criar uma variável qualitativa a partir de uma quantitativa. A função ifelse() faz isso: testa uma condição e devolve um valor se ela for verdadeira e outro se for falsa.
nascimentos$grupo <- ifelse(nascimentos$idade_mae < 20,
"Adolescente",
"20 anos ou mais")
table(nascimentos$grupo)##
## 20 anos ou mais Adolescente
## 3240 362
São 362 mães adolescentes e 3.240 com 20 anos ou mais. Os grupos ficaram bem desiguais, e vale entender o que isso significa antes de seguir.
O grupo pequeno é quem manda. Quando os grupos têm tamanhos diferentes, a capacidade de detectar diferenças fica limitada pelo menor deles. Não adianta ter 3.240 pessoas de um lado se do outro há 362: a comparação só enxerga o que 362 permitem enxergar.
Dá para medir isso. Comparando o menor efeito que cada arranjo detectaria, com 80% de poder:
| Arranjo dos grupos | Total | Menor diferença detectável |
|---|---|---|
| 362 e 3.240 (o nosso) | 3.602 | 89 g |
| 1.801 e 1.801 (balanceado, mesmo total) | 3.602 | 54 g |
| 362 e 362 (balanceado pelo menor) | 724 | 120 g |
| 362 e 1.000.000 | 1.000.362 | 87 g |
Três leituras saem daí.
A primeira: um estudo balanceado seria melhor. Com as mesmas 3.602 pessoas, mas divididas ao meio, detectaríamos 54 gramas em vez de 89. É por isso que, quando você planeja uma coleta e pode escolher quantas pessoas põe em cada grupo, grupos iguais rendem mais.
A segunda: mesmo assim, não jogue dados fora para equilibrar. Sortear 362 mães do grupo grande, só para ficar simétrico, pioraria tudo — passaríamos a detectar apenas 120 gramas. As 3.240 ajudam, só não ajudam tanto quanto ajudariam se estivessem distribuídas de outro jeito.
A terceira: crescer só o grupo grande resolve pouco. Repare na última linha: mesmo com um milhão de mães no segundo grupo, a diferença detectável cai de 89 para 87 gramas. Com o grupo pequeno preso em 362, não há tamanho do outro lado que salve.
E podíamos ter equilibrado? Não. Este é um estudo observacional: nós não decidimos quantas mães adolescentes tiveram filhos em Uberaba em 2022, apenas registramos o que aconteceu. O desbalanceamento aqui é uma característica da população, não um defeito do estudo. Equilibrar grupos é uma decisão de planejamento, e só existe quando você monta os grupos, como em um experimento.
Um detalhe técnico útil: grupos desiguais combinados com variâncias diferentes são a situação em que o teste t mais precisa da correção de Welch, aquela do argumento
var.equalque vimos no capítulo de comparação entre dois grupos. Aqui as variâncias de fato diferem um pouco (teste F, p = 0,04), e por sorte o R já faz a coisa certa: ot.test()usa Welch por padrão. É por isso que a saída dirá Welch Two Sample t-test.
30.6.3 Passo 3: olhar antes de testar
Nunca aplique um teste sem antes olhar os dados. Comece pelas medidas resumo, separadas por grupo:
aggregate(peso_g ~ grupo, data = nascimentos,
FUN = function(x) c(n = length(x),
media = round(mean(x), 1),
dp = round(sd(x), 1),
mediana = median(x)))## grupo peso_g.n peso_g.media peso_g.dp peso_g.mediana
## 1 20 anos ou mais 3240.0 3098.2 578.0 3165.0
## 2 Adolescente 362.0 3074.5 531.2 3080.0
E depois pelo boxplot, que mostra a distribuição inteira:
boxplot(peso_g ~ grupo, data = nascimentos,
xlab = "", ylab = "Peso ao nascer (g)",
col = c("lightblue", "lightpink"))
As duas caixas são muito parecidas. A diferença entre as médias é de apenas 24 gramas. Guarde essa impressão: o teste vai confirmá-la.
30.6.4 Passo 4: escolher o teste
Seguindo o fluxograma do capítulo de comparação entre dois grupos: são dois grupos, não pareados (cada bebê está em um grupo só) e a variável de resposta é quantitativa. Falta decidir entre o teste t e o de Wilcoxon, e para isso verificamos a normalidade dentro de cada grupo — é sobre cada grupo, e não sobre a variável inteira, que a suposição se aplica.
Comece pelo gráfico QQ, que é o mais informativo. Vamos fazer um para cada grupo, lado a lado:
# separa os dois grupos
adolescente <- nascimentos$peso_g[nascimentos$grupo == "Adolescente"]
adultas <- nascimentos$peso_g[nascimentos$grupo == "20 anos ou mais"]
# dois gráficos na mesma linha
par(mfrow = c(1, 2))
qqnorm(adolescente, main = "Mães adolescentes")
qqline(adolescente, col = "red", lwd = 2)
qqnorm(adultas, main = "Mães com 20 anos ou mais")
qqline(adultas, col = "red", lwd = 2)
Nos dois grupos o padrão se repete: o miolo dos pontos acompanha bem a linha, mas a ponta de baixo despenca. Aquela curva para fora, no canto inferior esquerdo, são os bebês prematuros e de muito baixo peso, valores bem mais extremos do que uma distribuição normal produziria. Do lado de cima não há nada equivalente, e é justamente isso que caracteriza uma assimetria à esquerda.
Agora o teste, para cada grupo:
##
## Anderson-Darling normality test
##
## data: adolescente
## A = 1.7845, p-value = 0.0001425
##
## Anderson-Darling normality test
##
## data: adultas
## A = 51.681, p-value < 2.2e-16
Os dois valores de p são pequenos, confirmando o que o gráfico já mostrava: nenhum dos grupos tem distribuição normal.
Por que olhar o gráfico se o teste já responde? Porque o teste diz apenas sim ou não, enquanto o gráfico mostra onde e quanto a distribuição se afasta da normal. Aqui ele revelou que o desvio está em uma cauda só — e é essa informação que vai explicar, daqui a dois passos, por que os dois testes discordam.
Com amostras deste tamanho, o teste t costuma continuar confiável mesmo assim. Mas em vez de confiar em uma regra de bolso, vamos fazer o que resolve a dúvida de vez: rodar os dois testes e comparar.
30.6.5 Passo 5: fazer os dois testes
##
## Welch Two Sample t-test
##
## data: peso_g by grupo
## t = 0.7957, df = 461.9, p-value = 0.4266
## alternative hypothesis: true difference in means between group 20 anos ou mais and group Adolescente is not equal to 0
## 95 percent confidence interval:
## -34.74361 82.02469
## sample estimates:
## mean in group 20 anos ou mais mean in group Adolescente
## 3098.160 3074.519
##
## Wilcoxon rank sum test with continuity correction
##
## data: peso_g by grupo
## W = 624130, p-value = 0.0446
## alternative hypothesis: true location shift is not equal to 0
E aqui acontece uma coisa que não estava no roteiro: os dois testes discordam. O teste t dá p = 0,43, e o de Wilcoxon dá p = 0,045.
Isso não é erro de nenhum dos dois testes, e entender por que acontece ensina mais do que qualquer um dos dois resultados sozinho.
30.6.6 Passo 6: por que eles discordam
Os dois testes respondem a perguntas diferentes. O teste t compara médias. O de Wilcoxon compara postos, ou seja, é sensível a um deslocamento da distribuição como um todo. Quando os dados são simétricos, os dois dão praticamente o mesmo resultado. Quando são assimétricos, podem divergir — e foi o que ocorreu.
Olhe as duas medidas de centro lado a lado:
aggregate(peso_g ~ grupo, data = nascimentos,
FUN = function(x) c(media = round(mean(x), 1),
mediana = median(x)))## grupo peso_g.media peso_g.mediana
## 1 20 anos ou mais 3098.2 3165.0
## 2 Adolescente 3074.5 3080.0
A diferença entre as médias é de 24 gramas. A diferença entre as medianas é de 85 gramas, quase quatro vezes maior. Como isso é possível?
# proporção de bebês com mais de 4 kg em cada grupo
prop.table(table(nascimentos$grupo, nascimentos$peso_g > 4000), margin = 1)##
## FALSE TRUE
## 20 anos ou mais 0.97407407 0.02592593
## Adolescente 0.95856354 0.04143646
Está aí a explicação: as mães adolescentes têm proporcionalmente mais bebês acima de 4 quilos (4,1% contra 2,6%). Esses poucos bebês muito pesados puxam a média do grupo para cima e escondem o fato de que o miolo da distribuição está deslocado para baixo. A mediana não se deixa levar por eles, e por isso enxerga a diferença.
A lição: quando a variável é assimétrica, média e mediana contam histórias diferentes, e escolher só uma delas pode mudar a sua conclusão. Rodar os dois testes leva dez segundos e evita esse tipo de armadilha. Se eles concordarem, ótimo. Se discordarem, você acabou de descobrir algo sobre a forma dos seus dados.
30.6.7 Passo 7: então, qual é a conclusão?
Nenhuma das duas, sozinha. É preciso olhar o tamanho da diferença, e não só o valor de p.
# o Wilcoxon também estima a diferença, não só o valor de p
wilcox.test(peso_g ~ grupo, data = nascimentos, conf.int = TRUE)##
## Wilcoxon rank sum test with continuity correction
##
## data: peso_g by grupo
## W = 624130, p-value = 0.0446
## alternative hypothesis: true location shift is not equal to 0
## 95 percent confidence interval:
## 7.264444e-05 1.050001e+02
## sample estimates:
## difference in location
## 54.99998
O deslocamento estimado é de 55 gramas, com intervalo de confiança que vai de 0 a 105 gramas. Repare que o limite inferior encosta no zero: é por isso que o valor de p ficou em 0,045, logo abaixo do corte, e não muito abaixo dele.
Junte as peças. Há um sinal de que bebês de mães adolescentes são um pouco mais leves, mas ele é fraco (p no limite, intervalo encostando no zero) e pequeno (55 gramas, quando um recém-nascido pesa em média mais de três quilos). Cinquenta e cinco gramas não mudam a conduta clínica de ninguém.
Como reportar:
O peso ao nascer de filhos de mães adolescentes (n = 362; mediana 3.080 g) foi comparado ao de filhos de mães com 20 anos ou mais (n = 3.240; mediana 3.165 g). O teste t não indicou diferença entre as médias (3.074 g contra 3.098 g; p = 0,43), enquanto o teste de Wilcoxon indicou diferença limítrofe (p = 0,045), com deslocamento estimado de 55 g (IC95%: 0 a 105 g). A divergência decorre da assimetria da distribuição: o grupo de adolescentes apresentou maior proporção de recém-nascidos acima de 4.000 g (4,1% contra 2,6%), o que eleva sua média. A magnitude estimada não sugere relevância clínica.
Repare no que essa redação faz: ela relata os dois testes, explica a divergência em vez de escondê-la e conclui pelo tamanho do efeito. Escolher só o teste que deu significativo e omitir o outro tem nome — é uma forma de p-hacking, e é exatamente o que o registro prévio de estudos, visto no capítulo de ciência aberta, existe para impedir.
30.6.8 Passo 8: a pergunta que os dados respondem com clareza
O resultado acima é honesto, mas é fraco e ambíguo. Ele não encerra o assunto: ele muda a pergunta.
Se a diferença no peso ao nascer é, na melhor das hipóteses, pequena, será que a assistência recebida durante a gravidez difere? O banco traz o número de consultas de pré-natal, então dá para verificar.
aggregate(consultas_pn ~ grupo, data = nascimentos,
FUN = function(x) c(n = length(x),
media = round(mean(x), 1),
mediana = median(x)))## grupo consultas_pn.n consultas_pn.media consultas_pn.mediana
## 1 20 anos ou mais 3124.0 8.1 8.0
## 2 Adolescente 343.0 7.3 7.0
Aqui a variável é uma contagem, e fortemente assimétrica, então o teste adequado é o de Wilcoxon:
##
## Wilcoxon rank sum test with continuity correction
##
## data: consultas_pn by grupo
## W = 630694, p-value = 5.33e-08
## alternative hypothesis: true location shift is not equal to 0
Agora sim: p menor que 0,001. Mães adolescentes fazem menos consultas de pré-natal, com mediana de 7 contra 8.
Como reportar:
Mães adolescentes realizaram menor número de consultas de pré-natal (mediana 7) do que mães com 20 anos ou mais (mediana 8); teste de Wilcoxon, p < 0,001.
30.6.9 O que este exemplo ensina
Repare no percurso. Partimos de uma hipótese que parecia evidente e os dados não deram uma resposta limpa: um teste disse que não havia diferença, o outro disse que havia, e no limite. Em vez de escolher o resultado mais conveniente, fomos entender por que discordavam — e a explicação (a assimetria da distribuição, com mais bebês acima de 4 kg entre as adolescentes) ensinou mais sobre os dados do que qualquer um dos dois valores de p sozinho.
Aí olhamos o tamanho da diferença, concluímos que 55 gramas não têm relevância clínica, e mudamos a pergunta. E na pergunta seguinte, sobre o acesso ao pré-natal, o dado respondeu com clareza.
São três hábitos que valem para qualquer análise:
- olhe antes de testar, porque o boxplot já sugeria que as distribuições eram parecidas;
- rode os dois testes quando a variável for assimétrica, porque média e mediana podem discordar;
- conclua pelo tamanho do efeito, e não pelo valor de p, porque foi o tamanho que resolveu o impasse.
E tudo isso foi feito com um banco público e gratuito, baixado em minutos, usando só os testes que você aprendeu ao longo do livro.
30.7 Cuidados ao usar dados do DATASUS
São dados secundários. Foram coletados para outra finalidade, a de vigilância em saúde, e não para a sua pergunta. Isso limita o que dá para concluir: você trabalha com as variáveis que existem, não com as que gostaria de ter.
É censo, não amostra. O SINASC registra todos os nascimentos, não uma amostra deles. Isso é uma vantagem, porque dispensa peso amostral, mas muda a interpretação: se você analisa todos os nascimentos de Uberaba em 2022, não está estimando nada — está descrevendo a população inteira daquele ano.
Há subnotificação e erro de preenchimento. A qualidade varia entre municípios e ao longo dos anos. Compare os seus totais com os publicados oficialmente antes de confiar neles.
Não há identificação nominal, mas ainda são dados de saúde. Trate com o mesmo cuidado que trataria uma coleta própria, e verifique junto ao seu orientador se o seu projeto precisa passar pelo Comitê de Ética — o uso de dados públicos anonimizados costuma ser dispensado, mas a decisão não é sua.
Cite a fonte. Uma forma usual é:
BRASIL. Ministério da Saúde. Sistema de Informações sobre Nascidos Vivos (SINASC). Brasília: DATASUS. Disponível em: https://datasus.saude.gov.br. Acesso em: [data].
30.8 Ideias para começar
Algumas perguntas que o SINASC responde e que dão bons trabalhos:
- O peso ao nascer difere entre partos vaginais e cesáreos, na sua cidade?
- A proporção de cesáreas mudou ao longo dos últimos dez anos?
- Mães adolescentes fazem menos consultas de pré-natal?
- A prematuridade se distribui igualmente entre os níveis de escolaridade materna?
Todas elas podem ser respondidas com os testes que você aprendeu neste livro — teste t, Wilcoxon, qui-quadrado, correlação e regressão — sobre um banco que você monta em cinco minutos.