library(tidyverse)
library(plotly)
library(scales)
library(ggrepel)
library(DT)
library(RColorBrewer)
library(showtext)
library(here)
sysfonts::font_add_google("News Cycle","news-cycle")
showtext::showtext_auto()
candidatos_df <- tibble::tribble(
~cod, ~nombre, ~color_cand,
"ic", "Iván Cepeda", "#A52E94",
"adle", "Abelardo de la Espriella", "#000066",
"vd", "Vicky Dávila","#A11E4E",
"sf", "Sergio Fajardo", "#442F7E",
"jmg", "Juan Manuel Galán", "#BE1100",
"jcp", "Juan Carlos Pinzón", "#23C062",
"pv", "Paloma Valencia", "#63B9E9",
"cl", "Claudia López", "#00B399",
"ep", "Enrique Peñalosa","#0BDA51",
"jdo", "Juan Daniel Oviedo","#F0E60D",
"voto_blanco","Voto en blanco", "#FFFFFF",
"ns_nr", "No sabe-No responde", "#EAECF0",
"ruido","Ruido","black"
)
encuestas_2026 <- readr::read_csv(here("elecciones","2026-colombia","2026-01-26-entrada","encuestas_2026.csv")) |>
dplyr::mutate(
fecha = lubridate::mdy(fecha),
encuesta_id = 1:dplyr::n(),
encuestadora = factor(encuestadora),
cand_ruido = 1 - rowSums(dplyr::across(-c("fecha","encuesta_id", "encuestadora", "muestra", "voto_blanco","ns_nr")), na.rm = TRUE)) |>
tidyr::pivot_longer(
cols = tidyselect::starts_with("cand"),
names_to = "candidato",
values_to = "int_voto")
# Datos para grafica ####
datos_entrada <- encuestas_2026 |>
dplyr::filter(candidato %in% c("cand_ic", "cand_adle", "cand_pv", "cand_sf", "cand_cl")) |>
dplyr::mutate(candidato = stringr::str_sub(candidato, start = 6L)) |>
dplyr::left_join(candidatos_df, by = c("candidato" = "cod")) |>
dplyr::filter(candidato != "ruido", !is.na(int_voto))
# LOESS por candidato (grilla densa para curva suave) ####
loess_entrada <- datos_entrada |>
dplyr::group_by(nombre) |>
dplyr::filter(dplyr::n() >= 3) |>
dplyr::group_modify(~{
fit <- stats::loess(int_voto ~ as.numeric(fecha), data = .x)
fecha_grid <- seq(min(.x$fecha), max(.x$fecha), by = "day")
tibble::tibble(
fecha = fecha_grid,
int_voto = stats::predict(fit, newdata = data.frame(fecha = as.numeric(fecha_grid)))
)
}) |>
dplyr::ungroup()
# Grafica ####
entrada_2026 <- datos_entrada |>
ggplot2::ggplot(ggplot2::aes(x = fecha, y = int_voto, color = nombre)) +
ggplot2::geom_line(data = loess_entrada,
ggplot2::aes(x = fecha, y = int_voto, color = nombre, group = nombre),
linetype = "dotdash", linewidth = 0.8, show.legend = FALSE) +
ggplot2::geom_point(ggplot2::aes(shape = encuestadora,
text = paste0(nombre, ": ", scales::percent(int_voto, accuracy = 0.1),
"<br>", encuestadora, " (", format(fecha, "%d-%b"), ")")),
size = 4, alpha = 0.85) +
ggplot2::scale_color_manual(values = setNames(candidatos_df$color_cand, candidatos_df$nombre)) +
ggplot2::scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
ggplot2::guides(shape = "none") +
ggplot2::labs(x = NULL, y = "Intención de voto", color = NULL) +
ggplot2::theme_minimal() +
ggplot2::theme(legend.position = "bottom")
#ggplotly ####
plotly::ggplotly(entrada_2026, tooltip = "text") |>
plotly::layout(
legend = list(orientation = "h", y = -0.15, x = 0.5, xanchor = "center"),
xaxis = list(
rangeslider = list(visible = TRUE, thickness = 0.04),
rangeselector = list(
buttons = list(
list(step = "all", label = "Todo"),
list(count = 3, step = "month", stepmode = "backward", label = "3m"),
list(count = 1, step = "month", stepmode = "backward", label = "1m")
)
)
)
)