Algunos datos (tal vez interesantes) de las paso 2019
20 Ago 2019 28 minsLa justicia electoral publicó una serie de tablas que representa los datos de los telegramas con el escrutinio de cada mesa. Una buena oportunidad para practicar un poco con R
y de paso intentar responder algunas preguntas.
Antes de empezar
La fuente oficial dónde descargar los datos es esta: http://descargaresultados.s3-sa-east-1.amazonaws.com/resultados.zip, son varios archivos delimitados por el pipe (|
), sin embargo, elaboré un paquete que ya trae precargados estos datos y además incorpora una reformulación de los mismos bajo un modelo relacional clásico, que para el que este acostunbrado a trabajar con bases de datos, como yo, seguramente es una foma más cómoda. El paquete se llama paso2019, se puede descar e instalar en R
o bien, si ya disponemos de devtools
, simplemente podremos hacer:
devtools::install_gitgub("pmoracho/paso2019")
Luego simplemente cargamos el paquete junto con los otros que vamos a necesitar. ggelegant
es otro paquete que armé, que contiene un tema para ggplot2 en el que estoy trabajando, igual que el anterior:
devtools::install_gitgub("pmoracho/ggelegant")
Importante: A la fecha, los datos, telegramas y la página, están “off-line” a la espera de la elección definitiva del 27 de octubre. Del paquete paso2019
se podrán usar todos los datos, pero las rutinas que consultan y visualizan los telegramas, lamentablemente ya no funcionan.
library("paso2019")
library("tidyverse")
library("ggplot2")
library("ggelegant")
Aclaraciones iniciales
Hay algunas inconsistencias en los datos que en algún momento puede llamar la atención. Por empezar hay una inconsistencia entre las tres tablas de mesas, descargadas del sitio oficial de los resultados. Podemos verificarlo así:
data.frame(tabla = c("mesas_totales",
"mesas_totales_lista",
"mesas_totales_agrp_politica"
),
registros = c( mesas_totales %>% distinct(CODIGO_MESA) %>% nrow(),
mesas_totales_lista %>% distinct(CODIGO_MESA) %>% nrow(),
mesas_totales_agrp_politica %>% distinct(CODIGO_MESA) %>% nrow()
)
) %>%
kable()
tabla | registros |
---|---|
mesas_totales | 100142 |
mesas_totales_lista | 100148 |
mesas_totales_agrp_politica | 100148 |
Podemos ver que mesas_totales
tiene seis mesas menos que el resto de las tablas. Particularmente son las siguientes:
mesas_totales_lista %>%
anti_join(mesas_totales, by = "CODIGO_MESA") %>%
distinct(CODIGO_MESA) %>%
kable()
CODIGO_MESA |
---|
0206300917X |
0207900541X |
0207900582X |
0207900606X |
0207900649X |
0207900738X |
La otra inconsistencia notable, es entre esta información y la que se publica en la página web: https://resultados.gob.ar, la mesas escrutadas según esta página son 100,156 mesas, los datos descargados, indican en el mejor de los casos 100,148 mesas, es decir 8 mesas menos.
Preguntas
¿Cuantas mesas fueron escrutadas y cuantas no?
mesas %>%
summarise(total = n()) %>%
as.numeric() -> mesas_totales
mesas %>%
group_by(escrutada=ifelse(escrutada, "Si", "No")) %>%
summarise(cantidad = n()) %>%
mutate(porcentaje=cantidad/mesas_totales*100) %>%
union(mesas %>%
summarise(escrutada = "Totales", cantidad = n(), porcentaje=100)
) %>%
arrange(-cantidad) %>%
kable()
escrutada | cantidad | porcentaje |
---|---|---|
Totales | 103134 | 100.000000 |
Si | 100568 | 97.511975 |
No | 2566 | 2.488025 |
En este análisis hemos considerado las mesas no escrutadas, aquellas dónde la cantidad de votos de todas las listas asociadas es cero. Aquí también hay una discrepancia con la página, la cual informa un 98.6% de mesas escrutadas y en el caso de los datos publicados, el porcentaje es algo menor 97.54%.
¿Quién ganó en la categoría de Presidente y Vice?
Acá es dónde tenemos que plantear todas las relaciones para llegar a la información que necesitamos, alquien que venga del mundo de las bases de datos se sentirá cómodo, los que vienen de la estadística, lo verán demasiado complejo tal vez.
votos %>%
left_join(listas, by = "id_lista") %>%
left_join(agrupaciones, by = "id_agrupacion") %>%
left_join(categorias, by = "id_categoria") %>%
left_join(meta_agrupaciones, by = "id_meta_agrupacion") %>%
filter(nombre_categoria == "Presidente y Vicepresidente de la República") %>%
group_by(nombre_meta_agrupacion, votos_totales) %>%
summarise(votos = sum(votos)) %>%
ungroup() %>%
arrange(-votos) %>%
mutate(porcentaje = votos / votos_totales,
acumulado = cumsum(porcentaje)) %>%
select(nombre_meta_agrupacion, votos, porcentaje, acumulado) %>%
kable()
nombre_meta_agrupacion | votos | porcentaje | acumulado |
---|---|---|---|
FRENTE DE TODOS | 11622428 | 0.4765586 | 0.4765586 |
JUNTOS POR EL CAMBIO | 7825208 | 0.3208599 | 0.7974185 |
CONSENSO FEDERAL | 2007035 | 0.0822952 | 0.8797137 |
VOTOS en BLANCO | 758988 | 0.0311211 | 0.9108347 |
FRENTE DE IZQUIERDA Y DE TRABAJADORES - UNIDAD | 697776 | 0.0286112 | 0.9394459 |
FRENTE NOS | 642662 | 0.0263513 | 0.9657972 |
UNITE POR LA LIBERTAD Y LA DIGNIDAD | 533100 | 0.0218589 | 0.9876561 |
MOVIMIENTO AL SOCIALISMO | 173585 | 0.0071176 | 0.9947737 |
FRENTE PATRIOTA | 58575 | 0.0024018 | 0.9971754 |
MOVIMIENTO DE ACCION VECINAL | 36324 | 0.0014894 | 0.9986648 |
PARTIDO AUTONOMISTA | 32562 | 0.0013352 | 1.0000000 |
De cualquier manera, con las tablas originales podemos hacer algo parecido:
mesas_totales_lista %>%
filter(CODIGO_CATEGORIA=="000100000000000") %>%
summarise(sum(VOTOS_LISTA)) %>%
as.numeric() -> votos_totales
mesas_totales_lista %>%
filter(CODIGO_CATEGORIA=="000100000000000") %>%
left_join(descripcion_postulaciones, by = c("CODIGO_CATEGORIA", "CODIGO_AGRUPACION", "CODIGO_LISTA")) %>%
group_by(NOMBRE_AGRUPACION) %>%
summarise(votos=sum(VOTOS_LISTA)) %>%
arrange(-votos) %>%
mutate(porcentaje = votos / votos_totales,
acumulado = cumsum(porcentaje)) %>%
select(NOMBRE_AGRUPACION, votos, porcentaje) %>%
kable()
NOMBRE_AGRUPACION | votos | porcentaje |
---|---|---|
FRENTE DE TODOS | 11622428 | 0.4918660 |
JUNTOS POR EL CAMBIO | 7825208 | 0.3311661 |
CONSENSO FEDERAL | 2007035 | 0.0849386 |
FRENTE DE IZQUIERDA Y DE TRABAJADORES - UNIDAD | 697776 | 0.0295302 |
FRENTE NOS | 642662 | 0.0271977 |
UNITE POR LA LIBERTAD Y LA DIGNIDAD | 533100 | 0.0225610 |
MOVIMIENTO AL SOCIALISMO | 173585 | 0.0073462 |
FRENTE PATRIOTA | 58575 | 0.0024789 |
MOVIMIENTO DE ACCION VECINAL | 36324 | 0.0015372 |
PARTIDO AUTONOMISTA | 32562 | 0.0013780 |
En los datos originales, los VOTOS en BLANCO no se consideran al nivel de una agrupación, por lo que no se ven en el agrupado por estas, hay que ir a buscarlos a mesas_totales
, en el modelo nuevo estos se consideran como si fueran una agrupación más, lo que es más consistente con ley que regula las Paso.
¿Cómo fue la distribución de votos de las agrupaciones más importantes?
Miremos la categoría de Predidente y Vice, primero armamos los datos:
votos %>%
filter(id_categoria == 137) %>%
left_join(listas, by = "id_lista") %>%
left_join(agrupaciones, by = "id_agrupacion") %>%
left_join(meta_agrupaciones, by = "id_meta_agrupacion") %>%
inner_join(votos %>%
group_by(id_mesa, id_categoria) %>%
summarize(total_mesa = sum(votos)) %>%
filter(total_mesa != 0), by = c("id_mesa", "id_categoria")) %>%
mutate(porcentaje = votos/total_mesa) %>%
select(nombre_meta_agrupacion, id_mesa, porcentaje, votos) -> votos_porcentaje
votos_porcentaje %>%
group_by(nombre_meta_agrupacion) %>%
summarise(total_votos = sum(votos)) %>%
arrange(-total_votos) %>%
filter(total_votos > quantile(total_votos, .3)) -> top
colores <- c("#26a7ed", "#fbfb00", "#b8867b", "#950000", "#996600", "#4d4d4d", "#57BFEB")
Y ahora la gráfica:
votos_porcentaje %>%
inner_join(top, by = "nombre_meta_agrupacion") %>%
ggplot(aes(x=fct_reorder(nombre_meta_agrupacion, porcentaje),
y=porcentaje,
color=nombre_meta_agrupacion)) +
scale_x_discrete(labels = function(x) str_wrap(x, 10)) +
geom_boxplot(color=rev(colores)) +
coord_flip() +
labs(title="Porcentajes de Votos",
subtitle="Distribución de los % por mesa en la categoria Presidente",
caption="fuente: DINE",
x="",
color=NULL) +
theme_elegante_std(base_family = "Raleway") +
theme(legend.position="none") +
theme(axis.text.x = element_text(size=8))
Es interesante ver el boxplot y prestar atención a los “outliers”, en particular los que caen el limite máximo, son las mesas dónde solo hay votos para una determinada agrupación. Interesante que al revisar estos casos vulneramos fácilmente el secreto del voto.
# Mesas con el 100% de votos a una agrupación
votos_porcentaje %>%
filter(porcentaje == 1) %>%
left_join(mesas, by = "id_mesa") %>%
left_join(circuitos, by = "id_circuito") %>%
left_join(secciones, by = "id_seccion") %>%
left_join(distritos, by = "id_distrito") %>%
left_join(establecimientos, by = "id_establecimiento") %>%
select(nombre_meta_agrupacion,
nombre_distrito,
nombre_seccion,
nombre_circuito,
nombre_establecimiento,
codigo_mesa,
votos) -> mesas_100
mesas_100 %>%
group_by(nombre_meta_agrupacion) %>%
summarise(mesas = n(), votos = sum(votos)) %>%
arrange(-votos) %>%
union(mesas_100 %>%
summarise(nombre_meta_agrupacion = "Total", mesas = n(), votos = sum(votos))
) %>%
kable()
nombre_meta_agrupacion | mesas | votos |
---|---|---|
Total | 63 | 2636 |
FRENTE NOS | 1 | 1 |
MOVIMIENTO AL SOCIALISMO | 1 | 2 |
FRENTE DE IZQUIERDA Y DE TRABAJADORES - UNIDAD | 1 | 3 |
JUNTOS POR EL CAMBIO | 20 | 204 |
VOTOS en BLANCO | 19 | 354 |
FRENTE DE TODOS | 21 | 2072 |
No lo voy a hacer, pero claramente es muy fácil llegar a estas mesas, para luego ir al padrón y relacionar votos con personas, claro aún faltaría saber exactamente quienes votaron y quienes no concurrieron, pero en estos casos es claro que es muy sencillo vulnerar el secreto del voto.
Es raro no, que todos los votos vayan a una única agrupación ¿no? sin embargo, estamos hablando de 63 mesas sobre 100,148, y no llegan a ser más de 2600 votos, por lo que no seamos suspicaces en esto. Podemos claro estudiar más en detalle el tema:
# Revisemos 10 casos
mesas_100 %>%
select(nombre_meta_agrupacion,
nombre_distrito,
nombre_seccion,
nombre_circuito,
nombre_establecimiento,
codigo_mesa,
votos) %>%
arrange(-votos) %>%
head(10) %>%
kable()
nombre_meta_agrupacion | nombre_distrito | nombre_seccion | nombre_circuito | nombre_establecimiento | codigo_mesa | votos |
---|---|---|---|---|---|---|
FRENTE DE TODOS | SANTIAGO DEL ESTERO | RÍO HONDO | CIRCUITO POZUELOS | ESCUELA N° 829 | 2202202012X | 279 |
VOTOS en BLANCO | BUENOS AIRES | BAHÍA BLANCA | CIRCUITO 74 | ESCUELA EP N°70/ES N°25 | 0200700142X | 272 |
FRENTE DE TODOS | SANTIAGO DEL ESTERO | SAN MARTÍN | CIRCUITO ATOJ POZO | ESCUELA N° 747 | 2202402154X | 260 |
FRENTE DE TODOS | SANTIAGO DEL ESTERO | ATAMISQUI | CIRCUITO MEDELLIN | ESCUELA N° 507 | 2200500851X | 246 |
FRENTE DE TODOS | SANTIAGO DEL ESTERO | BANDA | CIRCUITO CHAUPI POZO | ESCUELA N° 66 MAURO CARRANZA | 2200601184X | 244 |
FRENTE DE TODOS | SANTIAGO DEL ESTERO | CHOYA | CIRCUITO VILLA LA PUNTA | COLEGIO SECUNDARIO JUAN N. BURES | 2200901327X | 243 |
FRENTE DE TODOS | SANTIAGO DEL ESTERO | RÍO HONDO | CIRCUITO VINARA | ESCUELA N° 867 | 2202202007X | 239 |
FRENTE DE TODOS | FORMOSA | MATACOS | CIRCUITO 82 | EPEP N° 438 | 0900801352X | 213 |
FRENTE DE TODOS | SANTIAGO DEL ESTERO | PELLEGRINI | CIRCUITO AHI VEREMOS | ESCUELA N° 1006 | 2201801812X | 171 |
JUNTOS POR EL CAMBIO | SANTIAGO DEL ESTERO | CAPITAL | CIRCUITO ESTE | COLEGIO SAN FRANCISCO | 2200100229X | 111 |
¿Que observamos? muchas mesas con pocos votos, por lo que es mucho más probable que vayan todos a una única agrupación, ni hablar de las mesas que tienen 1 solo voto (eso podría ser motivo para otro análisis). Sin embargo también hay varias mesas con una cantidad importante de votos, por ejemplo, con más de 100 votos hay 10 mesas, la mayoría de los votos se los lleva el FRENTE de TODOS, una de VOTO en BLANCO y otra para JUNTOS por el CAMBIO. Si queremos estudiar estos casos y verificar si existió algún error de carga, podríamos obtener las urls
de las imagenes de los telegramas:
mesas_100 %>%
filter(votos >= 100) %>%
mutate(url = get_telegrama_url(codigo_mesa),
cmd = paste0("view_telegrama('", codigo_mesa, "')")) %>%
select(codigo_mesa, nombre_meta_agrupacion, votos, url) %>%
kable()
O incluso, ver directamente la imagen, mediante:
view_telegrama('0900801352X')
view_telegrama('2200100229X')
view_telegrama('2200500851X')
view_telegrama('2200601184X')
view_telegrama('2200901327X')
view_telegrama('2201801812X')
view_telegrama('2202202007X')
view_telegrama('2202202012X')
view_telegrama('2202402154X')
view_telegrama('0200700142X')
La otra curiosidad que me surgió es:
¿Cual es la distribución de votos por mesa?
# Calculamos los votos totales por mesa
votos_porcentaje %>%
group_by(id_mesa) %>%
summarize(total_votos = sum(votos)) -> mesas_votos
## Warning: The `printer` argument is deprecated as of rlang 0.3.0.
## This warning is displayed once per session.
summary(mesas_votos$total_votos)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.0 244.0 261.0 252.9 274.0 351.0
Siempre dentro de la categoría Presidente y Vice, la media de votos por mesa es de 253, con una curva centrada alrededor de los 300 votos por mesas y con un claro sesgo hacía la derecha. La mesa con mayor cantidad de votos tuvo 351.
# Calculamos la media de votos por mesa
media_total_votos <- round(mean(mesas_votos$total_votos),0)
# Para evitar un geom_histogram que es muy lento es que
# generamos esta función que hace lo mismo dibujando las
# barras mediante geom_rect
# + info: https://es.stackoverflow.com/questions/295767/c%c3%b3mo-mejorar-performance-de-un-geom-histogram
res = hist(mesas_votos$total_votos, plot=FALSE, breaks=1000)
dat = data.frame(xmin=head(res$breaks, -1L),
xmax=tail(res$breaks, -1L),
ymin=0.0,
ymax=res$counts)
ggplot(dat, aes(xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)) +
geom_rect(size=0.5, colour="#718FD7", fill="#718FD7") +
geom_vline(aes(xintercept = media_total_votos),
col = '#BC3204', size=1) +
geom_text(aes(label = paste("media:", media_total_votos), y = 1400, x = media_total_votos+1),
vjust = -1, col='#BC3204',size = 4, angle = 90) +
labs(title="Cantidad de votos por mesa",
subtitle="Distribución de cantidades de votos por mesa en la categoria Presidente",
caption="fuente: DINE",
x="Cantidad de mesas",
y="Cantidad de votos",
color=NULL) +
theme_elegante_std(base_family = "Raleway")
¿Y.. cuales fueron los distritos que más votos aportaron a las principales agrupaciones?
En esta elección hubo dos grandes fuerzas, el FRENTE de TODOS y JUNTOS por el CAMBIO, veamos dónde unos sacaron mayor diferencia sobre los otros. Primero preparamos los datos de los 10 distritos más importantes para cada una de estas agrupaciones:
votos_porcentaje %>%
filter(nombre_meta_agrupacion %in% c('FRENTE DE TODOS','JUNTOS POR EL CAMBIO')) %>%
left_join(mesas, by = "id_mesa") %>%
left_join(secciones, by = "id_seccion") %>%
left_join(distritos, by = "id_distrito") %>%
mutate(fdt = ifelse(nombre_meta_agrupacion == 'FRENTE DE TODOS',votos, 0),
jpc = ifelse(nombre_meta_agrupacion == 'JUNTOS POR EL CAMBIO',votos, 0)
) %>%
group_by(nombre_distrito, nombre_seccion) %>%
summarise(dif = sum(fdt-jpc)) %>%
ungroup() %>%
arrange(dif) -> diferencias_distritos
colores <- c("#26a7ed", "#fbfb00")
diferencias_distritos %>%
head(10) %>%
union(diferencias_distritos %>%
tail(10)) %>%
mutate(nombre_distrito = case_when( nombre_distrito == 'CIUDAD AUTÓNOMA DE BUENOS AIRES' ~ 'CABA',
nombre_distrito == 'BUENOS AIRES' ~ 'BS.AS.',
TRUE ~ nombre_distrito)
) %>%
mutate(area = paste(nombre_distrito, nombre_seccion, sep = ' / '),
agrupacion = ifelse(dif > 0, 'FRENTE DE TODOS', 'JUNTOS POR EL CAMBIO')) %>%
select(area,agrupacion,diferencia = dif) -> top_10_distritos
Y ahora sí, veamos los datos:
top_10_distritos %>%
ggplot(aes(x=fct_reorder(area,diferencia),y=diferencia)) +
geom_bar(stat='identity', aes(fill=agrupacion), width=.7) +
geom_text(aes(label=format(abs(diferencia), nsmall=0, big.mark=",")), vjust=0.38) +
scale_fill_manual(labels = function(x) str_wrap(x, 20), values = colores) +
scale_y_continuous(labels = scales::comma) +
coord_flip() +
labs(title='Top 10 de Distritos por agrupación',
subtitle="Diferencias en Votos entre las dos principales fuerzas",
caption="fuente: DINE",
x="",
color=NULL) +
theme(axis.text.y = element_text(size=8)) +
theme_elegante_std(base_family = "Raleway")
¿Y en cuantas mesas ganó cada agrupación?
votos_porcentaje %>%
select(nombre_meta_agrupacion, id_mesa, votos) %>%
arrange(id_mesa, -votos) %>%
group_by(id_mesa) %>%
mutate(nr = row_number()) %>%
filter(nr == 1) %>%
group_by(nombre_meta_agrupacion) %>%
summarize(mesas = n()) %>%
arrange(-mesas) %>%
kable()
nombre_meta_agrupacion | mesas |
---|---|
FRENTE DE TODOS | 65656 |
JUNTOS POR EL CAMBIO | 30512 |
CONSENSO FEDERAL | 122 |
VOTOS en BLANCO | 77 |
FRENTE DE IZQUIERDA Y DE TRABAJADORES - UNIDAD | 23 |
FRENTE NOS | 16 |
MOVIMIENTO AL SOCIALISMO | 5 |
UNITE POR LA LIBERTAD Y LA DIGNIDAD | 4 |
FRENTE PATRIOTA | 3 |
MOVIMIENTO DE ACCION VECINAL | 1 |