Ejemplo B-Call: Cámara de Diputados Chile 2025

Análisis de votaciones usando el paquete bcall.

¿Qué es B-Call?

B-Call es un método para analizar comportamiento legislativo que integra dos dimensiones:

¿Por qué B-Call? Modelos tradicionales como W-NOMINATE funcionan bien en sistemas estables con partidos disciplinados. Pero en contextos fragmentados (como Chile o Brasil), legisladores con baja cohesión aparecen artificialmente como "moderados" cuando en realidad son volátiles. B-Call corrige esto al medir explícitamente esa volatilidad como una segunda dimensión.

Instalación

# Instalar bcall
install.packages("devtools")
devtools::install_github("bcallr/bcall")
library(bcall)

Datos Necesarios

Clona el repositorio para obtener todos los archivos:

git clone https://github.com/Alcatruz/bcall-example.git
cd bcall-example

O descarga manualmente estos archivos en una carpeta data/:

Luego carga los datos en R:

rollcall <- read.csv("data/CHL-rollcall-2025.csv", row.names = 1)
clustering_original <- read.csv("data/CHL-clustering-2025.csv", row.names = 1)
clustering_corregido <- read.csv("data/CHL-clustering-2025-CORREGIDO.csv", row.names = 1)

Ejemplo 1: Análisis Automático (bcall_auto)

El análisis automático detecta grupos ideológicos sin necesidad de clasificación previa:

¿Qué es el pivot?

El pivot es un legislador de referencia que define la orientación del eje d1 (izquierda-derecha). Si conoces el contexto político, puedes especificar un legislador de derecha como pivot. Si no lo especificas, B-Call lo elige automáticamente. El pivot solo afecta la dirección (valores positivos vs negativos en d1), no altera el análisis.

rollcall <- read.csv("data/CHL-rollcall-2025.csv", row.names = 1)

resultado_auto <- bcall_auto(
  rollcall = rollcall,
  distance_method = 1,
  pivot = "Alessandri_Vergara_Jorge", # Opcional: si conoces el contexto, especifica un legislador de derecha
                                       # Si no lo incluyes, se elige automáticamente (solo cambia la dirección del eje)
  threshold = 0.1,
  verbose = TRUE
)

# Visualizar
plot_bcall_analysis(
  resultado_auto,
  title = "Cámara de Diputados Chile 2025 - Clustering Automático",
  show_names = FALSE
)
B-Call Automático

Ejemplo 2: Análisis Manual (bcall)

Cuando usamos clustering manual, el análisis depende de la clasificación que proporcionamos.

Con clustering original

clustering_original <- read.csv("data/CHL-clustering-2025.csv", row.names = 1)

resultado_original <- bcall(
  rollcall = rollcall,
  clustering = clustering_original,
  pivot = "Alessandri_Vergara_Jorge",
  threshold = 0.1,
  verbose = TRUE
)

plot_bcall_analysis(resultado_original, show_names = FALSE)

Corrigiendo el etiquetado

clustering_corregido <- read.csv("data/CHL-clustering-2025-CORREGIDO.csv", row.names = 1)

resultado_corregido <- bcall(
  rollcall = rollcall,
  clustering = clustering_corregido,
  pivot = "Alessandri_Vergara_Jorge",
  threshold = 0.1,
  verbose = TRUE
)

plot_bcall_analysis(resultado_corregido, show_names = FALSE)

Comparación Visual

Comparación Clustering Original vs Corregido

Izquierda: clustering original con errores (coherencia ~85%). Derecha: clustering corregido (coherencia ~98%).

El análisis B-Call manual depende de la clasificación que proporcionas.

Recomendación: Usa bcall_auto() cuando no tengas clasificación previa. Usa bcall() cuando tengas clasificación verificada.

Comparación con W-NOMINATE

Primera dimensión: ambos modelos correlacionan (r=0.995). Segunda dimensión: B-Call captura cohesión, W-NOMINATE no.

# Cargar datos W-NOMINATE
library(readxl)
library(dplyr)
library(ggplot2)

wnominate <- read_excel("data/df_wnominate.xlsx")

# Ejecutar B-Call
resultado_bcall <- bcall(
  rollcall = rollcall,
  clustering = clustering_corregido,
  pivot = "Alessandri_Vergara_Jorge",
  threshold = 0.1
)

# Matchear legisladores
comparacion <- resultado_bcall$results %>%
  left_join(wnominate %>% select(legislator, coord1D, coord2D), by = "legislator")

# Correlación primera dimensión
cor(comparacion$d1, comparacion$coord1D)  # r = 0.995

# Gráfico correlación d1
ggplot(comparacion, aes(x = coord1D, y = d1, color = cluster)) +
  geom_point(size = 3, alpha = 0.7) +
  geom_smooth(method = "lm", se = TRUE, color = "black", linetype = "dashed") +
  scale_color_manual(values = c("izquierda" = "#E74C3C", "derecha" = "#3498DB")) +
  labs(title = "Primera Dimensión: B-Call vs W-NOMINATE",
       x = "W-NOMINATE coord1D", y = "B-Call d1") +
  theme_minimal()

# Detectar zonas de volatilidad
# ZONA ROJA: d2 alto + coord2D centrado
outliers_roja <- comparacion %>%
  filter(d2 > 0.9, abs(coord2D) < 0.2) %>%
  mutate(zona = "Roja")

# ZONA AMARILLA: d1 centrado + d2 alto
outliers_amarilla <- comparacion %>%
  filter(abs(d1) < 0.3, d2 > 0.9) %>%
  mutate(zona = "Amarilla")

outliers <- bind_rows(outliers_roja, outliers_amarilla)

# Gráfico zonas
ggplot(comparacion, aes(x = d1, y = d2, color = cluster)) +
  geom_point(size = 3, alpha = 0.6) +
  geom_rect(aes(xmin = -Inf, xmax = Inf, ymin = 0.9, ymax = Inf),
            fill = "red", alpha = 0.05, inherit.aes = FALSE) +
  geom_rect(aes(xmin = -0.3, xmax = 0.3, ymin = 0.9, ymax = Inf),
            fill = "yellow", alpha = 0.1, inherit.aes = FALSE) +
  geom_text(data = outliers, aes(label = legislator), size = 2.5, vjust = -0.5) +
  scale_color_manual(values = c("izquierda" = "#E74C3C", "derecha" = "#3498DB")) +
  labs(title = "B-Call detecta volatilidad legislativa que W-NOMINATE no ve",
       x = "d1 (ideología)", y = "d2 (cohesión: alto = volátil)") +
  theme_minimal()

Primera Dimensión

Correlación d1 vs coord1D

Correlación r=0.995. Ambos modelos capturan ideología.

Segunda Dimensión

Zonas de volatilidad

B-Call detecta dos zonas de volatilidad que W-NOMINATE no captura.

Zona Roja

d2 > 0.9 y |coord2D| < 0.2. Legisladores volátiles que W-NOMINATE ve centrados.

Zona Amarilla

|d1| < 0.3 y d2 > 0.9. Legisladores centrados ideológicamente pero sin cohesión en votación.

Más Información