Introdução

A vacinação contra a COVID-19 foi 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, acumulando mais de um milhão de doses aplicadas entre esquemas primários e reforços. Compreender o perfil dessa campanha, incluindo quem foi vacinado, quando, com qual imunobiológico e em qual região da cidade, é fundamental para extrair lições que orientem futuras ações de imunização em larga escala.

Este projeto realiza uma análise exploratória dos dados abertos de vacinação contra COVID-19 em Recife, disponibilizados pela Prefeitura no portal de dados abertos. O tratamento dos dados envolveu padronização de variáveis categóricas, conversão de datas, separação de campos compostos e criação de variáveis derivadas, como contagem de doses por grupo e agregação mensal. Para contextualizar os volumes de doses aplicadas, os registros foram cruzados com estimativas populacionais do Censo Demográfico 2022 do IBGE, permitindo o cálculo de taxas de cobertura por faixa etária, sexo e raça/cor.

A análise exploratória busca revelar padrões na distribuição temporal, demográfica e geográfica da vacinação, identificando comportamentos que não seriam evidentes nos números agregados, como variações no ritmo de aplicação ao longo dos meses, diferenças de cobertura entre grupos populacionais e a concentração espacial das doses nos distritos sanitários.

Os resultados oferecem uma visão detalhada de como a campanha se desenvolveu no território recifense. As análises aqui apresentadas podem servir de referência para a avaliação de futuras ações de imunização em larga escala, bem como para a identificação de pontos de atenção que mereçam acompanhamento em campanhas subsequentes.

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).
janitor Limpeza de dados, padronização de colunas e tabelas de frequência (não ativo).
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.
skimr Criação de resumos estatísticos rápidos da estrutura dos dados (não ativo).
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, incluindo informações demográficas do vacinado (idade, sexo, raça/cor, faixa etária), dados da aplicação (data, dose, fabricante do imunobiológico), grupo prioritário, categoria de atendimento e localização da unidade de saúde (codificada em uma coluna cnes que combina o distrito sanitário e o nome da unidade).

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 é recodificada de valores inteiros (1 a 6) para um fator ordenado com rótulos descritivos ("1ª Dose", "2ª Dose", "1º Reforço", e assim por diante). O uso de factor garante ordenação correta nos gráficos e tabelas.

vacina <- vacina |>
  mutate(descricao_dose = factor(descricao_dose,
    levels = 1:6,
    labels = c("1ª Dose", "2ª Dose", "1º Reforço",
               "2º Reforço", "3º Reforço", "4º 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)

Visão Geral do Dataset

Após todas as transformações, o conjunto conta com 1424361 registros e 16 variáveis.

glimpse(vacina)
## Rows: 1,424,361
## Columns: 16
## $ 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, 45, 37, 44, 43, 35, …
## $ 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> "COMIRNATY (PFIZER)", "COMIRNATY (PFIZER)", "COMIRNA…
## $ descricao_dose    <fct> 1º Reforço, 1º Reforço, 1º Reforço, 1º Reforço, 1º R…
## $ sistema_origem    <chr> "Conecta Recife", "Conecta Recife", "Conecta Recife"…
## $ data_vacinacao    <date> 2022-01-24, 2022-01-11, 2022-01-08, 2022-02-18, 202…
## $ distrito          <chr> "DS 1", "DS 5", "DS 5", "DS 2", "DS 7", "DS 6", "DS …
## $ unidade           <chr> "POLICLÍNICA GOUVEIA DE BARROS", "MATERNIDADE BANDEI…
## $ mes_vacinacao     <date> 2022-01-01, 2022-01-01, 2022-01-01, 2022-02-01, 202…
## $ distrito_cod      <int> 1, 5, 5, 2, 7, 6, 8, 5, 1, 8, 1, 8, 6, 5, 3, 7, 4, 8…
vacina |> slice_sample(n = 200) |> DT::datatable()

Resumo das Variáveis

A tabela a seguir apresenta um resumo consolidado de todas as variáveis do dataset limpo, incluindo o tipo de dado, a quantidade de valores únicos, a proporção de valores ausentes e um exemplo de valor observado.

# Tabela resumida das variáveis do dataset limpo
resumo_vars <- tibble(
  Variável           = names(vacina),
  Tipo               = sapply(vacina, \(x) class(x)[1]),
  `Valores Únicos`   = sapply(vacina, n_distinct),
  `NAs (%)`          = sapply(vacina, \(x) scales::percent(mean(is.na(x)), accuracy = 0.01)),
  `Exemplo`          = sapply(vacina, \(x) {
    val <- na.omit(x)[1]
    if (is.null(val) || length(val) == 0) return("—")
    as.character(val)
  })
)

resumo_vars |>
  kable(align = "llccl", caption = "Resumo Consolidado das Variáveis do Dataset Limpo") |>
  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_vars$`NAs (%)`), "#27ae60", "#e74c3c"
  ))
Resumo Consolidado das Variáveis do Dataset Limpo
Variável Tipo Valores Únicos NAs (%) Exemplo
faixa_etaria character 22 0.00% 35 a 39 anos
idade numeric 120 0.00% 36
sexo character 4 0.00% FEMININO
raca_cor character 6 0.61% PARDA
municipio character 1 0.00% RECIFE
grupo character 29 0.00% TRABALHADORES DA SAÚDE
categoria character 170 89.52% PESSOAS COM OBESIDADE MÓRBIDA (IMC > OU = 40)
lote character 656 0.07% FM2951
vacina_fabricante character 7 0.00% COMIRNATY (PFIZER)
descricao_dose factor 6 0.00% 1º Reforço
sistema_origem character 2 0.00% Conecta Recife
data_vacinacao Date 361 0.00% 2022-01-24
distrito character 8 0.00% DS 1
unidade character 8 0.00% POLICLÍNICA GOUVEIA DE BARROS
mes_vacinacao Date 12 0.00% 2022-01-01
distrito_cod integer 8 0.00% 1

As variáveis podem ser divididas em três grupos. As demográficas (faixa_etaria, idade, sexo e raca_cor) descrevem o perfil do vacinado, com poucos valores ausentes (menos de 0,1% em cada). As variáveis relacionadas à campanha (grupo, categoria, descricao_dose, vacina_fabricante, distrito e unidade) descrevem o contexto da aplicação; categoria permanece ausente em cerca de 89% dos registros, o que é estrutural e não um problema de qualidade. Por fim, a variável temporal data_vacinacao cobre o período de janeiro de 2022 a dezembro de 2022, sem datas ausentes.