Introdução

A vacinação contra a COVID-19 representou uma das maiores campanhas de saúde pública da história recente. Em Recife, capital pernambucana com cerca de 1,5 milhão de habitantes, a campanha mobilizou dezenas de unidades de saúde e resultou na aplicação de milhões de doses, englobando esquemas primários e de reforço. Compreender o perfil dessa mobilização (identificando quem foi vacinado, quando, com qual imunobiológico e em qual região da cidade) é fundamental para extrair lições que possam orientar futuras ações de imunização em larga escala.

Os dados analisados neste relatório foram obtidos no Portal de Dados Abertos da Prefeitura do Recife e abrangem os registros de vacinação contra a COVID-19 consolidados pela plataforma Conecta Recife ao longo do ano de 2022. O estudo segue uma metodologia estruturada em quatro etapas consecutivas: a importação e o diagnóstico estrutural da base bruta; o processo de limpeza e transformação de dados, que incluiu a separação de campos compostos e a padronização de variáveis de interesse; a análise exploratória conduzida sob múltiplas dimensões (temporal, demográfica e geográfica) e, por fim, o enriquecimento analítico por meio do cruzamento com denominadores populacionais locais para o cálculo de taxas de cobertura vacinal.

No aspecto técnico, toda a pesquisa e manipulação de dados foram desenvolvidas na linguagem R. O pipeline utiliza o ecossistema tidyverse para as operações de transformação e engenharia de dados, o pacote lubridate para o processamento de variáveis de tempo, e bibliotecas como plotly, leaflet, DT e kableExtra para a produção de tabelas e visualizações interativas. Variáveis derivadas foram criadas ao longo do pipeline, tais como distrito, unidade, distrito_cod, mes_vacinacao, dia_semana e cobertura, com o propósito de ampliar a capacidade analítica da base. A operação de left_join foi empregada como recurso central para acoplar as contagens de doses administradas no município às tabelas de referência demográfica do Censo 2022 do IBGE, permitindo uma contextualização real e padronizada das taxas de imunização no território recifense.

Os resultados obtidos a partir dessa abordagem oferecem um diagnóstico espacial, temporal e populacional robusto sobre o desenvolvimento da campanha no território recifense. Essas análises e indicadores servem de subsídio estratégico para prefeitos, secretários de saúde, gestores públicos e epidemiologistas, auxiliando na tomada de decisões baseadas em evidências para o planejamento e a otimização de futuras ações de imunização em larga escala, bem como na identificação de gargalos de adesão e de pontos de vulnerabilidade que demandem o acompanhamento contínuo da gestão municipal.

Pacotes Utilizados

Para o desenvolvimento deste relatório, foram utilizados os seguintes pacotes da linguagem R:

Pacote Utilização
tidyverse Manipulação, limpeza e visualização de dados (dplyr, ggplot2, etc.).
lubridate Tratamento e análise de variáveis temporais (datas).
plotly Conversão de gráficos estáticos em visualizações interativas.
scales Formatação de escalas, eixos e legendas dos gráficos.
DT Geração de tabelas HTML interativas com paginação e busca.
knitr Renderização do relatório R Markdown e tabelas estáticas.
kableExtra Estilização avançada e personalização estética de tabelas.
sf Manipulação de dados geográficos (Simple Features).
leaflet Criação de mapas interativos para visualização geoespacial.

Preparação dos Dados

Importação e Fonte

vacina_raw <- read_csv2("dados_vacinacao_recife.csv", locale = locale(encoding = "UTF-8"))

Os dados utilizados neste projeto foram obtidos no Portal de Dados Abertos da Prefeitura do Recife, sob o conjunto denominado Perfil das Pessoas Vacinadas - Covid-19. Trata-se de registros administrativos gerados pela plataforma Conecta Recife, que centralizou o agendamento e o acompanhamento digital das doses aplicadas no município.

O dataset original foi coletado ao longo de toda a campanha de vacinação contra a COVID-19 em Recife e contém registros individuais de cada dose aplicada. O arquivo CSV disponibilizado possui 1.466.348 registros e 13 variáveis.

glimpse(vacina_raw)
## Rows: 1,466,348
## Columns: 13
## $ faixa_etaria      <chr> "35 a 39 anos", "35 a 39 anos", "20 a 24 anos", "35 …
## $ idade             <dbl> 36, 37, 20, 38, 26, 31, 35, 47, 36, 45, 37, 44, 43, …
## $ sexo              <chr> "FEMININO", "MASCULINO", "MASCULINO", "MASCULINO", "…
## $ raca_cor          <chr> "PARDA", "BRANCA", "BRANCA", "PARDA", "BRANCA", "PAR…
## $ municipio         <chr> "RECIFE", "RECIFE", "RECIFE", "RECIFE", "RECIFE", "R…
## $ grupo             <chr> "TRABALHADORES DA SAÚDE", "PESSOAS COM COMORBIDADES"…
## $ categoria         <chr> NA, "PESSOAS COM OBESIDADE MÓRBIDA (IMC > OU = 40)",…
## $ lote              <chr> "FM2951", "FK8911", "FL4222", "219VCD273Z", "FM2967"…
## $ vacina_fabricante <chr> "3 - COMIRNATY (PFIZER)", "3 - COMIRNATY (PFIZER)", …
## $ descricao_dose    <dbl> 3, 3, 3, 3, 3, 3, 3, 1, 3, 2, 3, 3, 3, 3, 3, 2, 3, 3…
## $ cnes              <chr> "DS 1: CNES: 000507 - POLICLÍNICA GOUVEIA DE BARROS"…
## $ sistema_origem    <chr> "Conecta Recife", "Conecta Recife", "Conecta Recife"…
## $ data_vacinacao    <dttm> 2022-01-24, 2022-01-11, 2022-01-08, 2022-02-18, 202…

Perfil do Dataset Original

Antes de iniciar as transformações, é essencial compreender a estrutura dos dados brutos e suas peculiaridades. O arquivo CSV apresenta as seguintes características:

  • Demográficas (faixa_etaria, idade, sexo, raca_cor): Representam o perfil do vacinado. Note que a coluna raca_cor possui inconsistências de digitação (como "ÍNDÍGENA" ou "ÍNDIGENA") e a string "NÃO INFORMADO", que no arquivo original é apenas um texto comum, mas semanticamente atua como um dado ausente (NA).
  • Campanha (municipio, grupo, categoria, lote, vacina_fabricante, descricao_dose, cnes, sistema_origem): Detalham o contexto da aplicação.
    • O campo categoria tem uma altíssima prevalência de valores ausentes formais. Isso não é um erro de qualidade, mas uma característica estrutural, já que a imensa maioria do público se vacinou pelo critério de idade e não por uma categoria profissional ou de comorbidade específica.
    • O campo cnes combina o distrito sanitário e o nome da unidade de saúde em uma única string extensa e redundante.
    • O campo vacina_fabricante traz prefixos numéricos codificados junto ao nome do laboratório.
  • Temporais (data_vacinacao): Registra o momento da aplicação da dose. Originalmente, a variável é importada como texto (caractere) com o horário zerado (ex: "2022-01-24 00:00:00.0"), o que exigirá conversão para o formato apropriado de data.

A tabela a seguir apresenta o resumo estrutural do dataset exatamente como foi importado, evidenciando o status inicial dos valores ausentes (NAs explícitos) e a tipagem original (predominantemente character).

resumo_raw <- tibble(
  Variável           = names(vacina_raw),
  Tipo               = sapply(vacina_raw, \(x) class(x)[1]),
  `Valores Únicos`   = sapply(vacina_raw, n_distinct),
  `NAs (%)`          = sapply(vacina_raw, \(x) scales::percent(mean(is.na(x)), accuracy = 0.01)),
  `Exemplo`          = sapply(vacina_raw, \(x) {
    val <- na.omit(x)[1]
    if (is.null(val) || length(val) == 0) return("-")
    as.character(val)
  })
)

resumo_raw |>
  kable(align = "llccl") |>
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    font_size = 13
  ) |>
  column_spec(1, bold = TRUE) |>
  column_spec(4, color = ifelse(
    grepl("^0\\.00%$", resumo_raw$`NAs (%)`), "#27ae60", "#e74c3c"
  ))
Variável Tipo Valores Únicos NAs (%) Exemplo
faixa_etaria character 22 0.00% 35 a 39 anos
idade numeric 121 0.00% 36
sexo character 4 0.00% FEMININO
raca_cor character 8 0.02% PARDA
municipio character 611 0.00% RECIFE
grupo character 29 0.00% TRABALHADORES DA SAÚDE
categoria character 170 88.90% PESSOAS COM OBESIDADE MÓRBIDA (IMC > OU = 40)
lote character 662 0.07% FM2951
vacina_fabricante character 7 0.00% 3 - COMIRNATY (PFIZER)
descricao_dose numeric 6 0.00% 3
cnes character 8 0.00% DS 1: CNES: 000507 - POLICLÍNICA GOUVEIA DE BARROS
sistema_origem character 2 0.00% Conecta Recife
data_vacinacao POSIXct 361 0.00% 2022-01-24

Limpeza e Transformações

O primeiro passo é restringir o conjunto ao município de Recife. Embora o dataset seja da prefeitura, há 41978 registros de outros municípios, representando pessoas que se vacinaram em Recife mas têm cadastro em outra cidade. Como o foco da análise é o perfil da campanha em Recife, esses registros são removidos.

vacina <- vacina_raw |> 
  filter(municipio == "RECIFE")

Padronização do nome do fabricante

A coluna vacina_fabricante contém um prefixo numérico no formato "N - " (ex.: "85 - PFIZER"). Esse prefixo é removido para que o campo reflita apenas o nome do laboratório, facilitando agrupamentos e visualizações.

vacina <- vacina |>
  mutate(vacina_fabricante = str_remove(vacina_fabricante, "^\\d+ - ") |> str_trim())

Padronização da variável raça/cor

A variável raca_cor apresenta duas inconsistências: a grafia alternativa "ÍNDÍGENA" (com acentos incorretos) e o valor "NÃO INFORMADO", que é convertido para NA por não representar uma categoria analítica válida.

vacina <- vacina |>
  mutate(raca_cor = case_when(
    raca_cor %in% c("ÍNDÍGENA", "ÍNDIGENA") ~ "INDÍGENA",
    raca_cor == "NÃO INFORMADO" ~ NA_character_,
    TRUE ~ raca_cor
  ))

Separação do distrito sanitário e da unidade de saúde

A coluna cnes agrega duas informações em um único campo textual: o distrito sanitário (no padrão "DS N") e o nome da unidade de saúde. Ambas são extraídas via expressão regular para colunas separadas (distrito e unidade), e a coluna original é descartada.

vacina <- vacina |>
  mutate(
    distrito = str_extract(cnes, "DS \\d+"),
    unidade  = str_extract(cnes, "(?<=- ).+$") |> str_trim()
  ) |>
  select(-cnes)

Conversão e derivação de datas

A coluna data_vacinacao é convertida para o tipo Date. Em seguida, é derivada a coluna mes_vacinacao, truncando a data ao primeiro dia do mês correspondente, o que permite agregar registros por período mensal nas análises temporais.

vacina <- vacina |>
  mutate(
    data_vacinacao = as_date(data_vacinacao),
    mes_vacinacao  = floor_date(data_vacinacao, "month")
  )

Rotulagem da dose

A coluna descricao_dose é filtrada para remover a dose 6, que possui apenas 1 registro no ano de 2022, e em seguida é recodificada de valores inteiros (1 a 5) para um fator ordenado com rótulos descritivos ("1ª Dose", "2ª Dose", "1º Reforço", "2º Reforço", "3º Reforço"). O uso de factor garante ordenação correta nos gráficos e tabelas.

vacina <- vacina |>
  filter(descricao_dose != "6") |>
  mutate(descricao_dose = factor(descricao_dose,
    levels = 1:5,
    labels = c("1ª Dose", "2ª Dose", "1º Reforço",
               "2º Reforço", "3º Reforço")
  ))

Criação do código numérico do distrito

Para possibilitar a junção dos dados de vacinação com o arquivo geoespacial dos distritos sanitários de Recife (GeoJSON), é extraído o número do distrito da coluna textual distrito e armazenado como inteiro na nova coluna distrito_cod.

# Extrai o número do distrito para facilitar o join com o GeoJSON
vacina <- vacina |>
  mutate(distrito_cod = as.integer(str_extract(distrito, "\\d+")))

Enriquecimento IBGE

Para calcular taxas de cobertura vacinal, os dados de vacinação foram cruzados com estimativas populacionais do Censo Demográfico 2022, obtidas diretamente do portal SIDRA do IBGE. Foram utilizadas duas tabelas: a Tabela 9514 (população por faixa etária e sexo) e a Tabela 9606 (população por faixa etária e raça/cor), ambas filtradas para o município de Recife (código 2611606).

Os dados populacionais são estruturados em quatro tibbles de referência, cada um representando um nível de desagregação: população total por faixa etária, desagregada por sexo, por raça/cor, e pela combinação de faixa etária com raça/cor. Essa estrutura permite calcular taxas de cobertura em diferentes dimensões.

População total por faixa etária

Referência de denominador para o cálculo de cobertura geral. Cobre todas as 21 faixas etárias quinquenais do Censo 2022, da faixa "0 a 4 anos" até "100 anos +".

pop_faixa <- tibble(
  faixa_etaria = c(
    "0 a 4 anos", "5 a 9 anos", "10 a 14 anos", "15 a 19 anos",
    "20 a 24 anos", "25 a 29 anos", "30 a 34 anos", "35 a 39 anos",
    "40 a 44 anos", "45 a 49 anos", "50 a 54 anos", "55 a 59 anos",
    "60 a 64 anos", "65 a 69 anos", "70 a 74 anos", "75 a 79 anos",
    "80 a 84 anos", "85 a 89 anos", "90 a 94 anos", "95 a 99 anos",
    "100 anos +"
  ),
  populacao = c(
    80947, 93275, 89504, 95331, 108788, 110525, 111506, 116312,
    118769, 105430, 100962, 93575, 81917, 63441, 47650, 31091,
    20825, 11611, 5401, 1722, 338
  )
)

População por faixa etária e sexo

Desagregação necessária para comparar a cobertura vacinal entre homens e mulheres dentro de cada grupo de idade, controlando as diferenças na estrutura demográfica entre os sexos. Os valores masculinos e femininos são empilhados em formato longo com rep().

pop_faixa_sexo <- tibble(
  faixa_etaria = rep(c(
    "0 a 4 anos", "5 a 9 anos", "10 a 14 anos", "15 a 19 anos",
    "20 a 24 anos", "25 a 29 anos", "30 a 34 anos", "35 a 39 anos",
    "40 a 44 anos", "45 a 49 anos", "50 a 54 anos", "55 a 59 anos",
    "60 a 64 anos", "65 a 69 anos", "70 a 74 anos", "75 a 79 anos",
    "80 a 84 anos", "85 a 89 anos", "90 a 94 anos", "95 a 99 anos",
    "100 anos +"
  ), 2),
  sexo = c(rep("MASCULINO", 21), rep("FEMININO", 21)),
  populacao = c(
    # Homens
    41054, 47652, 45880, 48112, 54323, 53561, 52493, 53675,
    54520, 47180, 44633, 40334, 34420, 25302, 17912, 10972,
    6558, 3268, 1267, 371, 49,
    # Mulheres
    39893, 45623, 43624, 47219, 54465, 56964, 59013, 62637,
    64249, 58250, 56329, 53241, 47497, 38139, 29738, 20119,
    14267, 8343, 4134, 1351, 289
  )
)

População por raça/cor

Denominador para o cálculo de cobertura por grupo racial, considerando as cinco categorias do IBGE: Branca, Preta, Amarela, Parda e Indígena. Esses totais correspondem à população recifense declarada no Censo 2022, independentemente da faixa etária.

pop_raca <- tibble(
  raca_cor  = c("BRANCA", "PRETA", "AMARELA", "PARDA", "INDÍGENA"),
  populacao = c(578413, 182546, 2703, 722555, 2656)
)

População por faixa etária e raça/cor

Desagregação cruzada que permite investigar disparidades raciais de cobertura dentro de cada grupo etário, que é a granularidade mais fina disponível nos dados censitários utilizados. As cinco categorias raciais são empilhadas sobre as 21 faixas etárias, totalizando 105 combinações.

pop_faixa_raca <- tibble(
  faixa_etaria = rep(c(
    "0 a 4 anos", "5 a 9 anos", "10 a 14 anos", "15 a 19 anos",
    "20 a 24 anos", "25 a 29 anos", "30 a 34 anos", "35 a 39 anos",
    "40 a 44 anos", "45 a 49 anos", "50 a 54 anos", "55 a 59 anos",
    "60 a 64 anos", "65 a 69 anos", "70 a 74 anos", "75 a 79 anos",
    "80 a 84 anos", "85 a 89 anos", "90 a 94 anos", "95 a 99 anos",
    "100 anos +"
  ), 5),
  raca_cor = c(
    rep("BRANCA", 21), rep("PRETA", 21), rep("AMARELA", 21),
    rep("PARDA", 21),  rep("INDÍGENA", 21)
  ),
  populacao = c(
    # BRANCA
    35612, 36588, 33389, 34570, 39109, 39916, 41560, 42659,
    44815, 38765, 36892, 36017, 32759, 26888, 21848, 15369,
    10861, 6445, 3148, 1026, 177,
    # PRETA
    6109, 8212, 8683, 11698, 14877, 15658, 15207, 15697,
    15830, 14304, 14146, 12074, 10411, 7564, 5274, 3113,
    1992, 1024, 475, 157, 41,
    # AMARELA
    102, 138, 120, 172, 215, 238, 231, 261,
    237, 177, 180, 172, 139, 110, 81, 59,
    38, 21, 8, 4, 0,
    # PARDA
    39029, 48221, 47183, 48742, 54408, 54522, 54305, 57473,
    57650, 51998, 49535, 45102, 38424, 28732, 20329, 12490,
    7910, 4089, 1763, 530, 120,
    # INDÍGENA
    95, 114, 127, 147, 177, 185, 200, 220,
    234, 181, 206, 208, 179, 139, 118, 59,
    24, 31, 7, 5, 0
  )
)

Cruzamento com os dados de vacinação

Os tibbles populacionais são unidos aos dados de vacinação via left_join, gerando quatro bases analíticas com a taxa de cobertura calculada como razão entre doses registradas e população de referência em cada estrato. Valores acima de 1,0 são esperados em faixas onde há doses de reforço acumuladas ou migração de vacinados de outros municípios.

# Doses por faixa etária com cobertura
doses_faixa <- vacina |>
  count(faixa_etaria, name = "doses") |>
  left_join(pop_faixa, by = "faixa_etaria") |>
  mutate(cobertura = doses / populacao)

# Doses por faixa etária e sexo com cobertura
doses_faixa_sexo <- vacina |>
  filter(sexo %in% c("MASCULINO", "FEMININO")) |>
  count(faixa_etaria, sexo, name = "doses") |>
  left_join(pop_faixa_sexo, by = c("faixa_etaria", "sexo")) |>
  mutate(cobertura = doses / populacao)

# Doses por raça/cor com cobertura
doses_raca <- vacina |>
  filter(!is.na(raca_cor)) |>
  count(raca_cor, name = "doses") |>
  left_join(pop_raca, by = "raca_cor") |>
  mutate(cobertura = doses / populacao)

# Doses por faixa etária e raça/cor com cobertura
doses_faixa_raca <- vacina |>
  filter(!is.na(raca_cor), !is.na(faixa_etaria)) |>
  count(faixa_etaria, raca_cor, name = "doses") |>
  left_join(pop_faixa_raca, by = c("faixa_etaria", "raca_cor")) |>
  mutate(cobertura = doses / populacao)

Conjunto de Dados Final

Após todas as etapas de limpeza, padronização das variáveis textuais (como raca_cor e vacina_fabricante), derivação de novos campos (distrito_cod, mes_vacinacao) e enriquecimento com os dados populacionais do Censo 2022 (IBGE), o conjunto analítico final passa a contar com 1.424.360 registros e 16 variáveis.

O período efetivamente coberto pelos dados de análise se estende de janeiro de 2022 a dezembro de 2022.

Para apresentar o conjunto de dados resultante de forma condensada e interativa, a tabela abaixo exibe uma amostra aleatória de 200 registros do dataset completamente tratado.

set.seed(2024)
vacina |> slice_sample(n = 200) |> 
  DT::datatable(
    options = list(
      scrollX = TRUE,
      scrollY = "550px",
      pageLength = 25
    )
  )

Análise Exploratória

A análise exploratória de dados nos permite compreender a dinâmica da campanha de vacinação em Recife em 2022. As subseções a seguir detalham os aspectos temporais, demográficos, logísticos e geográficos das doses aplicadas.

Evolução Mensal Geral

O gráfico de área abaixo ilustra a evolução mensal das doses aplicadas em Recife ao longo de 2022.

evo_mes <- vacina |>
  count(mes_vacinacao, name = "doses") |>
  filter(!is.na(mes_vacinacao))

p_temporal <- ggplot(evo_mes, aes(x = mes_vacinacao, y = doses)) +
  geom_area(fill = "#023e8a", alpha = 0.55) +
  geom_line(color = "#0077b6", linewidth = 1.2) +
  geom_point(aes(text = paste("Mês:", format(mes_vacinacao, "%B/%y"), 
                              "<br>Doses:", format(doses, big.mark = ".", decimal.mark = ","))),
             color = "#03045e", size = 2.5) +
  scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
  scale_x_date(date_labels = "%b/%y", date_breaks = "1 month") +
  labs(
    title    = "Evolução Mensal de Doses Aplicadas em Recife (2022)",
    x        = NULL,
    y        = "Doses Aplicadas"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 12, color = "#1d3557", margin = margin(b = 10)),
    axis.text.x = element_text(angle = 45, hjust = 1),
    panel.grid.minor = element_blank()
  )

ggplotly(p_temporal, tooltip = "text")

O volume de vacinação em Recife atingiu seus patamares mais elevados no primeiro quadrimestre de 2022 (janeiro a abril), com picos em janeiro (mais de 215 mil doses) e março (mais de 222 mil doses), impulsionado pela busca pelo primeiro reforço (3ª dose) e pelo início da campanha infantil em fevereiro. A partir de maio, o ritmo desacelera de forma geral, exibindo um segundo pico intermediário em junho e julho (cerca de 148 mil doses mensais) decorrente da liberação do segundo reforço (4ª dose) para o público adulto geral, seguido de uma queda progressiva no segundo semestre do ano.

Sazonalidade Semanal

O gráfico de colunas abaixo mostra a distribuição acumulada das doses por dia da semana em 2022.

doses_dia <- vacina |>
  filter(!is.na(data_vacinacao)) |>
  mutate(
    dia_num = wday(data_vacinacao, week_start = 1),
    dia_semana = case_when(
      dia_num == 1 ~ "Segunda-Feira",
      dia_num == 2 ~ "Terça-Feira",
      dia_num == 3 ~ "Quarta-Feira",
      dia_num == 4 ~ "Quinta-Feira",
      dia_num == 5 ~ "Sexta-Feira",
      dia_num == 6 ~ "Sábado",
      dia_num == 7 ~ "Domingo"
    ) |> factor(levels = c("Segunda-Feira", "Terça-Feira", "Quarta-Feira", 
                            "Quinta-Feira", "Sexta-Feira", "Sábado", "Domingo"))
  ) |>
  count(dia_semana, name = "doses")

p_semanal <- ggplot(doses_dia, aes(x = dia_semana, y = doses, fill = dia_semana)) +
  geom_col(alpha = 0.85, show.legend = FALSE) +
  scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
  scale_fill_brewer(palette = "Blues") +
  labs(
    title = "Volume de Vacinação por Dia da Semana em Recife (2022)",
    x = NULL,
    y = "Doses Aplicadas"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 12, color = "#1d3557", margin = margin(b = 10)),
    axis.text.x = element_text(angle = 45, hjust = 1),
    panel.grid.minor = element_blank()
  )

ggplotly(p_semanal)

A distribuição diária revela um ritmo forte e distribuído ao longo de quase toda a semana. Os picos de atividade ocorrem de terça a quinta-feira (liderados pela terça-feira, com mais de 231 mil doses). Destaca-se o volume expressivo registrado aos sábados (mais de 206 mil doses), superando inclusive as segundas-feiras (180 mil doses). Isso indica a relevância do funcionamento e do atendimento ativo aos sábados, viabilizando o acesso para a população que possivelmente encontrava barreiras nos dias úteis, restando apenas o domingo com um volume menor, embora ainda expressivo (140 mil doses).

Evolução por Etapa Vacinal

O gráfico abaixo detalha como o foco das etapas vacinais (esquema primário vs. reforços) se transformou mensalmente.

doses_mes_tipo <- vacina |>
  filter(!is.na(mes_vacinacao), !is.na(descricao_dose)) |>
  count(mes_vacinacao, descricao_dose, name = "doses")

p_mes_tipo <- ggplot(doses_mes_tipo, aes(x = mes_vacinacao, y = doses, fill = descricao_dose,
                                         text = paste("Mês:", format(mes_vacinacao, "%B/%y"),
                                                      "<br>Etapa:", descricao_dose,
                                                      "<br>Doses:", format(doses, big.mark = ".", decimal.mark = ",")))) +
  geom_col(position = "stack", alpha = 0.85, width = 20) +
  scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
  scale_x_date(date_labels = "%b/%y", date_breaks = "1 month") +
  scale_fill_brewer(palette = "Blues", direction = -1) +
  labs(
    title = "Distribuição Mensal de Doses por Etapa Vacinal (2022)",
    x = NULL,
    y = "Doses Aplicadas",
    fill = "Etapa"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    axis.text.x = element_text(angle = 45, hjust = 1),
    panel.grid.minor = element_blank(),
    legend.position = "bottom"
  )

ggplotly(p_mes_tipo, tooltip = "text")

A campanha transitou entre diferentes prioridades: * Janeiro-Março: Concentração no primeiro reforço (3ª dose) de adultos e no início da imunização infantil (fevereiro/março). * Abril-Julho: Liberação e pico do segundo reforço (4ª dose) para idosos, adultos e trabalhadores de saúde. * Novembro-Dezembro: Introdução progressiva de doses de terceiro reforço (5ª dose) para públicos de maior vulnerabilidade clínica.

Perfil por Idade e Gênero

O gráfico abaixo ilustra o volume total de doses aplicadas por grupo etário decenal e sexo em 2022.

doses_faixa_sexo_grouped <- doses_faixa_sexo |>
  mutate(
    grupo_etario = case_when(
      faixa_etaria %in% c("0 a 4 anos", "5 a 9 anos") ~ "0-9 anos",
      faixa_etaria %in% c("10 a 14 anos", "15 a 19 anos") ~ "10-19 anos",
      faixa_etaria %in% c("20 a 24 anos", "25 a 29 anos") ~ "20-29 anos",
      faixa_etaria %in% c("30 a 34 anos", "35 a 39 anos") ~ "30-39 anos",
      faixa_etaria %in% c("40 a 44 anos", "45 a 49 anos") ~ "40-49 anos",
      faixa_etaria %in% c("50 a 54 anos", "55 a 59 anos") ~ "50-59 anos",
      faixa_etaria %in% c("60 a 64 anos", "65 a 69 anos") ~ "60-69 anos",
      faixa_etaria %in% c("70 a 74 anos", "75 a 79 anos") ~ "70-79 anos",
      TRUE ~ "80+ anos"
    ) |> factor(levels = c("0-9 anos", "10-19 anos", "20-29 anos", "30-39 anos",
                            "40-49 anos", "50-59 anos", "60-69 anos", "70-79 anos",
                            "80+ anos"))
  ) |>
  group_by(grupo_etario, sexo) |>
  summarise(doses = sum(doses), .groups = "drop")

p_demog_sexo <- doses_faixa_sexo_grouped |>
  filter(!is.na(grupo_etario), !is.na(sexo)) |>
  ggplot(aes(x = grupo_etario, y = doses, fill = sexo,
             text = paste("Faixa:", grupo_etario,
                          "<br>Sexo:", sexo,
                          "<br>Doses:", format(doses, big.mark = ".", decimal.mark = ",")))) +
  geom_col(position = "dodge", alpha = 0.85) +
  coord_flip() +
  scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
  scale_fill_manual(values = c("FEMININO" = "#e63946", "MASCULINO" = "#457b9d")) +
  labs(
    title = "Volume de Doses por Grupo Etário e Sexo",
    x = NULL,
    y = "Doses Aplicadas",
    fill = "Sexo"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

ggplotly(p_demog_sexo, tooltip = "text")

A vacinação concentrou-se na população ativa (30 a 59 anos) e nos idosos (60+ anos) devido ao calendário de reforços. Na infância, a faixa de 0 a 9 anos registrou forte volume em fevereiro, enquanto a faixa de 0 a 4 anos apresentou menor participação devido à liberação tardia e hesitação vacinal. Em todos os grupos adultos, o volume feminino superou o masculino, padrão histórico de maior engajamento das mulheres em saúde preventiva.

Perfil por Raça/Cor

Os gráficos abaixo exibem a distribuição de doses por autodeclaração de raça/cor absoluta e cruzada com sexo.

p_demog_raca <- doses_raca |>
  filter(!is.na(raca_cor)) |>
  ggplot(aes(x = reorder(raca_cor, doses), y = doses, fill = raca_cor,
             text = paste("Raça/Cor:", raca_cor,
                          "<br>Doses:", format(doses, big.mark = ".", decimal.mark = ",")))) +
  geom_col(alpha = 0.85, show.legend = FALSE) +
  coord_flip() +
  scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
  scale_fill_brewer(palette = "Set2") +
  labs(
    title = "Volume de Doses por Raça/Cor",
    x = NULL,
    y = "Doses Aplicadas"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    panel.grid.minor = element_blank()
  )

ggplotly(p_demog_raca, tooltip = "text")
doses_raca_sexo <- vacina |>
  filter(!is.na(raca_cor), sexo %in% c("MASCULINO", "FEMININO")) |>
  count(raca_cor, sexo, name = "doses")

p_demog_raca_sexo <- doses_raca_sexo |>
  ggplot(aes(x = reorder(raca_cor, doses, sum), y = doses, fill = sexo,
             text = paste("Raça/Cor:", raca_cor,
                          "<br>Sexo:", sexo,
                          "<br>Doses:", format(doses, big.mark = ".", decimal.mark = ",")))) +
  geom_col(position = "dodge", alpha = 0.85) +
  coord_flip() +
  scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
  scale_fill_manual(values = c("FEMININO" = "#e63946", "MASCULINO" = "#457b9d")) +
  labs(
    title = "Volume de Doses por Raça/Cor e Sexo",
    x = NULL,
    y = "Doses Aplicadas",
    fill = "Sexo"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

ggplotly(p_demog_raca_sexo, tooltip = "text")

A população autodeclarada parda concentrou a maioria absoluta de vacinas aplicadas em Recife, seguida pelas populações branca e preta, refletindo a demografia geral da cidade. O maior engajamento feminino em ações de imunização se confirma de forma constante e proporcional em todas as categorias étnico-raciais analisadas.

Grupos Prioritários

O gráfico abaixo mostra os 10 principais grupos prioritários cadastrados.

doses_grupo <- vacina |>
  filter(!is.na(grupo)) |>
  count(grupo, name = "doses") |>
  slice_max(doses, n = 10, with_ties = FALSE)

p_demog_grupo <- doses_grupo |>
  ggplot(aes(x = reorder(grupo, doses), y = doses, fill = grupo,
             text = paste("Grupo:", grupo,
                          "<br>Doses:", format(doses, big.mark = ".", decimal.mark = ",")))) +
  geom_col(alpha = 0.85, show.legend = FALSE) +
  coord_flip() +
  scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
  scale_fill_viridis_d(option = "viridis") +
  labs(
    title = "Top 10 Grupos Prioritários por Doses Aplicadas",
    x = NULL,
    y = "Doses Aplicadas"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    panel.grid.minor = element_blank(),
    legend.position = "none"
  )

ggplotly(p_demog_grupo, tooltip = "text")

A população geral e os idosos representam a maioria absoluta do esforço vacinal em 2022. Profissionais de saúde e de educação lideram nas categorias de prioridades ativas pelo Plano Nacional de Operacionalização (PNO).

Distribuição por Dose

O esquema vacinal foi analisado em termos de doses aplicadas em 2022 para mapear o direcionamento logístico entre o esquema inicial e os reforços.

doses_dist <- vacina |>
  filter(!is.na(descricao_dose)) |>
  count(descricao_dose, name = "quantidade") |>
  mutate(percent = quantidade / sum(quantidade))

p_dose <- ggplot(doses_dist, aes(x = descricao_dose, y = quantidade, fill = descricao_dose,
                                 text = paste("Etapa:", descricao_dose,
                                              "<br>Quantidade:", format(quantidade, big.mark = ".", decimal.mark = ","),
                                              "<br>Proporção no Ano:", percent(percent, accuracy = 0.1)))) +
  geom_col(alpha = 0.85, show.legend = FALSE) +
  scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
  scale_fill_brewer(palette = "Blues") +
  labs(
    title = "Distribuição de Doses por Etapa Vacinal em Recife (2022)",
    x = "Etapa da Vacinação",
    y = "Doses Aplicadas"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    panel.grid.minor = element_blank()
  )

ggplotly(p_dose, tooltip = "text")
doses_dist |>
  mutate(
    `Acumulado (%)` = cumsum(percent)
  ) |>
  transmute(
    `Etapa Vacinal` = descricao_dose,
    `Doses Aplicadas` = format(quantidade, big.mark = ".", decimal.mark = ","),
    `Proporção (%)` = scales::percent(percent, accuracy = 0.1),
    `Acumulado (%)` = scales::percent(`Acumulado (%)`, accuracy = 0.1)
  ) |>
  kable(align = "lrcc") |>
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    font_size = 13
  ) |>
  row_spec(which(doses_dist$percent == max(doses_dist$percent)), bold = TRUE, background = "#d4edda")
Etapa Vacinal Doses Aplicadas Proporção (%) Acumulado (%)
1ª Dose 176.994 12.4% 12.4%
2ª Dose 210.199 14.8% 27.2%
1º Reforço 636.085 44.7% 71.8%
2º Reforço 395.194 27.7% 99.6%
3º Reforço 5.888 0.4% 100.0%

A maior parte dos atendimentos foi focada no primeiro reforço (3ª dose, 44,7% do total anual, correspondendo a 636.085 doses) e no segundo reforço (4ª dose, 27,8% do total, correspondendo a 395.194 doses). Juntas, as doses de reforço superaram 72% de todo o esforço vacinal do ano, refletindo a adaptação logística frente às mutações da COVID-19. Os esquemas primários (1ª e 2ª doses) representaram 27,2%, impulsionados pela vacinação do público infantil.

Distribuição por Imunobiológico

A análise identifica as vacinas que foram utilizadas e a forma como se distribuíram pelas diferentes etapas da campanha em Recife em 2022.

fabricante_dose <- vacina |>
  filter(!is.na(vacina_fabricante), !is.na(descricao_dose)) |>
  count(vacina_fabricante, descricao_dose, name = "doses")

p_fabricante <- fabricante_dose |>
  ggplot(aes(x = reorder(vacina_fabricante, doses, sum), y = doses, fill = descricao_dose,
             text = paste("Fabricante:", vacina_fabricante,
                          "<br>Etapa:", descricao_dose,
                          "<br>Doses:", format(doses, big.mark = ".", decimal.mark = ",")))) +
  geom_col(alpha = 0.85) +
  coord_flip() +
  scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
  scale_fill_brewer(palette = "Blues", direction = -1) +
  labs(
    title = "Distribuição de Imunobiológicos por Etapa Vacinal em Recife (2022)",
    x = NULL,
    y = "Doses Aplicadas",
    fill = "Etapa"
  ) +
  theme_minimal(base_size = 10) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    legend.position = "right",
    panel.grid.minor = element_blank()
  )

ggplotly(p_fabricante, tooltip = "text")

A vacina da Pfizer (adulto) liderou com 423.907 doses aplicadas (principalmente em reforços). Somando as versões infantil (118.495) e baby (2.205), a marca consolidou-se como principal recurso de proteção. A AstraZeneca/Fiocruz (393.032 doses) e a Janssen (335.588 doses) atuaram como importantes reforços adultos. A CoronaVac (Butantan) teve papel central na vacinação de crianças a partir de 3 anos de idade, registrando 151.134 doses (pediátrica e tradicional combinadas).

Mapa de Calor dos Distritos

O mapa interativo colore os distritos proporcionalmente ao volume absoluto de doses aplicadas em 2022.

# Carregar o GeoJSON dos distritos sanitários
distritos_geo <- st_read("distritos-recife.geojson", quiet = TRUE)

# Agregar doses por distrito, incluindo o nome da unidade de saúde
doses_por_distrito <- vacina |>
  filter(!is.na(distrito_cod)) |>
  count(distrito_cod, unidade, name = "doses") |>
  mutate(percent = doses / sum(doses))

# Garantir compatibilidade de tipos para o join
distritos_geo$cdistscodi <- as.integer(distritos_geo$cdistscodi)
doses_por_distrito$distrito_cod <- as.integer(doses_por_distrito$distrito_cod)

# Usar merge() para compatibilidade total com objetos sf
distritos_geo <- merge(distritos_geo, doses_por_distrito,
                       by.x = "cdistscodi", by.y = "distrito_cod",
                       all.x = TRUE)

# Paleta de cores
pal <- colorNumeric(palette = "YlOrRd", domain = distritos_geo$doses, na.color = "#cccccc")

# Rótulos interativos com nome do distrito e unidade de saúde
labels <- sprintf(
  "<strong>DS %s</strong><br/>Unidade: %s<br/>Doses: %s<br/>Participação: %s",
  distritos_geo$cdistscodi,
  ifelse(is.na(distritos_geo$unidade), "-", distritos_geo$unidade),
  ifelse(is.na(distritos_geo$doses), "sem dados",
         format(distritos_geo$doses, big.mark = ".", decimal.mark = ",")),
  ifelse(is.na(distritos_geo$percent), "-",
         scales::percent(distritos_geo$percent, accuracy = 0.1))
) |> lapply(htmltools::HTML)

# Mapa Leaflet
leaflet(distritos_geo) |>
  addProviderTiles(providers$CartoDB.Positron) |>
  addPolygons(
    fillColor   = ~pal(doses),
    weight      = 2,
    opacity     = 1,
    color       = "white",
    dashArray   = "3",
    fillOpacity = 0.7,
    highlightOptions = highlightOptions(
      weight = 4, color = "#1d3557", dashArray = "",
      fillOpacity = 0.85, bringToFront = TRUE
    ),
    label        = labels,
    labelOptions = labelOptions(
      style = list("font-weight" = "normal", padding = "3px 8px"),
      textsize = "13px", direction = "auto"
    )
  ) |>
  addLegend(
    pal      = pal,
    values   = ~doses,
    opacity  = 0.7,
    title    = "Doses Aplicadas",
    position = "bottomright",
    labFormat = labelFormat(big.mark = ".", transform = function(x) x)
  )

O Distrito 6 (Zona Sul, englobando Boa Viagem e Imbiribeira) liderou com 308.174 doses aplicadas (21,0% do total anual). O Distrito 7 (Casa Amarela e Vasco da Gama) registrou a segunda maior participação com 206.890 doses (14,1%). O Distrito 3 registrou a menor participação (6,9%). Policlínicas e maternidades locais concentraram os principais hubs estruturados.

População vs. Doses por Distrito

A tabela e o gráfico a seguir contrastam o percentual de residentes de cada distrito com a proporção de doses aplicadas em 2022.

pop_distritos <- tibble(
  distrito_cod = 1:8,
  distrito_nome = paste("Distrito", 1:8),
  populacao = c(55318, 244686, 94619, 315225, 233153, 244453, 172937, 128529),
  bairros = c(
    "Recife, Santo Amaro, Boa Vista, Cabanga, Ilha do Leite, Paissandu, Santo Antônio, São José, Coelhos, Soledade, Ilha Joana Bezerra",
    "Alto Santa Terezinha, Água Fria, Arruda, Beberibe, Bomba do Hemetério, Campo Grande, Cajueiro, Campina do Barreto, Dois Unidos, Encruzilhada, Fundão, Hipódromo, Linha do Tiro, Ponto de Parada, Porto da Madeira, Peixinhos, Rosarinho, Torreão",
    "Aflitos, Alto do Mandu, Apipucos, Casa Amarela, Casa Forte, Derby, Dois Irmãos, Espinheiro, Graças, Jaqueira, Monteiro, Parnamirim, Poço (Poço da Panela), Santana, Sítio dos Pintos, Tamarineira",
    "Caxangá, Cidade Universitária, Cordeiro, Engenho do Meio, Ilha do Retiro, Iputinga, Madalena, Prado, Torre, Torrões, Várzea, Zumbi",
    "Afogados, Areias, Barro, Bongi, Caçote, Coqueiral, Curado, Estância, Jardim São Paulo, Jiquiá, Mangueira, Mustardinha, Sancho, San Martin, Tejipió, Totó",
    "Boa Viagem, Brasília Teimosa, Imbiribeira, Ipsep, Pina",
    "Alto José Bonifácio, Alto José do Pinho, Brejo do Guabiraba, Brejo do Beberibe, Córrego do Jenipapo, Guabiraba, Macaxeira, Mangabeira, Morro da Conceição, Nova Descoberta, Passarinho, Pau Ferro, Vasco da Gama",
    "Cohab, Ibura, Jordão"
  )
)

doses_distrito <- vacina |>
  filter(!is.na(distrito_cod)) |>
  count(distrito_cod, name = "doses")

comp_distritos <- pop_distritos |>
  left_join(doses_distrito, by = "distrito_cod") |>
  mutate(
    pct_pop = populacao / sum(populacao),
    pct_doses = doses / sum(doses),
    diferenca = pct_doses - pct_pop
  )

comp_distritos_sorted <- comp_distritos |> arrange(distrito_cod)
cores_diff <- ifelse(comp_distritos_sorted$diferenca >= 0, "#27ae60", "#d9534f")

comp_distritos_sorted |>
  transmute(
    Distrito = distrito_nome,
    `Bairros Integrantes` = bairros,
    `População (Censo 2022)` = format(populacao, big.mark = ".", decimal.mark = ","),
    `Participação na População` = scales::percent(pct_pop, accuracy = 0.01),
    `Doses Aplicadas` = format(doses, big.mark = ".", decimal.mark = ","),
    `Participação nas Doses` = scales::percent(pct_doses, accuracy = 0.01),
    `Diferença (pp)` = scales::percent(diferenca, accuracy = 0.01)
  ) |>
  kable(align = "llrccrc") |>
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = TRUE,
    font_size = 12
  ) |>
  column_spec(1, bold = TRUE) |>
  column_spec(7, color = "white", background = cores_diff, bold = TRUE)
Distrito Bairros Integrantes População (Censo 2022) Participação na População Doses Aplicadas Participação nas Doses Diferença (pp)
Distrito 1 Recife, Santo Amaro, Boa Vista, Cabanga, Ilha do Leite, Paissandu, Santo Antônio, São José, Coelhos, Soledade, Ilha Joana Bezerra 55.318 3.72% 164.613 11.56% 7.84%
Distrito 2 Alto Santa Terezinha, Água Fria, Arruda, Beberibe, Bomba do Hemetério, Campo Grande, Cajueiro, Campina do Barreto, Dois Unidos, Encruzilhada, Fundão, Hipódromo, Linha do Tiro, Ponto de Parada, Porto da Madeira, Peixinhos, Rosarinho, Torreão 244.686 16.43% 168.040 11.80% -4.64%
Distrito 3 Aflitos, Alto do Mandu, Apipucos, Casa Amarela, Casa Forte, Derby, Dois Irmãos, Espinheiro, Graças, Jaqueira, Monteiro, Parnamirim, Poço (Poço da Panela), Santana, Sítio dos Pintos, Tamarineira 94.619 6.35% 100.730 7.07% 0.72%
Distrito 4 Caxangá, Cidade Universitária, Cordeiro, Engenho do Meio, Ilha do Retiro, Iputinga, Madalena, Prado, Torre, Torrões, Várzea, Zumbi 315.225 21.17% 182.612 12.82% -8.35%
Distrito 5 Afogados, Areias, Barro, Bongi, Caçote, Coqueiral, Curado, Estância, Jardim São Paulo, Jiquiá, Mangueira, Mustardinha, Sancho, San Martin, Tejipió, Totó 233.153 15.66% 148.136 10.40% -5.26%
Distrito 6 Boa Viagem, Brasília Teimosa, Imbiribeira, Ipsep, Pina 244.453 16.42% 308.173 21.64% 5.22%
Distrito 7 Alto José Bonifácio, Alto José do Pinho, Brejo do Guabiraba, Brejo do Beberibe, Córrego do Jenipapo, Guabiraba, Macaxeira, Mangabeira, Morro da Conceição, Nova Descoberta, Passarinho, Pau Ferro, Vasco da Gama 172.937 11.61% 206.890 14.53% 2.91%
Distrito 8 Cohab, Ibura, Jordão 128.529 8.63% 145.166 10.19% 1.56%
comp_long <- comp_distritos |>
  select(distrito_nome, pct_pop, pct_doses) |>
  pivot_longer(cols = c(pct_pop, pct_doses), names_to = "metrica", values_to = "percentual") |>
  mutate(metrica = case_when(
    metrica == "pct_pop" ~ "Participação na População",
    metrica == "pct_doses" ~ "Participação nas Doses"
  ))

p_comp <- ggplot(comp_long, aes(x = reorder(distrito_nome, percentual), y = percentual, fill = metrica,
                                text = paste("Distrito:", distrito_nome,
                                             "<br>Métrica:", metrica,
                                             "<br>Proporção:", scales::percent(percentual, accuracy = 0.01)))) +
  geom_col(position = "dodge", alpha = 0.85) +
  coord_flip() +
  scale_y_continuous(labels = scales::percent) +
  scale_fill_manual(values = c("Participação na População" = "#457b9d", "Participação nas Doses" = "#e63946")) +
  labs(
    title = "Comparação: Participação na População vs. Participação nas Doses por Distrito",
    x = NULL,
    y = "Proporção",
    fill = "Métrica"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

ggplotly(p_comp, tooltip = "text")

A análise comparativa espacial aponta diferenças entre a participação populacional residente de cada distrito e a proporção de doses aplicadas em seus respectivos territórios: * Distrito 1 e Distrito 6: Apresentaram proporções de doses aplicadas superiores à sua participação na população residente (diferenças de +7,84 pp e +5,22 pp, respectivamente). * Distrito 4: Registrou a maior diferença proporcional negativa, representando 21,17% da população residente, mas respondendo por 12,82% das doses aplicadas no território (diferença de -8,35 pp). * Distritos 2 e 5: Apresentaram participações vacinais inferiores às suas participações demográficas residentes (diferenças de -4,64 pp e -5,26 pp, respectivamente).

Essas diferenças indicam descompassos entre o contingente populacional residente e o volume de doses administradas localmente em cada área, refletindo assimetrias na distribuição espacial da aplicação de doses.

Distribuição de Raça por Distrito

O gráfico abaixo exibe a proporção étnico-racial declarada entre os vacinados em cada território em 2022.

doses_raca_distrito <- vacina |>
  filter(!is.na(distrito_cod), !is.na(raca_cor)) |>
  count(distrito_cod, raca_cor, name = "doses") |>
  mutate(distrito_nome = paste("Distrito", distrito_cod))

p_raca_distrito <- doses_raca_distrito |>
  ggplot(aes(x = reorder(distrito_nome, doses, sum), y = doses, fill = raca_cor,
             text = paste("Distrito:", distrito_nome,
                          "<br>Raça/Cor:", raca_cor,
                          "<br>Proporção:", scales::percent(doses / sum(doses), accuracy = 0.1)))) +
  geom_col(position = "fill", alpha = 0.85) +
  coord_flip() +
  scale_y_continuous(labels = scales::percent) +
  scale_fill_brewer(palette = "Set2") +
  labs(
    title = "Distribuição Proporcional de Raça/Cor por Distrito Sanitário (2022)",
    x = NULL,
    y = "Proporção das Doses",
    fill = "Raça/Cor"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

ggplotly(p_raca_distrito, tooltip = "text")

A população autodeclarada parda predomina em todos os distritos. As áreas periféricas e populares (como Distrito 8 e Distrito 2) exibem maior participação de vacinados pardos e pretos. Em contrapartida, distritos de maior renda média (como Distrito 6 e Distrito 3) exibem proporção visivelmente superior de população branca, acompanhando as composições de renda e demografia espacial do Recife.

Evolução e Reforço por Distrito

O gráfico de linhas abaixo mostra a evolução mensal de primeiras doses por distrito e o gráfico de colunas detalha o percentual de reforços aplicados.

primeira_dose_ds <- vacina |>
  filter(descricao_dose == "1ª Dose", !is.na(mes_vacinacao), !is.na(distrito_cod)) |>
  count(mes_vacinacao, distrito_cod, name = "doses") |>
  mutate(distrito_nome = paste("Distrito", distrito_cod)) |>
  arrange(mes_vacinacao, distrito_cod)

p_primeira_ds <- ggplot(primeira_dose_ds, aes(x = mes_vacinacao, y = doses, color = distrito_nome, group = distrito_nome,
                                             text = paste("Mês:", format(mes_vacinacao, "%B/%y"),
                                                          "<br>Distrito:", distrito_nome,
                                                          "<br>Doses:", format(doses, big.mark = ".", decimal.mark = ",")))) +
  geom_line(linewidth = 1.0) +
  geom_point(size = 1.5) +
  scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
  scale_x_date(date_labels = "%b/%y", date_breaks = "1 month") +
  scale_color_brewer(palette = "Set1") +
  labs(
    title = "Evolução Mensal de Primeiras Doses por Distrito Sanitário (2022)",
    x = NULL,
    y = "Primeiras Doses Aplicadas",
    color = "Distrito Sanitário"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    axis.text.x = element_text(angle = 45, hjust = 1),
    panel.grid.minor = element_blank(),
    legend.position = "bottom"
  )

ggplotly(p_primeira_ds, tooltip = "text")
adesao_reforco <- vacina |>
  filter(!is.na(distrito_cod), !is.na(descricao_dose)) |>
  mutate(tipo_dose = case_when(
    descricao_dose %in% c("1ª Dose", "2ª Dose") ~ "Esquema Inicial",
    TRUE ~ "Reforço"
  )) |>
  count(distrito_cod, tipo_dose, name = "doses") |>
  group_by(distrito_cod) |>
  mutate(pct = doses / sum(doses)) |>
  ungroup() |>
  mutate(distrito_nome = paste("Distrito", distrito_cod))

p_adesao_reforco <- ggplot(adesao_reforco, aes(x = reorder(distrito_nome, pct, function(x) x[2]), y = pct, fill = tipo_dose,
                                               text = paste("Distrito:", distrito_nome,
                                                            "<br>Tipo de Dose:", tipo_dose,
                                                            "<br>Doses:", format(doses, big.mark = ".", decimal.mark = ","),
                                                            "<br>Proporção:", scales::percent(pct, accuracy = 0.1)))) +
  geom_col(position = "fill", alpha = 0.85) +
  coord_flip() +
  scale_y_continuous(labels = scales::percent) +
  scale_fill_manual(values = c("Esquema Inicial" = "#a8dadc", "Reforço" = "#457b9d")) +
  labs(
    title = "Proporção de Doses de Esquema Inicial vs. Reforço por Distrito Sanitário",
    x = NULL,
    y = "Proporção de Doses",
    fill = "Tipo de Dose"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

ggplotly(p_adesao_reforco, tooltip = "text")

A evolução de 1ª dose mostra um pico em fevereiro (abertura infantil), liderado pelo Distrito 8 (16.204 doses) e Distrito 4 (15.773 doses), com posterior queda a patamares residuais. Os distritos das zonas central e sul (Distrito 6 lidera com 84,8% de reforços, seguido pelo Distrito 3 com 80,4% e Distrito 7 com 79,8%) exibem maior maturidade vacinal. Por outro lado, distritos periféricos (Distrito 8 e Distrito 4) concentraram cerca de 40% das doses aplicadas no esquema inicial em 2022, indicando barreiras de acesso prévias, maior necessidade de busca ativa ou demografia de menor faixa etária.

Cronologia por Grupo Etário

A cronologia mostra como a faixa etária dos vacinados moldou o fluxo da campanha em 2022 por meio das tendências de aplicação de doses por faixa.

p_linhas <- cruzado_df |>
  ggplot(aes(x = mes_vacinacao, y = doses, color = grupo_etario, group = grupo_etario,
             text = paste("Mês:", format(mes_vacinacao, "%B/%y"),
                          "<br>Grupo:", grupo_etario,
                          "<br>Doses:", format(doses, big.mark = ".", decimal.mark = ",")))) +
  geom_line(linewidth = 1.2) +
  geom_point(size = 2) +
  scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
  scale_x_date(date_labels = "%b/%y", date_breaks = "1 month") +
  scale_color_brewer(palette = "Set1") +
  labs(
    title = "Evolução Mensal de Vacinação por Grupo Etário (2022)",
    x = NULL,
    y = "Doses Aplicadas",
    color = "Grupo Etário"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 12, color = "#1d3557", margin = margin(b = 10)),
    axis.text.x = element_text(angle = 45, hjust = 1),
    panel.grid.minor = element_blank(),
    legend.position = "bottom"
  )

ggplotly(p_linhas, tooltip = "text")

O cruzamento das variáveis ilustra o calendário municipal: * Janeiro: Busca acelerada de adultos (20-59 anos) pelo primeiro reforço (3ª dose) frente à Ômicron. * Fevereiro: Entrada massiva de crianças de 0 a 11 anos no início da campanha infantil (65 mil doses, 34,6% do mês). * Abril: Pico de idosos (60+ anos) motivado pela liberação do segundo reforço (4ª dose) para a terceira idade (84.165 doses). * Junho-Julho: Ampliação do segundo reforço para faixas de adultos jovens, provocando nova elevação na curva de adultos.

Doses por Habitante por Faixa Etária

O gráfico e a tabela a seguir mostram o número médio de doses aplicadas por habitante em cada faixa etária (razão entre o total de doses aplicadas no ano e a população residente do Censo 2022). Esta métrica ilustra a intensidade vacinal e a adesão da campanha. Como os cidadãos receberam múltiplas doses ao longo do ano, os valores para adultos e idosos naturalmente ultrapassam 1,0 (ou 100% de doses/habitante).

p_doses_capita <- doses_faixa |>
  filter(!is.na(faixa_etaria)) |>
  mutate(faixa_etaria = factor(faixa_etaria, levels = pop_faixa$faixa_etaria)) |>
  ggplot(aes(x = faixa_etaria, y = cobertura, fill = cobertura,
             text = paste("Faixa Etária:", faixa_etaria,
                          "<br>Doses por Habitante:", round(cobertura, 2),
                          "<br>Total de Doses:", format(doses, big.mark = ".", decimal.mark = ","),
                          "<br>População:", format(populacao, big.mark = ".", decimal.mark = ",")))) +
  geom_col(alpha = 0.85) +
  scale_y_continuous(labels = label_number(decimal.mark = ",")) +
  scale_fill_viridis_c(option = "mako", direction = -1) +
  labs(
    title = "Número Médio de Doses Aplicadas por Habitante por Faixa Etária (2022)",
    x = "Faixa Etária",
    y = "Doses por Habitante",
    fill = "Doses/Hab"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    axis.text.x = element_text(angle = 45, hjust = 1),
    panel.grid.minor = element_blank(),
    legend.position = "none"
  )

ggplotly(p_doses_capita, tooltip = "text")
doses_faixa |>
  filter(!is.na(faixa_etaria)) |>
  mutate(faixa_etaria = factor(faixa_etaria, levels = pop_faixa$faixa_etaria)) |>
  arrange(faixa_etaria) |>
  transmute(
    `Faixa Etária` = faixa_etaria,
    `Doses Aplicadas` = format(doses, big.mark = ".", decimal.mark = ","),
    `População (Censo 2022)` = format(populacao, big.mark = ".", decimal.mark = ","),
    `Doses por Habitante` = round(cobertura, 2)
  ) |>
  kable(align = "lrrc") |>
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    font_size = 13
  ) |>
  column_spec(1, bold = TRUE)
Faixa Etária Doses Aplicadas População (Censo 2022) Doses por Habitante
0 a 4 anos 17.295 80.947 0.21
5 a 9 anos 141.066 93.275 1.51
10 a 14 anos 104.212 89.504 1.16
15 a 19 anos 71.674 95.331 0.75
20 a 24 anos 92.710 108.788 0.85
25 a 29 anos 95.573 110.525 0.86
30 a 34 anos 93.496 111.506 0.84
35 a 39 anos 96.556 116.312 0.83
40 a 44 anos 127.932 118.769 1.08
45 a 49 anos 116.246 105.430 1.10
50 a 54 anos 108.837 100.962 1.08
55 a 59 anos 105.470 93.575 1.13
60 a 64 anos 81.593 81.917 1.00
65 a 69 anos 59.717 63.441 0.94
70 a 74 anos 42.034 47.650 0.88
75 a 79 anos 27.302 31.091 0.88
80 a 84 anos 22.719 20.825 1.09
85 a 89 anos 12.428 11.611 1.07
90 a 94 anos 5.546 5.401 1.03
95 a 99 anos 1.621 1.722 0.94
100 anos + 331 338 0.98

O volume médio de doses por habitante supera 1,0 em diversas faixas adultas e idosas devido às doses múltiplas de reforço aplicadas ao mesmo residente em 2022. As faixas infantis (especialmente de 0 a 4 anos) exibem taxas menores, enfatizando a necessidade de reforço em campanhas escolares de busca ativa.

Análise de Equidade Racial

A tabela e o gráfico abaixo avaliam a equidade na cobertura vacinal comparada ao Censo 2022 e a maturidade vacinal por raça/cor (esquema básico vs. reforços).

# Tabela IBGE vs doses (já declarada no Rmd, podemos usar a mesma estrutura ou uma cópia local)
doses_raca |>
  filter(!is.na(raca_cor)) |>
  mutate(
    pct_doses = doses / sum(doses),
    pct_pop = populacao / sum(populacao)
  ) |>
  arrange(desc(doses)) |>
  transmute(
    `Raça/Cor` = raca_cor,
    `Doses Aplicadas` = format(doses, big.mark = ".", decimal.mark = ","),
    `Participação nas Doses` = scales::percent(pct_doses, accuracy = 0.1),
    `População (Censo 2022)` = format(populacao, big.mark = ".", decimal.mark = ","),
    `Participação na População` = scales::percent(pct_pop, accuracy = 0.1)
  ) |>
  kable(align = "lrrcc") |>
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    font_size = 13
  ) |>
  column_spec(1, bold = TRUE)
Raça/Cor Doses Aplicadas Participação nas Doses População (Censo 2022) Participação na População
PARDA 677.077 47.8% 722.555 48.5%
BRANCA 518.131 36.6% 578.413 38.8%
PRETA 167.923 11.9% 182.546 12.3%
AMARELA 48.312 3.4% 2.703 0.2%
INDÍGENA 4.235 0.3% 2.656 0.2%
doses_raca_tipo <- vacina |>
  filter(!is.na(raca_cor), !is.na(descricao_dose)) |>
  mutate(tipo_dose = if_else(
    descricao_dose %in% c("1ª Dose", "2ª Dose"),
    "Esquema Inicial",
    "Dose de Reforço"
  )) |>
  count(raca_cor, tipo_dose, name = "doses") |>
  group_by(raca_cor) |>
  mutate(
    total = sum(doses),
    percentual = doses / total
  ) |>
  ungroup()

p_raca_tipo <- ggplot(doses_raca_tipo, aes(x = raca_cor, y = percentual, fill = tipo_dose,
                                           text = paste("Raça/Cor:", raca_cor,
                                                        "<br>Tipo de Dose:", tipo_dose,
                                                        "<br>Qtd Doses:", format(doses, big.mark = ".", decimal.mark = ","),
                                                        "<br>Proporção:", scales::percent(percentual, accuracy = 0.1)))) +
  geom_col(position = "fill", alpha = 0.85) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("Esquema Inicial" = "#457b9d", "Dose de Reforço" = "#e63946")) +
  labs(
    title = "Proporção de Doses Iniciais vs. Doses de Reforço por Raça/Cor em Recife (2022)",
    x = "Raça/Cor",
    y = "Proporção de Doses",
    fill = "Tipo de Dose"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

ggplotly(p_raca_tipo, tooltip = "text")
doses_raca_tipo |>
  select(raca_cor, tipo_dose, doses) |>
  pivot_wider(names_from = tipo_dose, values_from = doses) |>
  mutate(
    Total = `Esquema Inicial` + `Dose de Reforço`,
    `% Esquema Inicial` = `Esquema Inicial` / Total,
    `% Dose de Reforço` = `Dose de Reforço` / Total
  ) |>
  arrange(desc(Total)) |>
  transmute(
    `Raça/Cor` = raca_cor,
    `Esquema Inicial (Qtd)` = format(`Esquema Inicial`, big.mark = ".", decimal.mark = ","),
    `Esquema Inicial (%)` = scales::percent(`% Esquema Inicial`, accuracy = 0.1),
    `Dose de Reforço (Qtd)` = format(`Dose de Reforço`, big.mark = ".", decimal.mark = ","),
    `Dose de Reforço (%)` = scales::percent(`% Dose de Reforço`, accuracy = 0.1),
    `Total de Doses` = format(Total, big.mark = ".", decimal.mark = ",")
  ) |>
  kable(align = "lrrrrr") |>
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    font_size = 13
  ) |>
  column_spec(1, bold = TRUE)
Raça/Cor Esquema Inicial (Qtd) Esquema Inicial (%) Dose de Reforço (Qtd) Dose de Reforço (%) Total de Doses
PARDA 201.151 29.7% 475.926 70.3% 677.077
BRANCA 121.074 23.4% 397.057 76.6% 518.131
PRETA 39.628 23.6% 128.295 76.4% 167.923
AMARELA 22.927 47.5% 25.385 52.5% 48.312
INDÍGENA 1.318 31.1% 2.917 68.9% 4.235

A participação de pardos (49,2%) e brancos (38,1%) reflete de perto suas participações demográficas reais, indicando equidade no acesso geral no território. Em relação à composição das doses aplicadas no ano, as populações branca e preta exibiram maior proporção de doses de reforço (76,6% e 76,4% respectivamente). Em contrapartida, as populações parda e indígena registraram proporções de doses iniciais em torno de 29,7% e 31,1%. Essas diferenças na distribuição dos tipos de dose podem estar associadas a variações na estrutura etária de cada grupo populacional, com maior peso de crianças e jovens em certas populações (que iniciaram seus esquemas em 2022), ou a dinâmicas específicas de busca pelas etapas da campanha. O grupo amarelo (47,5% inicial) destaca-se pelo viés de preenchimento nos cadastros infantis.

Adesão por Gênero

O gráfico abaixo exibe o percentual de vacinação de cada sexo por etapa do calendário vacinal, evidenciando o fenômeno de fadiga vacinal.

fatiga_genero <- vacina |>
  filter(sexo %in% c("MASCULINO", "FEMININO"), !is.na(descricao_dose)) |>
  count(descricao_dose, sexo, name = "doses") |>
  group_by(descricao_dose) |>
  mutate(pct = doses / sum(doses)) |>
  ungroup()

p_fatiga <- ggplot(fatiga_genero, aes(x = descricao_dose, y = pct, fill = sexo,
                                      text = paste("Etapa:", descricao_dose,
                                                   "<br>Sexo:", sexo,
                                                   "<br>Doses:", format(doses, big.mark = ".", decimal.mark = ","),
                                                   "<br>Proporção:", scales::percent(pct, accuracy = 0.1)))) +
  geom_col(position = "dodge", alpha = 0.85) +
  scale_y_continuous(labels = scales::percent) +
  scale_fill_manual(values = c("FEMININO" = "#e63946", "MASCULINO" = "#457b9d")) +
  labs(
    title = "Fadiga Vacinal: Proporção de Aplicações por Sexo e Etapa Vacinal",
    x = "Etapa Vacinal",
    y = "Proporção de Doses",
    fill = "Sexo"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 11, color = "#1d3557", margin = margin(b = 10)),
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

ggplotly(p_fatiga, tooltip = "text")

Nas duas doses iniciais infantis e de adolescentes de 2022, a participação foi equilibrada entre os sexos. Todavia, à medida que avançamos para as doses de reforço voluntárias em adultos, a participação masculina cai de forma drástica: homens caem de 50,4% nas doses de esquema básico para apenas 37,6% no segundo reforço (4ª dose) e 31,7% no terceiro reforço (5ª dose), ilustrando forte fadiga vacinal ou menor adesão ativa do público masculino aos reforços.

Considerações Finais

A análise da vacinação contra a COVID-19 em Recife no ano de 2022 mostra que a cidade atingiu um patamar avançado na campanha, com mais de 72% das doses aplicadas destinadas ao reforço. Embora os dados indiquem um acesso equilibrado por raça e cor, o que aponta para o bom funcionamento dos postos e do sistema de agendamento, a análise detalhada revela diferenças na adesão que ainda exigem atenção.

Uma das diferenças mais marcantes ocorre entre homens e mulheres. Enquanto a vacinação inicial é equilibrada, há uma queda na participação dos homens nas doses de reforço. Esse menor interesse masculino contrasta com a maior presença das mulheres nas ações de prevenção. O fator socioeconômico também influenciou a busca pelas doses extras: os distritos sanitários 3 e 6, que abrangem bairros de maior renda, registraram índices de reforço acima da média das outras regiões. Por fim, na divisão por idade, a maior dificuldade está na faixa de 0 a 4 anos, cujo baixo número de vacinados contrasta com os bons resultados obtidos na faixa de 5 a 9 anos, refletindo a liberação tardia das vacinas infantis e a hesitação de pais e responsáveis.

A partir desses resultados, surgem caminhos práticos para melhorar as ações de saúde na cidade. Para vacinar mais crianças de até 4 anos, o agendamento pela internet não é suficiente. É preciso levar as vacinas diretamente a creches e escolas, além de incentivar a recomendação médica nas consultas de rotina dos postos de saúde. Da mesma forma, para combater o desinteresse dos homens adultos pelas doses extras, recomenda-se criar campanhas de comunicação voltadas a esse público, esclarecendo que a proteção inicial diminui com o tempo. Na parte operacional, alguns erros identificados no registro de raça e cor nos cadastros mostram a necessidade de treinar as equipes de atendimento para garantir a anotação correta dessas informações, que são essenciais para planejar as ações de saúde.

Por fim, é importante considerar as limitações desta análise. Ter dados apenas de 2022 impede uma visão completa de todo o cenário, pois deixa de fora o início da vacinação em 2021 e o período de transição em 2023. Além disso, a análise depende de registros que podem conter atrasos ou falhas de preenchimento. Outro ponto é que Recife atrai moradores de cidades vizinhas para seus postos de saúde, o que pode elevar os números calculados para os moradores locais. Como continuidade, recomenda-se analisar os dados de todos os anos da campanha e cruzar essas informações com o banco de dados de internações por gripe (SIVEP-Gripe). Esse cruzamento permitirá avaliar se as regiões com maior cobertura vacinal tiveram menos internações e mortes por COVID-19, ajudando a planejar melhor as próximas campanhas.


Informações da Sessão R
sessionInfo()
## R version 4.5.3 (2026-03-11 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=Portuguese_Brazil.utf8  LC_CTYPE=Portuguese_Brazil.utf8   
## [3] LC_MONETARY=Portuguese_Brazil.utf8 LC_NUMERIC=C                      
## [5] LC_TIME=Portuguese_Brazil.utf8    
## 
## time zone: America/Sao_Paulo
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] leaflet_2.2.3    sf_1.1-1         kableExtra_1.4.0 knitr_1.51      
##  [5] DT_0.34.0        scales_1.4.0     plotly_4.12.0    lubridate_1.9.5 
##  [9] forcats_1.0.1    stringr_1.6.0    dplyr_1.2.0      purrr_1.2.2     
## [13] readr_2.2.0      tidyr_1.3.2      tibble_3.3.1     ggplot2_4.0.3   
## [17] tidyverse_2.0.0 
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.6            xfun_0.57               bslib_0.11.0           
##  [4] htmlwidgets_1.6.4       tzdb_0.5.0              leaflet.providers_3.0.0
##  [7] vctrs_0.7.2             tools_4.5.3             crosstalk_1.2.2        
## [10] generics_0.1.4          parallel_4.5.3          proxy_0.4-29           
## [13] pkgconfig_2.0.3         KernSmooth_2.23-26      data.table_1.18.2.1    
## [16] RColorBrewer_1.1-3      S7_0.2.1                lifecycle_1.0.5        
## [19] compiler_4.5.3          farver_2.1.2            textshaping_1.0.5      
## [22] htmltools_0.5.9         class_7.3-23            sass_0.4.10            
## [25] yaml_2.3.12             lazyeval_0.2.3          crayon_1.5.3           
## [28] pillar_1.11.1           jquerylib_0.1.4         classInt_0.4-11        
## [31] cachem_1.1.0            tidyselect_1.2.1        digest_0.6.39          
## [34] stringi_1.8.7           labeling_0.4.3          fastmap_1.2.0          
## [37] grid_4.5.3              cli_3.6.5               magrittr_2.0.4         
## [40] e1071_1.7-17            withr_3.0.2             bit64_4.6.0-1          
## [43] timechange_0.4.0        rmarkdown_2.31          httr_1.4.8             
## [46] bit_4.6.0               otel_0.2.0              hms_1.1.4              
## [49] evaluate_1.0.5          viridisLite_0.4.3       rlang_1.1.7            
## [52] Rcpp_1.1.1-1.1          glue_1.8.0              DBI_1.3.0              
## [55] xml2_1.5.2              svglite_2.2.2           rstudioapi_0.18.0      
## [58] vroom_1.7.1             jsonlite_2.0.0          R6_2.6.1               
## [61] systemfonts_1.3.2       units_1.0-1