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:
- Votação por seção eleitoral, com os votos de cada candidato no Brasil Inteiro, contabilizando https://dadosabertos.tse.jus.br/dataset/resultados-2026/resource/01ea4ccd-f443-469c-9f29-c69ff97f7d4c;
- Perfil do eleitorado, com gênero, faixa etária e escolaridade dos eleitores aptos, detalhado até o nível de zona eleitoral, somente de Pernambuco. https://dadosabertos.tse.jus.br/dataset/eleitorado-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 emQT_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"
## [1] "negativa"
## [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:
- soma os eleitores de cada zona e categoria;
- 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 |
| 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_ELEITORESdo 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 (%)"))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 |
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
- TRIBUNAL SUPERIOR ELEITORAL (TSE). Portal de Dados Abertos. Resultados: votação por seção eleitoral e Eleitorado: perfil do eleitorado, eleições 2026. Disponível em: https://dadosabertos.tse.jus.br. Acesso em: 05/11/2026.
- Abstenções no Segundo Turno - Jornal O Globo https://oglobo.globo.com/politica/eleicoes-2026/guia/abstencao-no-segundo-turno-entenda-o-que-e-o-indice-e-quem-pode-votar-no-proximo-domingo.ghtml
- Voto obrigatório e voto facultativo: saiba o que diz a Constituição e quais as consequências de não votar - TSE https://www.tre-sp.jus.br/comunicacao/noticias/2025/Agosto/voto-obrigatorio-e-voto-facultativo-saiba-o-que-diz-a-constituicao-e-quais-as-consequencias-de-nao-votar
- Definição e Exemplo de Análise Bivariada https://www.statisticshowto.com/probability-and-statistics/statistics-definitions/bivariate-analysis/
- Correlação de Pearson - Psicometria Online https://www.blog.psicometriaonline.com.br/o-que-e-correlacao-de-pearson
- RMD Formats - Repositório https://github.com/juba/rmdformats
- Base de Dados Utilizada:
- Perfil de Eleitorado em 2026 - TSE https://dadosabertos.tse.jus.br/dataset/eleitorado-2026;
- Resultado Eleitoral para Presidente (Primeiro Turno) - TSE <https://dadosabertos.tse.jus.br/dataset/resultados-2026/resource/01ea4ccd-f443-469c-9f29-c69ff97f7d4c;