Zonas eleitorais e perfil de eleitorado em Pernambuco nas eleições para Presidente em 2026 (1º Turno)

Introdução

Contextualização do Problema

Fundamentais para o exercício da democracia, os direitos políticos são garantidos pela Constituição e permitem que a cidadã ou cidadão participe da vida política do país escolhendo seus representantes por meio do voto. No Brasil, o voto é obrigatório para maiores de 18 anos e facultativo para os analfabetos, maiores de 70 e jovens de 16 e 17 anos. A obrigatoriedade do voto, prevista no artigo 14, parágrafo 1º, da Carta Magna, busca fortalecer o processo eleitoral, preservar a ampla participação popular e assegurar que os governantes eleitos reflitam a vontade da maioria. Ainda assim, milhões de pessoas deixam de comparecer a cada eleição.

Segundo o jornal O Globo, mais de 33 milhões de eleitores não compareceram às urnas no primeiro turno das eleições. A abstenção chegou a 21,08% do eleitorado brasileiro, o maior índice desde 1998. Também foram registrados 2,2 milhões de votos em branco e 3,6 milhões de nulos, quase 6 milhões no total. Nessa conta, não entram os votos nulos e brancos, pois os eleitores que optaram por rejeitar os candidatos disponíveis nas urnas precisaram comparecer a seu local de votação. No primeiro turno, 4,77% dos eleitores (quase 6 milhões) optaram pelas duas opções acima

Quando se fala em resultado eleitoral, o foco costuma ficar em quem vence onde. Este relatório propõe olhar também para o outro lado: quem comparece e quem decide faltar.

Com base nisso, o objetivo desse projeto é responder a pergunta de pesquisa: Como as desigualdades sociodemográficas (idade, escolaridade e gênero) das zonas eleitorais de Pernambuco se relacionam com a abstenção e com a preferência por cada candidato à Presidência no 1º turno de 2026?

Origem dos Dados

Os dados aqui trabalhados serão provenientes do Portal de Dados Abertos do TSE, onde ele compila os dados brutos das eleições brasileiras desde 1933, voltados para pesquisadores, pesquisadoras, imprensa e demais pessoas interessadas em analisar os dados eleitorais. O portal concretiza as premissas estabelecidas na Política de Dados Abertos do Tribunal Superior Eleitoral (Portaria TSE nº 93/2021) com a disseminação dos dados abertos do TSE por meio de sítio próprio na internet.

Para a realização desta pesquisa, foram selecionados e combinados dois conjuntos de dados independentes do primeiro turno das Eleições Gerais de 2026:

Como o perfil não chega à seção, a unidade de análise é a zona eleitoral (combinação de município e zona), e as seções da votação são somadas dentro de cada zona. Os arquivos brutos foram disponibilizados originalmente no formato delimitado (.csv), estruturados com separador ponto e vírgula (;), codificação de caracteres Latin-1 e valores ausentes ou não informados codificados textualmente pelo órgão (como #NULO# ou -3).

Pacotes requeridos

Todos os pacotes são carregados aqui, de uma vez, para que quem for replicar saiba o que instalar.

library(tidyverse)   # importação, manipulação (dplyr, tidyr, readr, stringr, forcats) e ggplot2
library(janitor)     # padronização de nomes de colunas (clean_names)
library(stringi)     # remoção de acentos em textos
library(plotly)      # gráficos interativos
library(DT)          # tabelas interativas
library(scales)      # formatação de números e percentuais
library(knitr)       # tabelas simples (kable)
library(kableExtra)  # estilização das tabelas
library(broom)       # resultados de modelos em formato tidy
library(rmdformats)  # Template HTML
Pacote Propósito neste projeto
tidyverse Importar, filtrar, reorganizar (pivot_wider()), unir (inner_join()) e visualizar
janitor Padronizar nomes de colunas vindos do TSE
stringi Tratar acentos e maiúsculas nos textos
plotly Gráficos interativos (dispersão, boxplot, barras, heatmap)
DT Tabelas interativas com filtros e ordenação
scales Formatação de números nos eixos
knitr e kableExtra Tabelas estáticas formatadas
broom Transformar o modelo linear em tabelas
rmdformats Template HTML do relatório (`robobook``)

Preparação dos dados

Importação

Os arquivos do TSE usam ponto e vírgula(;) como separador. Todas as colunas são lidas inicialmente como texto, para evitar que o R adivinhe tipos errados, e convertidas depois, de forma controlada. Os códigos #NULO# e #NE#, que o TSE usa para dado ausente, são tratados como NA.

na_tse <- c("", "NA", "#NULO#", "#NE#")

votacao <- read_delim(params$arq_votacao, delim = ";",
                      locale = locale(encoding = params$enc_votacao),
                      na = na_tse, col_types = cols(.default = col_character())) %>%
  clean_names()

perfil <- read_delim(params$arq_perfil, delim = ";",
                     locale = locale(encoding = params$enc_perfil),
                     na = na_tse, col_types = cols(.default = col_character())) %>%
  clean_names()

# Guarda informações dos dados originais para descrevê-los na próxima aba
dim_vot    <- dim(votacao)
dim_per    <- dim(perfil)
n_cargos   <- n_distinct(votacao$ds_cargo)
dt_ger_vot <- paste(unique(votacao$dt_geracao), collapse = ", ")
dt_ger_per <- paste(unique(perfil$dt_geracao), collapse = ", ")
na_vot     <- colSums(is.na(votacao))
na_per     <- colSums(is.na(perfil))

Dados originais

Os arquivos são publicados pelo TSE para dar transparência à apuração: a base de votação registra o resultado de cada seção, e a base de perfil descreve quem estava apto a votar. Os dados se referem às eleições gerais de 2026, cujo 1º turno ocorreu em 4 de outubro de 2026.

Tamanho e data de geração.

  • Votação: 5.731.614 linhas e 26 variáveis, gerada em 26/10/2026. Reúne 1 cargos em disputa, sendo ele o de Presidente.
  • Perfil: 466.661 linhas e 27 variáveis, gerado em 14/07/2026, ou seja, antes da eleição.

Peculiaridades importantes dos dados de origem:

  • Votação em formato longo por candidato: há uma linha por combinação de candidato × seção, com o número de votos (QT_VOTOS). A base não traz o total de eleitores aptos nem as abstenções,então vamos precisar calcular esses valores.
  • Brancos e nulos como “candidatos”: Aparecem como linhas (95 = Brancos; 96 = Nulos, 97 = anulado e apurado em separado), e precisam ser separados dos votos nominais para melhor apuração dos dados.
  • Único cargo analisado: O arquivo de seções possui somente a análise do Presidente em diferentes seções eleitorais, o que torna mais fácil o foco da análise.
  • Perfil sem seção: a base de perfil chega até município e zona (não há NR_SECAO), em formato longo: uma linha por combinação de gênero × faixa etária × escolaridade × estado civil × raça/cor × zona, com a contagem em QT_ELEITORES. Por isso a unidade de análise é a zona eleitoral.
  • Categorias “não informado”: o perfil usa o texto “NÃO INFORMADO” (e o código -1) quando a informação não está disponível.
  • Texto não padronizado: rótulos com acentos e maiúsculas (por exemplo, “ENSINO MÉDIO COMPLETO”, “LÊ E ESCREVE”).

Colunas com valores ausentes após a leitura (os rótulos “NÃO INFORMADO” não contam como NA):

# Conta quantos valores ausentes (NA) existem em cada coluna de cada base
na_votacao <- tibble(Base = "Votação", Variável = names(na_vot), Ausentes = as.integer(na_vot))
na_perfil  <- tibble(Base = "Perfil",  Variável = names(na_per), Ausentes = as.integer(na_per))

# Junta as duas listas, mantém só as colunas que têm NA e ordena da maior para a menor
na_tab <- bind_rows(na_votacao, na_perfil) %>%
  filter(Ausentes > 0) %>%
  arrange(desc(Ausentes)) %>%
  head(8)

if (nrow(na_tab) > 0) {
  kable(na_tab, align = "llr", format.args = list(big.mark = ".")) %>%
    kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)
} else {
  cat("Nenhuma coluna das duas bases apresentou valores ausentes (NA) após a leitura.")
}
## Nenhuma coluna das duas bases apresentou valores ausentes (NA) após a leitura.

Funções auxiliares

Depois de observar a base de dados, juntamente de suas colunas, foi decidido criar algumas funções auxiliares para ajudar no processamento e informar melhor para o usuário que está sendo transmitido pelos dados. Cada uma está explicada abaixo, com um exemplo pequeno que mostra o que entra e o que sai. Elas são usadas nas etapas seguintes do relatório.

1. Configurações (cfg) e chave das zonas (chave)

cfg é uma lista com valores que podem ser ajustados em um só lugar (os cortes de idade e o tamanho dos rankings). chave guarda os nomes das duas colunas que, juntas, identificam uma zona eleitoral, lembrando que o primeir. Ela será usada nas junções de tabelas.

cfg <- list(
  jovem_ate   = 24,   # faixas de idade que começam até aqui = "jovens" (16 a 24)
  idoso_desde = 60,   # faixas de idade que começam daqui em diante = "idosos"
  n_rank      = 50    # tamanho dos rankings nas tabelas
)

chave <- c("cd_municipio", "nr_zona")   # o perfil não traz o número da seção

cfg$jovem_ate   # acessa um item da lista com $
## [1] 24

2. norm()

Essa função tem o foco em normalizar todos os textos presentes no database para que, por exemplo, “Superior Completo” e “SUPERIOR COMPLETO” sejamtratados como a mesma coisa.

norm <- function(x) {
  x <- stri_trans_general(x, "Latin-ASCII")   # 1. tira os acentos
  x <- str_to_upper(x)                        # 2. deixa em maiúsculas
  x <- str_squish(x)                          # 3. tira espaços sobrando
  x
}

norm("  Ensino   Médio Completo ")   # resultado: "ENSINO MEDIO COMPLETO"
## [1] "ENSINO MEDIO COMPLETO"

3. fmt(), sinal() e forca()

Essas funções vão ajudar a montar frases automáticas no relatório (pra tornar a análise mais reprodutiva em diferentes estados). 1. fmt() formata um número com vírgula decimal; 2. sinal() diz se uma correlação é positiva ou negativa; 3. forca() classifica a intensidade (abaixo de 0,1 é desprezível; até 0,3, fraca; até 0,5, moderada; acima disso, forte). Vale ressaltar que os limites entre grupos foram escolhidos utilizando o coeficiente Kappa de Cohen (K)

fmt <- function(x, d = 1) formatC(x, format = "f", digits = d, decimal.mark = ",")

sinal <- function(r) ifelse(r > 0, "positiva", "negativa")

forca <- function(r) {
  case_when(abs(r) < 0.1 ~ "desprezível",
            abs(r) < 0.3 ~ "fraca",
            abs(r) < 0.5 ~ "moderada",
            TRUE         ~ "forte")
}

fmt(3.14159, 2)                # "3,14"
## [1] "3,14"
sinal(-0.4)                    # "negativa"
## [1] "negativa"
forca(c(0.05, 0.2, 0.4, 0.8))  # "desprezível" "fraca" "moderada" "forte"
## [1] "desprezível" "fraca"       "moderada"    "forte"

4. pivota()

O perfil do eleitorado vem comprido, ou seja muitas linhas por zona, uma para cada categoria. Para unir com a votação, precisamos de uma linha por zona, então, essa função faz isso em dois passos:

  1. soma os eleitores de cada zona e categoria;
  2. transforma cada categoria em uma coluna (pivot_wider), preenchendo com 0 quando a zona não tem aquela categoria.

O trecho .data[[var]] significa “a coluna cujo nome está guardado em var”, o que permite usar a mesma função para escolaridade, faixa etária e gênero.

pivota <- function(df, var) {
  df %>%
    group_by(cd_municipio, nr_zona, categoria = .data[[var]]) %>%    # agrupa por zona e categoria
    summarise(n = sum(qt_eleitores), .groups = "drop") %>%           # soma os eleitores
    pivot_wider(names_from = categoria, values_from = n,             # categoria vira coluna
                values_fill = 0, names_prefix = paste0(var, "_"))
}

# Exemplo com dados pequenos: 2 zonas e 2 níveis de escolaridade
exemplo <- tibble(
  cd_municipio = c(1, 1, 1, 1, 1, 1),
  nr_zona      = c(10, 10, 10, 11, 11, 11),
  escol        = c("medio", "medio", "superior_completo", "medio", "superior_completo", "superior_completo"),
  qt_eleitores = c(60, 20, 20, 50, 30, 20)
)

kable(exemplo)                   # ANTES: 6 linhas
cd_municipio nr_zona escol qt_eleitores
1 10 medio 60
1 10 medio 20
1 10 superior_completo 20
1 11 medio 50
1 11 superior_completo 30
1 11 superior_completo 20
kable(pivota(exemplo, "escol"))  # DEPOIS: 1 linha por zona, uma coluna por categoria
cd_municipio nr_zona escol_medio escol_superior_completo
1 10 80 20
1 11 50 50

Na zona 10, os 80 eleitores de ensino médio (60 + 20) e os 20 de superior completo viram as colunas escol_medio e escol_superior_completo.

Filtros e limpeza

A análise foca em Presidente no 1º turno em Pernambuco, então filtramos logo, reduzindo muito o tamanho da base de votação. Convertemos os campos numéricos (lidos como texto) e normalizamos os textos antes de qualquer comparação, para evitar que “Superior Completo” e “SUPERIOR COMPLETO” sejam tratados como categorias diferentes.

# Passo 1: converte para número as colunas que o R leu como texto
votacao <- votacao %>%
  mutate(cd_municipio = as.integer(cd_municipio),
         nr_zona      = as.integer(nr_zona),
         nr_secao     = as.integer(nr_secao),
         nr_turno     = as.integer(nr_turno),
         nr_votavel   = as.integer(nr_votavel),
         qt_votos     = as.integer(qt_votos))

perfil <- perfil %>%
  mutate(cd_municipio = as.integer(cd_municipio),
         nr_zona      = as.integer(nr_zona),
         qt_eleitores = as.integer(qt_eleitores))

# Passo 2: mantém só a UF, o cargo e o turno da análise
votacao <- votacao %>%
  filter(sg_uf == params$uf,
         norm(ds_cargo) == params$cargo,
         nr_turno == params$turno)

perfil <- perfil %>%
  filter(sg_uf == params$uf)

# Passo 3: conta seções e zonas que sobraram (usado no texto abaixo)
n_secoes_filtro <- n_distinct(votacao$cd_municipio, votacao$nr_zona, votacao$nr_secao)
n_zonas_filtro  <- n_distinct(votacao$cd_municipio, votacao$nr_zona)

# Passo 4: verificação. Segundo o dicionário do TSE, 95 = voto em branco e 96 = voto nulo.
# Sem essas linhas o comparecimento seria subestimado e a abstenção, superestimada.
n_brn <- sum(votacao$nr_votavel %in% c(95, 96))
if (n_brn == 0) stop("A base de votação não traz votos brancos/nulos: comparecimento seria subestimado.")

Após os filtros restam 156.699 linhas na votação, que cobrem 21.418 seções distribuídas em 209 zonas eleitorais (combinações de município e zona).

Padronização de categorias: Criamos três variáveis categóricas a partir dos rótulos do TSE: a escolaridade (agrupada em quatro níveis, mais “não informado”), a faixa etária (jovens, adultos, idosos) e o gênero. Os cortes de idade vêm da lista cfg. Vale notar que o voto é facultativo para 16 e 17 anos e a partir dos 70, o que pode influenciar a abstenção nesses grupos.

Passo 1: escolaridade O texto é padronizado com norm() e cada rótulo do TSE vai para um de cinco grupos.

perfil <- perfil %>%
  mutate(esc_n = norm(ds_grau_escolaridade),
         escol = case_when(
           str_detect(esc_n, "SUPERIOR COMPLETO")   ~ "superior_completo",
           str_detect(esc_n, "SUPERIOR INCOMPLETO") ~ "superior_incompleto",
           str_detect(esc_n, "MEDIO")               ~ "medio",
           str_detect(esc_n, "ANALFABETO|LE E ESCREVE|FUNDAMENTAL") ~ "fundamental_ou_menos",
           TRUE ~ "nao_informado"))

Passo 2: faixa etária parse_number() pega o primeiro número do rótulo (“55 a 59 anos” vira 55). Se esse número for até o corte de jovens, a faixa é “jovens”; se for a partir do corte de idosos, “idosos”; no meio, “adultos”. Os cortes vêm da lista cfg.

perfil <- perfil %>%
  mutate(idade_ini = parse_number(ds_faixa_etaria),
         faixa = case_when(
           is.na(idade_ini)             ~ "nao_informado",
           idade_ini <= cfg$jovem_ate   ~ "jovens",
           idade_ini >= cfg$idoso_desde ~ "idosos",
           TRUE                         ~ "adultos"))

Passo 3: gênero Por fim, é analisado qual o gênero, com três itens: FEMININO, MASCULINO e NÃO INFORMADO

perfil <- perfil %>%
  mutate(gen_n  = norm(ds_genero),
         genero = case_when(
           str_detect(gen_n, "FEMININO")  ~ "mulheres",
           str_detect(gen_n, "MASCULINO") ~ "homens",
           TRUE                           ~ "nao_informado"))

Conferência de todas as categorias Para conferir, nenhuma categoria deve cair em “nao_informado” sem motivo:

perfil %>%
  count(ds_grau_escolaridade, escol, wt = qt_eleitores, name = "eleitores") %>%
  kable(col.names = c("Rótulo original", "Categoria criada", "Eleitores"),
        format.args = list(big.mark = ".")) %>%
  kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)
Rótulo original Categoria criada Eleitores
ANALFABETO fundamental_ou_menos 432.693
ENSINO FUNDAMENTAL COMPLETO fundamental_ou_menos 332.362
ENSINO FUNDAMENTAL INCOMPLETO fundamental_ou_menos 1.622.083
ENSINO MÉDIO COMPLETO medio 1.978.608
ENSINO MÉDIO INCOMPLETO medio 1.247.876
LÊ E ESCREVE fundamental_ou_menos 664.126
NÃO INFORMADO nao_informado 334
SUPERIOR COMPLETO superior_completo 603.235
SUPERIOR INCOMPLETO superior_incompleto 344.427

Reshape do perfil

Como explicado anteriormente na introdução da base de dados, o perfil vem em formato longo (muitas linhas por zona), mas para unir com a votação precisamos de uma linha por zona. Usamos pivot_wider() para levar cada categoria para uma coluna e depois calculamos o percentual de cada grupo no eleitorado da zona.

# Passo 1: uma tabela larga para cada dimensão
escol_wide  <- pivota(perfil, "escol")
faixa_wide  <- pivota(perfil, "faixa")
genero_wide <- pivota(perfil, "genero")

# Passo 2: junta as três tabelas pela chave da zona
perfil_wide <- escol_wide %>%
  full_join(faixa_wide,  by = chave) %>%
  full_join(genero_wide, by = chave)

# Passo 3: garante que todas as colunas esperadas existem (uma categoria pode não aparecer)
esperadas <- c("escol_fundamental_ou_menos", "escol_medio", "escol_superior_incompleto",
               "escol_superior_completo", "escol_nao_informado",
               "faixa_jovens", "faixa_adultos", "faixa_idosos",
               "genero_mulheres", "genero_homens")
faltam <- setdiff(esperadas, names(perfil_wide))
if (length(faltam) > 0) perfil_wide[faltam] <- 0

# Passo 4: total de eleitores da zona e percentual de cada grupo
perfil_wide <- perfil_wide %>%
  mutate(total_perfil = escol_fundamental_ou_menos + escol_medio + escol_superior_incompleto +
                        escol_superior_completo + escol_nao_informado) %>%
  filter(total_perfil > 0) %>%
  mutate(pct_escol_fundamental_ou_menos = escol_fundamental_ou_menos / total_perfil * 100,
         pct_escol_medio                = escol_medio                / total_perfil * 100,
         pct_escol_superior_incompleto  = escol_superior_incompleto  / total_perfil * 100,
         pct_escol_superior_completo    = escol_superior_completo    / total_perfil * 100,
         pct_faixa_jovens               = faixa_jovens               / total_perfil * 100,
         pct_faixa_adultos              = faixa_adultos              / total_perfil * 100,
         pct_faixa_idosos               = faixa_idosos               / total_perfil * 100,
         pct_genero_mulheres            = genero_mulheres            / total_perfil * 100,
         pct_genero_homens              = genero_homens              / total_perfil * 100)

# Passo 5: escolaridade predominante = o grupo com maior percentual na zona.
# pmax() devolve, linha a linha, o maior entre as colunas listadas.
perfil_wide <- perfil_wide %>%
  mutate(maior = pmax(pct_escol_fundamental_ou_menos, pct_escol_medio,
                      pct_escol_superior_incompleto, pct_escol_superior_completo),
         escol_pred = case_when(
           pct_escol_fundamental_ou_menos == maior ~ "fundamental_ou_menos",
           pct_escol_medio                == maior ~ "medio",
           pct_escol_superior_incompleto  == maior ~ "superior_incompleto",
           TRUE                                    ~ "superior_completo")) %>%
  select(cd_municipio, nr_zona, starts_with("pct_"), escol_pred)

Abstenção e votos

Um dos focos da nossa pesquisa é analisar a abstenção e votos proeminentes nas zonas eleitorais de Pernambuco e a base de votação que foi baixada não informa quantos eleitores estavam aptos, então para calcular esse dado de abstenções, utilizamos:

  • Aptos da zona = soma de QT_ELEITORES do perfil na zona.
  • Comparecimento da zona = soma de todos os votos (nominais, brancos e nulos) para Presidente nas seções da zona.
  • Abstenções = aptos − comparecimento.

\[\text{Taxa de abstenção} = \frac{\text{aptos} - \text{comparecimento}}{\text{aptos}} \times 100\] Os votos nominais excluem brancos e nulos, e o percentual de cada candidato é calculado sobre o total de votos nominais (válidos) da zona.

# 1. Eleitores aptos por zona = soma do perfil
aptos_zona <- perfil %>%
  group_by(cd_municipio, nr_zona) %>%
  summarise(nm_municipio = first(nm_municipio),
            qt_aptos     = sum(qt_eleitores),
            .groups = "drop")

# 2. Comparecimento por zona = soma de todos os votos registrados
comparecimento <- votacao %>%
  group_by(cd_municipio, nr_zona) %>%
  summarise(qt_comparecimento = sum(qt_votos), .groups = "drop")

# 3. Junta as duas tabelas e calcula a abstenção
zona <- aptos_zona %>%
  inner_join(comparecimento, by = chave) %>%
  filter(qt_aptos > 0) %>%
  mutate(qt_abstencoes  = qt_aptos - qt_comparecimento,
         taxa_abstencao = qt_abstencoes / qt_aptos * 100)

# 4. Votos nominais: tira branco (95), nulo (96) e anulado e apurado em separado (97)
cand_nom <- votacao %>%
  filter(!nr_votavel %in% c(95, 96, 97))

# 5. Votos de cada candidato em cada zona (TODOS os candidatos)
votos_cand <- cand_nom %>%
  group_by(cd_municipio, nr_zona, nm_municipio, nm_votavel) %>%
  summarise(votos = sum(qt_votos), .groups = "drop")

# 6. Percentual de cada candidato dentro da zona
votos_cand <- votos_cand %>%
  group_by(cd_municipio, nr_zona) %>%
  mutate(votos_validos = sum(votos),
         pct_voto      = votos / votos_validos * 100) %>%
  ungroup() %>%
  filter(votos_validos > 0)

# 7. Vencedor de cada zona = candidato com mais votos
vencedor <- votos_cand %>%
  group_by(cd_municipio, nr_zona) %>%
  slice_max(votos, n = 1, with_ties = FALSE) %>%
  ungroup() %>%
  select(cd_municipio, nr_zona, vencedor = nm_votavel)

# 8. Ranking estadual e os candidatos mais votados (os demais viram "OUTROS")
ranking <- cand_nom %>%
  group_by(nm_votavel) %>%
  summarise(votos = sum(qt_votos), .groups = "drop") %>%
  arrange(desc(votos))

top_cand <- head(ranking$nm_votavel, params$n_top_cand)

# 9. Uma coluna de % para cada candidato (pivot_wider)
cand_wide <- votos_cand %>%
  mutate(cand = if_else(nm_votavel %in% top_cand, nm_votavel, "OUTROS")) %>%
  group_by(cd_municipio, nr_zona, cand) %>%
  summarise(pct = sum(pct_voto), .groups = "drop") %>%
  pivot_wider(names_from = cand, values_from = pct, values_fill = 0, names_prefix = "voto_")

# 10. Verificação: zonas com abstenção negativa (comparecimento maior que os aptos do perfil)
n_neg <- sum(zona$taxa_abstencao < 0)

Verificação de consistência O perfil foi gerado antes da eleição, então o número de aptos pode diferir um pouco do eleitorado no dia da votação. Contudo, foi gerado 0 zona(s) com abstenção negativa, o que indica uma alta taxa de congruência dos dados aqui analisados.

Junção das bases

Para assegurar a integridade relacional entre as bases, a junção só foi executada após a agregação e o reshape dos dados, garantindo a granularidade estrita de uma linha por zona eleitoral em cada tabela. Usamos inner_join(), que mantém apenas zonas presentes em todas as bases, e checamos quantas ficaram de fora.

# Junta as quatro tabelas, todas com uma linha por zona
base <- zona %>%
  inner_join(perfil_wide, by = chave) %>%
  inner_join(cand_wide,   by = chave) %>%
  inner_join(vencedor,    by = chave)

# Cria duas colunas auxiliares:
# - vencedor_grp: mantém os candidatos do top e troca os demais por "OUTROS" (fct_other)
# - escol_f: escolaridade predominante como fator, com ordem e nomes legíveis
base <- base %>%
  mutate(vencedor_grp = fct_other(vencedor, keep = top_cand, other_level = "OUTROS"),
         escol_f = factor(escol_pred,
                          levels = c("fundamental_ou_menos", "medio",
                                     "superior_incompleto", "superior_completo"),
                          labels = c("Fundamental ou menos", "Médio",
                                     "Superior incompleto", "Superior completo")))

n_sem_perfil <- nrow(anti_join(comparecimento, perfil_wide, by = chave))

Das 209 zonas da votação, 0 não tinham perfil correspondente e ficaram de fora. A base final tem 209 zonas.

Base final

Cada linha é uma zona eleitoral (município × zona). As variáveis principais são:

Variável Descrição Tipo
cd_municipio, nr_zona Chave que identifica a zona Inteiro
nm_municipio Nome do município Texto
qt_aptos Eleitores aptos na zona (soma do perfil; criada) Inteiro
taxa_abstencao (aptos − comparecimento) / aptos × 100 (criada) Numérico (%)
pct_faixa_jovens / idosos % de jovens e de idosos entre os aptos (criada) Numérico (%)
pct_genero_mulheres % de mulheres entre os aptos (criada) Numérico (%)
pct_escol_* % por nível de escolaridade (criada) Numérico (%)
escol_f Escolaridade predominante da zona (criada) Fator
voto_<candidato> % dos votos nominais de cada candidato (criada) Numérico (%)
vencedor / vencedor_grp Candidato mais votado na zona (criada) Texto / Fator

A base final tem 209 linhas e 26 colunas. Uma amostra de 8 linhas:

set.seed(2026)  # reprodutibilidade da amostra

base %>%
  select(nm_municipio, nr_zona, qt_aptos, taxa_abstencao, pct_faixa_jovens,
         pct_faixa_idosos, pct_escol_superior_completo, vencedor) %>%
  slice_sample(n = 8) %>%
  kable(digits = 1, format.args = list(big.mark = ".", decimal.mark = ","),
        col.names = c("Município", "Zona", "Aptos", "Abstenção (%)", "% jovens",
                      "% idosos", "% superior compl.", "Vencedor")) %>%
  kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)
Município Zona Aptos Abstenção (%) % jovens % idosos % superior compl. Vencedor
OLINDA 117 101.271 14,5 11,5 22,6 9,6 LUIZ INÁCIO LULA DA SILVA
BOM CONSELHO 61 34.230 21,4 15,2 23,4 5,1 LUIZ INÁCIO LULA DA SILVA
BUÍQUE 60 42.622 27,8 16,8 22,8 3,6 LUIZ INÁCIO LULA DA SILVA
LAJEDO 94 32.472 19,5 15,2 20,6 5,5 LUIZ INÁCIO LULA DA SILVA
INGAZEIRA 50 4.256 16,3 14,2 24,7 4,4 LUIZ INÁCIO LULA DA SILVA
LAGOA DE ITAENGA 135 18.503 16,7 14,8 20,7 4,8 LUIZ INÁCIO LULA DA SILVA
SANTA CRUZ DO CAPIBARIBE 109 66.209 13,8 15,0 14,4 4,4 FLAVIO NANTES BOLSONARO
SÃO LOURENÇO DA MATA 13 83.724 15,9 13,0 18,7 6,3 LUIZ INÁCIO LULA DA SILVA

Resumo das variáveis

# Estatísticas de cada variável de interesse (uma linha por variável)
resumo <- base %>%
  select(taxa_abstencao, qt_aptos, pct_faixa_jovens, pct_faixa_idosos,
         pct_genero_mulheres, pct_escol_superior_completo) %>%
  pivot_longer(everything(), names_to = "variavel", values_to = "valor") %>%
  group_by(variavel) %>%
  summarise(Média = mean(valor), Mediana = median(valor), DP = sd(valor),
            Mín = min(valor), Máx = max(valor), .groups = "drop")

# Troca os nomes das colunas por rótulos legíveis
resumo <- resumo %>%
  mutate(Variável = recode(variavel,
                           taxa_abstencao              = "Taxa de abstenção (%)",
                           qt_aptos                    = "Eleitores aptos por zona",
                           pct_faixa_jovens            = "% de jovens (16 a 24)",
                           pct_faixa_idosos            = "% de idosos (60+)",
                           pct_genero_mulheres         = "% de mulheres",
                           pct_escol_superior_completo = "% com superior completo")) %>%
  select(Variável, Média, Mediana, DP, Mín, Máx)

resumo %>%
  kable(digits = 1, align = "lrrrrr", format.args = list(big.mark = ".", decimal.mark = ",")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Variável Média Mediana DP Mín Máx
% com superior completo 5,9 4,6 4,4 0,8 38,0
% de idosos (60+) 21,5 21,3 2,8 13,0 30,9
% de jovens (16 a 24) 14,2 14,4 2,0 7,4 20,4
% de mulheres 52,6 52,5 1,5 48,0 56,0
Eleitores aptos por zona 34.572,9 19.891,0 32.958,4 3.701,0 131.958,0
Taxa de abstenção (%) 19,4 19,0 3,5 13,1 33,5

Em média, 19,4% dos eleitores aptos de uma zona não compareceram (mediana de 19,0%), com valores entre 13,1% e 33,5%. A zona média tem cerca de 34573 eleitores aptos, e 52,6% deles são mulheres. Os 5 candidatos mais votados no estado são: Luiz Inácio Lula Da Silva, Flavio Nantes Bolsonaro, Augusto Jorge Cury, Renan Antonio Ferreira Dos Santos, Ronaldo Ramos Caiado.

Análise exploratória dos dados

1. Quem falta

Pergunta: a abstenção muda conforme a idade e a escolaridade das zonas?

Idade

O foco desta seção é investigar se a idade do eleitorado influencia a decisão de ir ou não votar. Para isso, cruzamos a proporção de jovens (16 a 24 anos) e de idosos (60 anos ou mais) com a taxa de abstenção em cada zona eleitoral de Pernambuco, identificando tendências e padrões de comparecimento às urnas. Para isso, vamos utilizar um gráfico de dispersão com linha de tendência (pra observar se a correlação cresce ou não), no qual cada ponto representa uma zona eleitoral e a linha vermelha indica a direção média da relação entre as variáveis.

base %>%
  select(taxa_abstencao, qt_aptos, `% de jovens (16 a 24)` = pct_faixa_jovens,
         `% de idosos (60+)` = pct_faixa_idosos) %>%
  pivot_longer(-c(taxa_abstencao, qt_aptos), names_to = "grupo", values_to = "pct") %>%
  ggplot(aes(pct, taxa_abstencao)) +
  geom_point(aes(size = qt_aptos), alpha = .4, color = "grey30", show.legend = FALSE) +
  geom_smooth(aes(weight = qt_aptos), method = "lm", color = "darkred") +
  facet_wrap(~grupo, scales = "free_x") +
  scale_y_continuous(labels = label_number(suffix = "%")) +
  scale_x_continuous(labels = label_number(suffix = "%")) +
  labs(title = "A abstenção cresce ou cai com o peso de jovens e idosos?",
       subtitle = "Cada ponto é uma zona eleitoral (tamanho = eleitores aptos); a linha vermelha é o ajuste linear e o acizentado é o intervalo de confiança (95%)",
       x = "Participação do grupo no eleitorado da zona",
       y = "Taxa de abstenção") +
  theme_minimal(base_size = 12)

r_jov <- cor(base$taxa_abstencao, base$pct_faixa_jovens)
r_ido <- cor(base$taxa_abstencao, base$pct_faixa_idosos)

Insight: A relação entre abstenção e % de jovens é positiva e moderada (r = 0,38); com % de idosos, é positiva e fraca (r = 0,11). Lembre que o voto é facultativo para 16-17 anos e 70+.

Escolaridade

Aqui, analisamos por outra visão, o nível de escolaridade com um gráfico boxplot, para observar a distribuição de taxa de abstenção de acordo com o nível de instrução de cada eleitor e se há alguma correlação.

plot_ly(base, x = ~escol_f, y = ~taxa_abstencao, color = ~escol_f, type = "box",
        boxpoints = "all", jitter = .4, pointpos = 0, marker = list(opacity = .4),
        showlegend = FALSE) %>%
  layout(title = "Abstenção por escolaridade predominante da zona",
         xaxis = list(title = "Escolaridade predominante entre os aptos da zona"),
         yaxis = list(title = "Taxa de abstenção (%)"))
med_esc <- base %>% group_by(escol_f) %>% summarise(m = median(taxa_abstencao), .groups = "drop")

Insight: A mediana da abstenção vai de 13,7% (zonas de superior completo) a 19,9% (zonas de fundamental ou menos), uma diferença de 6,2 pontos percentuais.

2. Quem vence onde

Pergunta: os candidatos vencem em zonas com perfis educacionais e níveis de abstenção diferentes?

Abstenção onde cada candidato venceu

Para responder a essa questão, primeiro analisamos a taxa de abstenção nas zonas eleitorais agrupadas pelo candidato vencedor. O gráfico de caixa (boxplot) horizontal abaixo permite comparar se as regiões onde cada candidato obteve a vitória apresentam níveis de não comparecimento semelhantes ou distintos.

plot_ly(base, x = ~taxa_abstencao,
        y = ~fct_reorder(vencedor_grp, taxa_abstencao, median),
        color = ~vencedor_grp, type = "box", orientation = "h",
        boxpoints = "all", jitter = .4, pointpos = 0, marker = list(opacity = .4),
        showlegend = FALSE) %>%
  layout(title = "Qual a abstenção nas zonas em que cada candidato venceu?",
         xaxis = list(title = "Taxa de abstenção (%)"),
         yaxis = list(title = ""))

Voto e escolaridade

Depois de analisar as taxas de abstenções por zona, vamos observar o tipo de preferência eleitoral de acordo com a escolaridade de cada zona, em um gráfico que possui duas dimensões: (i) Porcentagem de votos nominais de um candidato na zona e (ii) Porcentagem de eleitores com ensino superior completo.

Cada ponto é uma zona; clique na legenda para ligar e desligar candidatos.

long <- base %>%
  select(all_of(chave), nm_municipio, pct_escol_superior_completo, starts_with("voto_")) %>%
  pivot_longer(starts_with("voto_"), names_to = "candidato", values_to = "pct_voto",
               names_prefix = "voto_") %>%
  mutate(candidato = str_to_title(candidato))

plot_ly(long, x = ~pct_escol_superior_completo, y = ~pct_voto, color = ~candidato,
        type = "scatter", mode = "markers", opacity = .6,
        text = ~paste0(nm_municipio, " | zona ", nr_zona),
        hoverinfo = "text+x+y") %>%
  layout(title = "Preferência eleitoral e escolaridade das zonas",
         xaxis = list(title = "% de eleitores com ensino superior completo"),
         yaxis = list(title = "% dos votos nominais do candidato na zona"))

Correlação por candidato

Por fim, para completar, vamos analisar a correlação de Pearson entre a preferência eleitoral por candidato, o nível de escolaridade da zona e a taxa de abstenção.

# Para cada candidato: correlação do % de votos com o % de superior completo e com a abstenção
cor_cand <- base %>%
  select(taxa_abstencao, pct_escol_superior_completo, starts_with("voto_")) %>%
  pivot_longer(starts_with("voto_"), names_to = "candidato", values_to = "pct_voto",
               names_prefix = "voto_") %>%
  group_by(candidato) %>%
  summarise(`r com % superior`  = cor(pct_voto, pct_escol_superior_completo),
            `r com abstenção`   = cor(pct_voto, taxa_abstencao),
            `% médio nas zonas` = mean(pct_voto),
            .groups = "drop") %>%
  mutate(candidato = str_to_title(candidato))

datatable(cor_cand, rownames = FALSE, options = list(dom = "t")) %>%
  formatRound(2:4, 2)   # arredonda as colunas 2 a 4 para 2 casas decimais
# Candidato com maior e menor correlação (usados no texto abaixo)
mais_esc  <- cor_cand$candidato[which.max(cor_cand$`r com % superior`)]
menos_esc <- cor_cand$candidato[which.min(cor_cand$`r com % superior`)]

Insight: O candidato cujo voto mais cresce com a escolaridade das zonas é Outros (r = 0,78), e o que mais cai é Luiz Inácio Lula Da Silva (r = -0,42).

3. Tamanho da abstenção

Pergunta: quando medimos todos os candidatos sobre o eleitorado apto (e não sobre os votos válidos), onde a abstenção se posiciona?

A porcentagem de votos válidos esconde quem não foi votar. Aqui o foco de análise é todo o eleitorado apto, e a abstenção entra na comparação como se fosse “mais uma opção”.

# 1. Votos de cada candidato nas zonas analisadas
tot_cand <- votos_cand %>%
  semi_join(base, by = chave) %>%
  group_by(nm_votavel) %>%
  summarise(votos = sum(votos), .groups = "drop")

# 2. Totais do estado
tot_aptos   <- sum(base$qt_aptos)
tot_abst    <- sum(base$qt_abstencoes)
tot_validos <- sum(tot_cand$votos)
tot_brn     <- tot_aptos - tot_abst - tot_validos   # o que sobra = brancos + nulos

# 3. Candidatos do top individualizados; os demais somados em "Outros candidatos"
painel_cand <- tot_cand %>%
  mutate(grupo = if_else(nm_votavel %in% top_cand, str_to_title(nm_votavel), "Outros candidatos")) %>%
  group_by(grupo) %>%
  summarise(votos = sum(votos), .groups = "drop")

# 4. As outras "opções" do eleitorado
painel_outros <- tibble(grupo = c("ABSTENÇÃO", "Brancos e nulos"),
                        votos = c(tot_abst, tot_brn))

# 5. Junta tudo e calcula o peso de cada opção sobre o eleitorado APTO
painel <- bind_rows(painel_cand, painel_outros) %>%
  mutate(pct_aptos = votos / tot_aptos * 100) %>%
  arrange(desc(votos))

pos_abst     <- which(painel$grupo == "ABSTENÇÃO")           # posição da abstenção no ranking
pct_top_cand <- max(tot_cand$votos) / tot_aptos * 100        # % dos aptos do candidato mais votado
painel$destaque <- if_else(painel$grupo == "ABSTENÇÃO", "Abstenção", "Demais")

# 6. Gráfico de barras: a abstenção em vermelho, as demais opções em cinza
plot_ly(painel, y = ~fct_reorder(grupo, pct_aptos), x = ~pct_aptos, type = "bar",
        orientation = "h", color = ~destaque,
        colors = c("Abstenção" = "darkred", "Demais" = "grey50"),
        text = ~paste0(round(pct_aptos, 1), "%"), textposition = "outside",
        hovertemplate = "%{y}: %{x:.1f}% dos aptos<extra></extra>", showlegend = FALSE) %>%
  layout(title = "Peso de cada opção sobre o eleitorado apto de Pernambuco",
         xaxis = list(title = "% do eleitorado apto"), yaxis = list(title = ""))

Insight: A abstenção reuniu 18,2% do eleitorado apto (1.314.682 pessoas), o que a coloca em 3º lugar se fosse tratada como uma opção. O candidato mais votado alcançou 49,2% dos aptos.

4. Modelo e zonas atípicas

Pergunta: descontando idade, gênero e escolaridade, quais zonas se abstêm muito mais do que o esperado?

Para isso, vamos utilizar regressão linear de forma descritiva: ela vai estimar quanto cada característica está associada à abstenção quando as demais são mantidas constantes.Para garantir precisão metodológica e evitar distorções provocadas por zonas pequenas, a estimação foi ponderada pelo eleitorado apto (weights = qt_aptos).

# Regressão linear: abstenção explicada por idade, escolaridade e gênero.
# weights = qt_aptos dá mais peso às zonas com mais eleitores.
modelo <- lm(taxa_abstencao ~ pct_faixa_jovens + pct_faixa_idosos +
               pct_escol_superior_completo + pct_genero_mulheres,
             data = base, weights = qt_aptos)

# tidy() transforma o resultado do modelo em uma tabela; recode() troca os nomes técnicos
tidy(modelo, conf.int = TRUE) %>%
  mutate(term = recode(term,
                       "(Intercept)"                 = "Intercepto",
                       "pct_faixa_jovens"            = "% jovens",
                       "pct_faixa_idosos"            = "% idosos",
                       "pct_escol_superior_completo" = "% superior completo",
                       "pct_genero_mulheres"         = "% mulheres")) %>%
  select(Termo = term, Estimativa = estimate, `IC 95% inf.` = conf.low,
         `IC 95% sup.` = conf.high, `p-valor` = p.value) %>%
  kable(digits = 3, align = "lrrrr") %>%
  kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)
Termo Estimativa IC 95% inf. IC 95% sup. p-valor
Intercepto -15.257 -35.868 5.354 0.146
% jovens 1.320 1.064 1.576 0.000
% idosos 0.719 0.578 0.859 0.000
% superior completo -0.089 -0.158 -0.020 0.011
% mulheres 0.021 -0.301 0.343 0.897
r2 <- glance(modelo)$r.squared   # R²: parte da variação explicada pelo modelo

Insight: O modelo (ponderado pelo número de aptos) explica 53,8% da variação da abstenção entre zonas (R² = 0,538). A estimativa indica quantos pontos percentuais a abstenção muda para cada ponto percentual a mais do grupo, mantidas as demais características.

Zonas que mais fogem do esperado (maior resíduo positivo):

augment(modelo, data = base) %>%
  arrange(desc(.resid)) %>%
  slice_head(n = 20) %>%
  transmute(Município = nm_municipio, Zona = nr_zona, Aptos = qt_aptos,
            `Abstenção observada (%)` = taxa_abstencao,
            `Abstenção esperada (%)` = .fitted,
            `Diferença (p.p.)` = .resid) %>%
  datatable(rownames = FALSE, filter = "top", options = list(pageLength = 10, scrollX = TRUE)) %>%
  formatRound(4:6, 1)

5. Correlações em conjunto

Visão geral das relações entre o perfil das zonas, a abstenção e o voto em cada candidato. No código, rename_with() aplica uma função (aqui, tirar o prefixo voto_ e deixar o nome capitalizado) a vários nomes de coluna de uma vez, e cor() calcula a correlação entre todos os pares de colunas, gerando uma matriz.

vars_cor <- base %>%
  select(Abstenção = taxa_abstencao, Jovens = pct_faixa_jovens, Idosos = pct_faixa_idosos,
         Mulheres = pct_genero_mulheres, `Superior completo` = pct_escol_superior_completo,
         starts_with("voto_")) %>%
  rename_with(~ str_to_title(str_remove(.x, "voto_")), starts_with("voto_"))

matriz <- cor(as.matrix(vars_cor), use = "complete.obs")   # matriz de correlação

plot_ly(x = colnames(matriz), y = rownames(matriz), z = matriz, type = "heatmap",
        colorscale = list(c(0, "#2B5C8F"), c(.5, "#ffffff"), c(1, "#C0392B")),
        zmin = -1, zmax = 1, hovertemplate = "%{y} × %{x}: %{z:.2f}<extra></extra>") %>%
  layout(title = "Matriz de correlação entre perfil, abstenção e votos (por zona)",
         yaxis = list(autorange = "reversed"))

6. Tabelas interativas

Essas tabelas vão ajudar numa pesquisa mais específica de cada zona, para observar o que está acontecendo em cada zona eleitoral.

Municípios

# Vencedor de cada município: soma os votos de cada candidato no município e pega o maior
venc_muni <- votos_cand %>%
  semi_join(base, by = chave) %>%
  group_by(nm_municipio, nm_votavel) %>%
  summarise(votos = sum(votos), .groups = "drop") %>%
  group_by(nm_municipio) %>%
  slice_max(votos, n = 1, with_ties = FALSE) %>%
  ungroup() %>%
  select(nm_municipio, vencedor_muni = nm_votavel)

# Resumo por município: zonas, aptos, abstenção e escolaridade (média ponderada pelos aptos)
muni <- base %>%
  group_by(nm_municipio) %>%
  summarise(zonas    = n(),
            aptos    = sum(qt_aptos),
            abst     = sum(qt_abstencoes) / sum(qt_aptos) * 100,
            superior = weighted.mean(pct_escol_superior_completo, qt_aptos),
            .groups = "drop") %>%
  left_join(venc_muni, by = "nm_municipio")

# Tabela interativa
datatable(muni, rownames = FALSE, filter = "top", options = list(pageLength = 10),
          colnames = c("Município", "Zonas", "Aptos", "Abstenção (%)",
                       "% superior compl.", "Vencedor")) %>%
  formatRound(c("abst", "superior"), 1) %>%
  formatCurrency("aptos", currency = "", digits = 0, mark = ".") %>%
  formatStyle("abst", background = styleColorBar(range(muni$abst), "lightcoral"),
              backgroundSize = "100% 80%", backgroundRepeat = "no-repeat",
              backgroundPosition = "center")

Zonas mais contrastantes

As 50 zonas com maior abstenção e as 50 com maior proporção de eleitores com ensino superior completo.

bind_rows(
  base %>% slice_max(taxa_abstencao, n = cfg$n_rank, with_ties = FALSE) %>%
    mutate(grupo = "Maior abstenção"),
  base %>% slice_max(pct_escol_superior_completo, n = cfg$n_rank, with_ties = FALSE) %>%
    mutate(grupo = "Maior escolaridade")
) %>%
  select(grupo, nm_municipio, nr_zona, qt_aptos, taxa_abstencao,
         pct_faixa_jovens, pct_faixa_idosos, pct_escol_superior_completo, vencedor) %>%
  datatable(filter = "top", rownames = FALSE, options = list(pageLength = 10, scrollX = TRUE),
            colnames = c("Grupo", "Município", "Zona", "Aptos", "Abstenção (%)",
                         "% jovens", "% idosos", "% superior compl.", "Vencedor")) %>%
  formatRound(c("taxa_abstencao", "pct_faixa_jovens", "pct_faixa_idosos",
                "pct_escol_superior_completo"), 1)

Conclusões

Este trabalho teve o intuito de realizar uma análise exploratória dos dados provenientes das seções e zonas eleitorais que foram registradas nas eleições presidenciais brasileiras de 2026 em Pernambuco. O relatório investigou como idade, escolaridade e gênero do eleitorado das zonas eleitorais de Pernambuco se associam à abstenção e à preferência pelos candidatos à Presidência no 1º turno de 2026, com o foco em responder se poderiámos associar as abstenções adquiridas na zona com alguma característica sociodemográfica ou eleitoral. Para isso, foram unidas duas bases públicas do TSE (votação presidencial por seção em todo o Brasil e perfil eleitoral dos eleitores de Pernambuco. Ao nível de zona eleitoral, foram analisadas 209 zonas eleitorais em Pernambuco.

As etapas para trabalhar com essa basse de dados foram filtrar, limpar e padronizar, reorganizar o perfil, derivar aptos e abstenção por zona, unir por chave composta e criar variáveis.

Após isso, tivemos algumas resoluções:

  • Idade: a abstenção tem relação positiva e moderada com o % de jovens (r = 0,38) e positiva e fraca com o % de idosos (r = 0,11).
  • Escolaridade: a mediana da abstenção vai de 19,9% nas zonas de fundamental ou menos a 13,7% nas zonas de superior completo.
  • Peso da abstenção: ela somou 18,2% dos aptos, ficando em 3º lugar quando comparada com os candidatos.
  • Preferência: o voto em Outros é o que mais cresce com a escolaridade e em Luiz Inácio Lula Da Silva o que mais cai.
  • Modelo de análise: idade, gênero e escolaridade explicam 53,8% da variação da abstenção, o que deixa espaço para fatores não observados.

Essa análise pode ajudar campanhas e partidos para trazer mobilizações a favor de sua equipe, com foco em zonas de alta abstenção, como também para o TRE-PE, aumentando a conscientização do voto para a sociedade. Foi notado que a desigualdade na participação acompanha desigualdades de escolaridade e de idade, o que reforça a leitura territorial da participação política.

Referências