A presente análise explora os casos de Dengue notificados em Recife durante o ano de 2024. Em um cenário de crescente preocupação com arboviroses, a simples contagem de notificações não me é suficiente para orientar a gestão pública. Meu debate gerencial foca em expor o custo da ineficiência e identificar o perfil real de vulnerabilidade.
O problema central que abordo é que a distribuição de recursos e a intervenção da vigilância epidemiológica são, frequentemente, baseadas apenas no volume de casos, e não na gravidade real e na rapidez da resposta do sistema de saúde. Este estudo visa expor onde e para quem o sistema falha, transformando dados em um plano de ação focado.
A premissa fundamental que guia esta minha análise é:
A gravidade da dengue (medida por hospitalização) em Recife em 2024 é desproporcionalmente concentrada em perfis demográficos específicos e em bairros com maior atraso na notificação, indicando a necessidade de reestruturação dos protocolos de vigilância.
Para explorar essa relação crítica entre gravidade e eficiência, utilizarei o dataset de Casos Notificados de Dengue de Recife (2024).
Minhas Variáveis Chave Incluem:
dt_sin_pri), Data da Notificação
(dt_notific), Semana Epidemiológica.dt_nasc), Sexo, Situação Gestacional.id_bairro), Código da Unidade de Saúde
(id_unidade).hospitaliz), Classificação Final
(classi_fin), Evolução (evolucao).Minha abordagem se concentra na criação de métricas sintéticas para gerar insights acionáveis, devido à ausência de dados nas colunas de sinais de alarme:
Atraso_Notificacao_Dias para mapear a resposta da rede de
vigilância.Faixa_Etaria a partir da data de nascimento.Taxa de Hospitalização por perfil (o desfecho mais
confiável).Meu objetivo é expor: Quais bairros e perfis de pacientes possuem o maior risco de ineficiência do sistema de vigilância e saúde pública em responder a um caso de dengue de forma rápida e eficaz, resultando em um desfecho mais grave
A tabela a seguir lista todas as bibliotecas R utilizadas neste projeto, bem como a finalidade de cada uma na análise da Dengue.
| Pacote | Finalidade |
|---|---|
| readr |
Importação de dados: leitura eficiente do arquivo CSV
(read_csv2).
|
| dplyr |
Manipulação de dados: filtragem, seleção, criação de novas variáveis
(select, filter, mutate).
|
| knitr | Ferramenta base para renderizar tabelas e texto no R Markdown. |
| kableExtra |
Estilização avançada de tabelas (kbl,
kable_styling).
|
| DT |
Criação de tabelas interativas com filtro e navegação
(datatable).
|
| lubridate | Manipulação de datas e intervalos: criação de métricas temporais e idade. |
| ggplot2 | Visualização gráfica: construção de todos os gráficos da EDA. |
| scales | Formatação de escalas numéricas (eixos, percentuais, números grandes). |
| ggrepel | Rótulos inteligentes nos gráficos, evitando sobreposição de textos. |
O conjunto de dados contém informações detalhadas sobre os registros de casos notificados de dengue no município de Recife. Os dados se referem ao ano de 2024 e abrangem variáveis como data de notificação, classificação final (confirmado, descartado ou inconclusivo), critério de confirmação, evolução do caso e dados de localização. O conjunto de dados é disponibilizado e mantido pela Secretaria de Saúde através do Portal de Dados Abertos da Cidade do Recife.
O arquivo original contém mais de 80 colunas. Primeiro, irei carregar todas as colunas para visualizar a estrutura completa do dataset:
# Carregar o dataset COMPLETO
df_completo <- read_csv2(
"dengon-2024.csv",
locale = locale(encoding = "latin1")
)
# Visualizar todas as colunas com scroll horizontal
DT::datatable(
head(df_completo, 20),
options = list(
pageLength = 10,
scrollX = TRUE, # Ativa scroll horizontal
scrollY = "400px", # Limita altura com scroll vertical
scrollCollapse = TRUE,
columnDefs = list(
list(targets = "_all", className = "dt-center")
)
),
caption = "Dataset Completo - Todas as colunas disponíveis"
)
Para a análise, focada em desfecho e eficiência, seleciei apenas 14 variáveis chave (mais 3 que serão criadas posteriormente):
| Variável (Novo Nome) | Variável (Nome Original) | Tipo | Finalidade na Análise |
|---|---|---|---|
st_ocorreu_hospitalizacao |
hospitaliz |
Numérico (0/1/NA) | Métrica PRINCIPAL de Gravidade (Desfecho). |
tp_evolucao_caso |
evolucao |
Numérico | Desfecho Clínico (Cura, Óbito). |
dt_notificacao |
dt_notific |
Data | Cálculo da Eficiência (Atraso). |
dt_diagnostico_sintoma |
dt_sin_pri |
Data | Cálculo da Eficiência (Atraso). |
ds_semana_notificacao |
sem_not |
Numérico | Análise de Série Temporal (pico da epidemia). |
dt_nascimento |
dt_nasc |
Data | Cálculo da Faixa_Etaria
(Perfil de Risco). |
tp_sexo |
cs_sexo |
Caractere | Perfil Demográfico. |
tp_gestante |
cs_gestant |
Numérico | Variável de Risco Adicional (subgrupo prioritário). |
nome_bairro |
nm_bairro |
Char | Normalização/Análise Espacial. |
co_unidade_notificacao |
id_unidade |
Numérico | Análise de Eficiência por Unidade de Saúde. |
notificacao_ano |
nu_ano |
Numérico | Variável de Filtro (garantir que seja 2024). |
tp_classificacao_final |
classi_fin |
Numérico | Filtro de Agravo (isolar casos de Dengue). |
febre |
febre |
Numérico (0/1/NA) | Métrica Exploratória Mínima (base do diagnóstico). |
exantema |
exantema |
Numérico (0/1/NA) | Métrica Exploratória Mínima. |
# Lista das colunas originais que serão mantidas
colunas_selecionadas <- c(
"nu_ano", "classi_fin", "hospitaliz", "evolucao",
"dt_notific", "dt_sin_pri", "sem_not", "dt_nasc",
"cs_sexo", "cs_gestant", "nm_bairro", "id_unidade",
"febre", "exantema"
)
# Mapeamento para rename(): [Novo Nome Legível] = [Original CSV Header]
col_mapping_corrigido <- c(
"notificacao_ano" = "nu_ano",
"tp_classificacao_final" = "classi_fin",
"st_ocorreu_hospitalizacao" = "hospitaliz",
"tp_evolucao_caso" = "evolucao",
"dt_notificacao" = "dt_notific",
"dt_diagnostico_sintoma" = "dt_sin_pri",
"ds_semana_notificacao" = "sem_not",
"dt_nascimento" = "dt_nasc",
"tp_sexo" = "cs_sexo",
"tp_gestante" = "cs_gestant",
"Nome_Bairro" = "nm_bairro",
"co_unidade_notificacao" = "id_unidade",
"febre" = "febre",
"exantema" = "exantema"
)
# Selecionar apenas as colunas de interesse
df_dengue <- df_completo %>%
select(all_of(colunas_selecionadas)) %>%
rename(!!!col_mapping_corrigido)
# Visualizar dataset reduzido
DT::datatable(
head(df_dengue, 20),
options = list(
pageLength = 10,
scrollX = TRUE,
scrollY = "400px",
scrollCollapse = TRUE
),
caption = "Dataset Reduzido - Apenas variáveis de interesse"
)
Esta etapa é a transição de dados brutos para dados prontos para
análise. Aqui, aplico os filtros de ano e doença, limpo os códigos
numéricos (1, 2, 9) e crio as métricas de Eficiência
(Atraso_Notificacao_Dias) e Risco
Demográfico (Faixa_Etaria).
# Códigos válidos de Dengue Confirmada (SINAN)
# Define o vetor com os códigos que representam casos confirmados de dengue no SINAN.
codigos_dengue_confirmada <- c(1, 2, 3, 4, 10, 11, 12)
# FILTRAGEM + LIMPEZA
df_analise <- df_dengue %>%
# Filtra o dataset para incluir apenas o ano de 2024 e casos confirmados de dengue.
filter(
notificacao_ano == 2024,
tp_classificacao_final %in% codigos_dengue_confirmada
) %>%
mutate(
# Converte todas as colunas que iniciam com "dt_" (data) para o formato 'Date' do R.
across(starts_with("dt_"), lubridate::as_date),
# Mapeia as respostas de múltipla escolha (1, 2, 9) para o formato binário (1 para Sim, 0 para Não/Ignorado).
across(
c(st_ocorreu_hospitalizacao, febre, exantema),
~ dplyr::case_when(
. %in% c(2, 9) ~ 0,
. == 1 ~ 1,
TRUE ~ 0
)
),
Atraso_Notificacao_Dias =
as.numeric(dt_notificacao - dt_diagnostico_sintoma)
) %>%
mutate(
idade_anos =
floor(lubridate::time_length(
lubridate::interval(dt_nascimento, dt_notificacao),
"years"
)),
# Categorização da Faixa Etária
Faixa_Etaria = cut(
idade_anos,
breaks = c(0, 12, 19, 39, 59, Inf),
labels = c(
"0-12 (Criança)",
"13-19 (Adolescente)",
"20-39 (Adulto Jovem)",
"40-59 (Adulto)",
"60+ (Idoso)"
),
right = FALSE
)
) %>%
mutate(
Unidade = paste("USF-", co_unidade_notificacao)
) %>%
select(-co_unidade_notificacao)
# Exibe as primeiras 10 linhas do dataset final, selecionando as colunas mais relevantes para a análise.
DT::datatable(
head(
df_analise %>%
select(
Faixa_Etaria,
Atraso_Notificacao_Dias,
st_ocorreu_hospitalizacao,
Nome_Bairro,
Unidade
),
10
),
options = list(pageLength = 5),
caption = "Dataset Analítico Final (Pronto para EDA)"
)
A análise exploratória testará diretamente a tese, focando no
cruzamento de Gravidade (Hospitalização),
Eficiência (Atraso) e
Localização/Demografia, usando o dataframe
df_analise pronto.
Insight: Identificar o pico da crise (Semana Epidemiológica) e verificar se o Atraso Médio na Notificação piora nesse período, indicando sobrecarga do sistema.
df_semanal <- df_analise %>%
# Agrupa os dados pela Semana Epidemiológica de notificação (ds_semana_notificacao).
group_by(ds_semana_notificacao) %>%
summarise(
# Calcula o total de casos e o atraso médio de notificação por semana.
Casos_Totais = n(),
Atraso_Medio_Dias = round(mean(Atraso_Notificacao_Dias, na.rm = TRUE), 1),
.groups = 'drop'
) %>%
# Cria uma coluna numérica para o número da Semana Epidemiológica (Semana_ID) e filtra inválidos.
mutate(Semana_ID = as.numeric(substring(ds_semana_notificacao, 5, 6))) %>%
filter(Semana_ID > 0)
# Fator de escala eixo secundário
# Calcula a proporção para ajustar o Atraso Médio no eixo secundário.
fator_escala <- max(df_semanal$Casos_Totais, na.rm = TRUE) /
max(df_semanal$Atraso_Medio_Dias, na.rm = TRUE)
grafico_temporal_combinado <- df_semanal %>%
# Inicia o gráfico combinando Casos Totais (eixo primário) e Atraso Médio (eixo secundário escalado).
ggplot(aes(x = Semana_ID)) +
geom_bar(aes(y = Casos_Totais), stat = "identity", fill = "#0072B2", alpha = 0.7) +
geom_line(aes(y = Atraso_Medio_Dias * fator_escala), color = "#D55E00", linewidth = 1) +
geom_point(aes(y = Atraso_Medio_Dias * fator_escala), color = "#D55E00", size = 3) +
# Configura os dois eixos Y (primário para Casos, secundário para Atraso Médio).
scale_y_continuous(
name = "Casos Totais Notificados",
labels = comma,
sec.axis = sec_axis(~./fator_escala,
name = "Atraso Médio (Dias)",
breaks = pretty_breaks(5))
) +
# Define os rótulos do gráfico (título, subtítulo e eixo X).
labs(
title = "Série Temporal: Casos (Barras) vs. Atraso na Notificação (Linha)",
subtitle = "O atraso está no eixo secundário (direita)",
x = "Número da Semana Epidemiológica (SE)"
) +
# Aplica um tema visual minimalista.
theme_minimal()
grafico_temporal_combinado
df_risco <- df_analise %>%
mutate(tp_sexo = ifelse(tp_sexo == "I", NA, tp_sexo)) %>%
filter(!is.na(Faixa_Etaria), !is.na(tp_sexo)) %>%
# Agrupa os dados por Faixa Etária e Sexo.
group_by(Faixa_Etaria, tp_sexo) %>%
summarise(
# Calcula o total de casos, casos hospitalizados e a Taxa de Hospitalização (%) para cada grupo.
Casos_Total = n(),
Casos_Hospitalizados = sum(st_ocorreu_hospitalizacao == 1, na.rm = TRUE),
Taxa_Hospitalizacao = round((Casos_Hospitalizados / Casos_Total) * 100, 1),
.groups = 'drop'
) %>%
# Ordena o resultado pela Taxa de Hospitalização em ordem decrescente.
arrange(desc(Taxa_Hospitalizacao))
# Exibe a tabela de risco de hospitalização de forma interativa.
DT::datatable(df_risco, caption = "Taxa de Hospitalização por Faixa Etária e Sexo")
grafico_hospitalizacao <- df_risco %>%
# Cria um gráfico de barras comparando a Taxa de Hospitalização por Faixa Etária, agrupado por Sexo.
ggplot(aes(x = Faixa_Etaria, y = Taxa_Hospitalizacao, fill = tp_sexo)) +
geom_bar(stat = "identity", position = "dodge") +
# Adiciona rótulos de texto nas barras com a porcentagem exata.
geom_text(
aes(label = paste0(Taxa_Hospitalizacao, "%")),
position = position_dodge(width = 0.9),
hjust = -0.2, size = 3
) +
# Define os rótulos do gráfico (título e eixos).
labs(
title = "Taxa de Hospitalização por Faixa Etária e Sexo",
x = "Faixa Etária",
y = "Taxa de Hospitalização (%)",
fill = "Sexo"
) +
# Aplica um tema visual em preto e branco.
theme_bw() +
# Inverte os eixos para melhorar a legibilidade.
coord_flip()
grafico_hospitalizacao
df_bairro_resumo <- df_analise %>%
# Agrupa os dados por Nome do Bairro.
group_by(Nome_Bairro) %>%
summarise(
# Calcula o total de casos, o Atraso Médio e a Taxa de Hospitalização por bairro.
Casos_Total = n(),
Atraso_Medio_Dias = round(mean(Atraso_Notificacao_Dias, na.rm = TRUE), 1),
Casos_Hospitalizados = sum(st_ocorreu_hospitalizacao == 1, na.rm = TRUE),
Taxa_Hospitalizacao = round((Casos_Hospitalizados / Casos_Total) * 100, 1),
.groups = 'drop'
) %>%
# Filtra para incluir apenas bairros com no mínimo 50 casos, ordena por risco e seleciona o Top 10.
filter(Casos_Total >= 50) %>%
arrange(desc(Taxa_Hospitalizacao), desc(Atraso_Medio_Dias)) %>%
head(10)
# Exibe a tabela dos Top 10 Bairros Críticos de forma interativa.
DT::datatable(df_bairro_resumo, caption = "Top 10 Bairros Críticos")
grafico_risco_bairro <- df_bairro_resumo %>%
# Cria um gráfico de bolhas visualizando Atraso vs. Hospitalização, com o tamanho da bolha representando Casos Totais.
ggplot(aes(x = Atraso_Medio_Dias, y = Taxa_Hospitalizacao, size = Casos_Total, label = Nome_Bairro)) +
geom_point(alpha = 0.7, color = "#009E73") +
# Adiciona rótulos de texto aos pontos, evitando sobreposição (ggrepel).
ggrepel::geom_text_repel(size = 3, force = 5) +
# Aplica um tema visual minimalista.
theme_minimal()
grafico_risco_bairro
A análise exploratória validou a tese central: a crise de Dengue em Recife (2024) foi definida pela concentração desproporcional do risco em perfis específicos e por falhas crônicas de eficiência na vigilância.
| Pilar da Tese | Evidência Principal | Implicação Gerencial |
|---|---|---|
| Gravidade | Desproporcional: Homens jovens (Crianças 0-12 e Adolescentes 13-19) têm as maiores taxas de hospitalização (acima de 11%), o dobro da média feminina. | O risco exige um Protocolo de Triagem por Idade/Sexo para priorizar este grupo. |
| Atraso (Ineficiência) | Crônico, não por Sobrecarga: Atraso de notificação em bairros como Imbiribeira chega a 16,6 dias. A correlação fraca (0,08) entre casos e atraso prova que a lentidão é um problema administrativo, não de demanda de pico. | A solução é a Auditoria de Processos, focada nos bairros com atraso crônico. |
| Focos Geográficos | Altamente Localizado: O ranking aponta para Afogados, Imbiribeira e Cordeiro como as áreas de maior risco combinado (alta hospitalização + alto atraso). | A alocação de recursos de combate ao mosquito e vigilância deve ser concentrada nestes bairros. |
Os insights exigem um salto para uma gestão segmentada e preditiva.
Atraso_Notificacao_Dias como um
KPI diário para gestores de vigilância.A principal limitação da análise foi a alta taxa de missing data nas colunas de Sinais de Alarme, forçando a dependência exclusiva da Hospitalização como métrica de gravidade. Futuras análises devem focar na Taxa de Incidência (casos por população) para maior precisão geográfica.