Atraso na Notificação e Risco Hospitalar: Análise de Gravidade da Dengue em Recife (2024)

Introdução

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 e a Relevância

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 Tese Central

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.


O Foco

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:

  • Temporal/Eficiência: Data do Primeiro Sintoma (dt_sin_pri), Data da Notificação (dt_notific), Semana Epidemiológica.
  • Demográfica: Data de Nascimento (dt_nasc), Sexo, Situação Gestacional.
  • Geográfica/Institucional: Código do Bairro (id_bairro), Código da Unidade de Saúde (id_unidade).
  • Desfecho/Gravidade: Hospitalização (hospitaliz), Classificação Final (classi_fin), Evolução (evolucao).

Abordagem Analítica

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:

  1. Métrica de Eficiência: Calculo o Atraso_Notificacao_Dias para mapear a resposta da rede de vigilância.
  2. Métrica Demográfica: Categorizo a Faixa_Etaria a partir da data de nascimento.
  3. Gravidade: Dou foco exclusivo na 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

Pacotes Necessários

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.

Preparação dos Dados

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.

Importação e Visão Completa do Dataset

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"
)

Seleção de Variáveis de Interesse

Para a análise, focada em desfecho e eficiência, seleciei apenas 14 variáveis chave (mais 3 que serão criadas posteriormente):

Dicionário de Variáveis Selecionadas

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.

Visão do dataset com as variáveis selecionadas

# 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"
)

Filtragem, Limpeza e Criação de Métricas

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)"
)

Análise Exploratória dos Dados

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.

Análise Temporal e Eficiência (Pico da Epidemia)

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

Taxa de Hospitalização por Faixa Etária e Sexo

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

Ranking dos Bairros por Risco (Hospitalização e Atraso)

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

Conclusão

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.

Validação da Tese e Insights Chave

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.

Plano de Ação Estratégico (Implicações)

Os insights exigem um salto para uma gestão segmentada e preditiva.

Proteção Focada

  • Ação: Criar um Alerta de Risco Jovem Masculino no sistema de triagem.
  • Detalhe: Pacientes do sexo masculino de 0 a 19 anos devem ter prioridade máxima de acompanhamento.

KPI de Eficiência

  • Ação: Adotar o Atraso_Notificacao_Dias como um KPI diário para gestores de vigilância.
  • Detalhe: Meta de 3 dias ou menos; iniciar auditoria nas unidades com atraso superior a 15 dias.

Correção Processual

  • Ação: Direcionar a fiscalização e recursos de combate ao mosquito especificamente aos Top 10 Bairros Críticos ranqueados.

Limitações

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.

Referências