Helen Granados Rodríguez
CC: 1000835249
hgranados@unal.edu.co
Universidad Nacional de Colombia
Este documento presenta un análisis de las redes de poder en Colombia a partir de una base de datos bipartita que relaciona funcionarios públicos con las entidades en las que fueron nombrados a lo largo de seis gobiernos: Uribe 1, Uribe 2, Santos 1, Santos 2, Duque y Petro. El enfoque sigue la metodología de Análisis Estadístico de Redes (Sosa, 2024; Luke, 2015), construyendo redes de co-membresía a partir de proyecciones de matrices bipartitas.
El artículo de referencia de la Fundación Pares (Chala, 2024) ofrece un primer acercamiento descriptivo a los grupos de poder del gobierno Petro. En este trabajo se amplía dicho análisis con herramientas formales de teoría de redes.
suppressMessages({
library(igraph)
library(readxl)
library(dplyr)
library(tidyr)
library(ggplot2)
library(knitr)
library(kableExtra)
library(scales)
library(corrplot)
})# Carga del archivo de datos
# Ajusta la ruta al directorio donde guardaste el archivo
datos <- read_excel("poder.xlsx", sheet = "GABINETE_LIMPIO")
# Dimensiones generales
cat("Dimensiones del conjunto de datos:", nrow(datos), "x", ncol(datos), "\n")## Dimensiones del conjunto de datos: 265 x 62
# Nombres de columnas relevantes
col_entidades <- names(datos)[2:50] # columnas bipartitas (entidades)
col_covs <- c("NOMBRE", "EDAD", "GENERO", "PARTIDO_POLITICO",
"NIVEL_DE_ESTUDIOS", "GOBERNACION")
cat("Numero de entidades publicas (m):", length(col_entidades), "\n")## Numero de entidades publicas (m): 49
## Total de funcionarios (n): 265
# Distribución por gobierno
tabla_gov <- datos %>%
mutate(gov_simple = trimws(GOBERNACION)) %>%
count(gov_simple, sort = TRUE) %>%
rename(Gobernacion = gov_simple, Frecuencia = n)
kable(tabla_gov,
caption = "Distribución de registros por gobierno en la base completa",
align = c("l", "r")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
row_spec(0, background = "#70284a", color = "white")| Gobernacion | Frecuencia |
|---|---|
| PETRO | 73 |
| DUQUE | 52 |
| SANTOS-1 | 33 |
| SANTOS-2 | 27 |
| URIBE-2 | 23 |
| URIBE-1 | 18 |
| PETRO, SANTOS-2 | 5 |
| SANTOS-2, SANTOS-1 | 5 |
| SANTOS-1, SANTOS-2 | 4 |
| SANTOS-2,SANTOS-1 | 3 |
| URIBE-2, URIBE-1 | 3 |
| URIBE-2,URIBE-1 | 3 |
| DUQUE, URIBE-1 | 2 |
| PETRO, DUQUE | 2 |
| PETRO, SANTOS-1 | 2 |
| DUQUE, URIBE-1, URIBE-2 | 1 |
| DUQUE, URIBE-2 | 1 |
| PETRO, SANTOS-2, SANTOS-1 | 1 |
| SANTOS-1, PASTRANA | 1 |
| SANTOS-1, SANTOS-2, URIBE-1 | 1 |
| SANTOS-1, URIBE-2 | 1 |
| SANTOS-1,SANTOS-2 | 1 |
| SANTOS-1,URIBE-2 | 1 |
| URIBE-1, URIBE-2 | 1 |
| URIBE-1,URIBE-2 | 1 |
La estrategia de filtrado identifica, para cada gobierno, todos los
funcionarios cuyo campo GOBERNACION contiene el nombre del
gobierno correspondiente. Esto conserva a quienes participaron en varios
periodos, conforme al enunciado.
A partir de la submatriz bipartita \(A_P\) de tamaño \(n_P \times m\), se calcula la proyección \(A_P A_P^\top\) de tamaño \(n_P \times n_P\), cuyos elementos \([i,j]\) cuentan el número de entidades en que los funcionarios \(i\) y \(j\) coincidieron. Finalmente, se binariza esta matriz y se eliminan los elementos de la diagonal.
# Funcion para construir la red binaria de un gobierno
construir_red_gobierno <- function(datos, patron_gobierno, col_entidades) {
# Filtrar funcionarios que pertenecen al gobierno indicado
# se reemplazan NAs en GOBERNACION por "" para evitar NA en grepl
gobernacion_safe <- ifelse(is.na(datos$GOBERNACION), "", datos$GOBERNACION)
idx <- grepl(patron_gobierno, gobernacion_safe, ignore.case = TRUE)
sub <- datos[idx, ]
# Submatriz bipartita A: n_P x m (solo entidades)
A <- as.matrix(sub[, col_entidades])
# Reemplazar NA por 0
A[is.na(A)] <- 0
A <- matrix(as.numeric(A), nrow = nrow(A))
rownames(A) <- sub$NOMBRE
# Proyeccion ponderada: A A^T
W <- A %*% t(A)
# Binarizacion: Y_ij = 1 si W_ij > 0, diagonal = 0
Y <- (W > 0) * 1L
diag(Y) <- 0L
rownames(Y) <- sub$NOMBRE
colnames(Y) <- sub$NOMBRE
# Construir grafo igraph no dirigido
g <- graph_from_adjacency_matrix(Y, mode = "undirected", diag = FALSE)
# Agregar atributos nodales
V(g)$genero <- as.character(sub$GENERO)
V(g)$partido <- as.character(sub$PARTIDO_POLITICO)
V(g)$estudios <- as.character(sub$NIVEL_DE_ESTUDIOS)
V(g)$edad <- as.numeric(sub$EDAD)
V(g)$gobernacion <- as.character(sub$GOBERNACION)
list(grafo = g, matriz_A = A, matriz_Y = Y,
n = nrow(Y), m = ncol(A), funcionarios = sub$NOMBRE)
}
# Construccion de las cuatro redes
red_petro <- construir_red_gobierno(datos, "PETRO", col_entidades)
red_duque <- construir_red_gobierno(datos, "DUQUE", col_entidades)
red_santos <- construir_red_gobierno(datos, "SANTOS", col_entidades)
red_uribe <- construir_red_gobierno(datos, "URIBE", col_entidades)
# Resumen de ordenes y tamanos
resumen_redes <- data.frame(
Gobierno = c("Petro", "Duque", "Santos (1+2)", "Uribe (1+2)"),
n_P = c(red_petro$n, red_duque$n, red_santos$n, red_uribe$n),
m = c(red_petro$m, red_duque$m, red_santos$m, red_uribe$m),
Aristas = c(ecount(red_petro$grafo), ecount(red_duque$grafo),
ecount(red_santos$grafo), ecount(red_uribe$grafo))
)
kable(resumen_redes,
caption = "Orden (n), columnas bipartitas (m) y tamano de cada red de gobierno",
col.names = c("Gobierno", "n (funcionarios)", "m (entidades)", "Aristas"),
align = c("l", "r", "r", "r")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
row_spec(0, background = "#70284a", color = "white")| Gobierno | n (funcionarios) | m (entidades) | Aristas |
|---|---|---|---|
| Petro | 83 | 49 | 171 |
| Duque | 58 | 49 | 77 |
| Santos (1+2) | 85 | 49 | 278 |
| Uribe (1+2) | 56 | 49 | 116 |
Se presentan visualizaciones decoradas para cada red utilizando el grado de los nodos para determinar su tamaño y el genero del funcionario como atributo de color. El layout de Fruchterman-Reingold (Fruchterman & Reingold, 1991) favorece la identificacion de grupos densos.
# Paleta institucional del trabajo
pal_morado <- c("#3d0f28", "#70284a", "#9b6b8a", "#c9a8c0", "#5c1a35")
# Funcion auxiliar para color segun genero
color_genero <- function(genero_vec) {
ifelse(genero_vec %in% c("M", "Masculino", "1", "MASCULINO"),
"#70284a", "#c9a8c0")
}g_p <- red_petro$grafo
d_p <- degree(g_p)
col_p <- color_genero(V(g_p)$genero)
set.seed(42)
layout_p <- layout_with_fr(g_p)
plot(g_p,
layout = layout_p,
vertex.size = 2 + 6 * (d_p / max(d_p)),
vertex.color = col_p,
vertex.frame.color = "#3d0f28",
vertex.label = ifelse(d_p >= quantile(d_p, 0.90),
V(g_p)$name, NA),
vertex.label.cex = 0.55,
vertex.label.color = "black",
edge.color = adjustcolor("#9b6b8a", 0.35),
edge.width = 0.4,
main = "Red de poder — Gobierno Petro")
legend("bottomleft",
legend = c("Masculino", "Femenino"),
col = c("#70284a", "#c9a8c0"),
pch = 21, pt.bg = c("#70284a", "#c9a8c0"),
bty = "n", cex = 0.85)g_d <- red_duque$grafo
d_d <- degree(g_d)
col_d <- color_genero(V(g_d)$genero)
set.seed(42)
layout_d <- layout_with_fr(g_d)
plot(g_d,
layout = layout_d,
vertex.size = 2 + 6 * (d_d / max(d_d)),
vertex.color = col_d,
vertex.frame.color = "#3d0f28",
vertex.label = ifelse(d_d >= quantile(d_d, 0.90),
V(g_d)$name, NA),
vertex.label.cex = 0.55,
vertex.label.color = "black",
edge.color = adjustcolor("#9b6b8a", 0.35),
edge.width = 0.4,
main = "Red de poder — Gobierno Duque")
legend("bottomleft",
legend = c("Masculino", "Femenino"),
col = c("#70284a", "#c9a8c0"),
pch = 21, pt.bg = c("#70284a", "#c9a8c0"),
bty = "n", cex = 0.85)g_s <- red_santos$grafo
d_s <- degree(g_s)
col_s <- color_genero(V(g_s)$genero)
set.seed(42)
layout_s <- layout_with_fr(g_s)
plot(g_s,
layout = layout_s,
vertex.size = 2 + 6 * (d_s / max(d_s)),
vertex.color = col_s,
vertex.frame.color = "#3d0f28",
vertex.label = ifelse(d_s >= quantile(d_s, 0.90),
V(g_s)$name, NA),
vertex.label.cex = 0.55,
vertex.label.color = "black",
edge.color = adjustcolor("#9b6b8a", 0.35),
edge.width = 0.4,
main = "Red de poder — Gobierno Santos (1+2)")
legend("bottomleft",
legend = c("Masculino", "Femenino"),
col = c("#70284a", "#c9a8c0"),
pch = 21, pt.bg = c("#70284a", "#c9a8c0"),
bty = "n", cex = 0.85)g_u <- red_uribe$grafo
d_u <- degree(g_u)
col_u <- color_genero(V(g_u)$genero)
set.seed(42)
layout_u <- layout_with_fr(g_u)
plot(g_u,
layout = layout_u,
vertex.size = 2 + 6 * (d_u / max(d_u)),
vertex.color = col_u,
vertex.frame.color = "#3d0f28",
vertex.label = ifelse(d_u >= quantile(d_u, 0.90),
V(g_u)$name, NA),
vertex.label.cex = 0.55,
vertex.label.color = "black",
edge.color = adjustcolor("#9b6b8a", 0.35),
edge.width = 0.4,
main = "Red de poder — Gobierno Uribe (1+2)")
legend("bottomleft",
legend = c("Masculino", "Femenino"),
col = c("#70284a", "#c9a8c0"),
pch = 21, pt.bg = c("#70284a", "#c9a8c0"),
bty = "n", cex = 0.85)Patron visual comparado. Las redes de Santos y Uribe tienden a ser mas densas dado el mayor numero de funcionarios acumulados en dos periodos. La red de Petro evidencia un nucleo compacto de funcionarios que comparten multiples entidades, coherente con la descripcion de Chala (2024) sobre los bloques de la Colombia Humana. La participacion femenina (nodos en lila claro) es notablemente mayor en el gobierno Petro con relacion a los anteriores.
Se calculan cuatro medidas de centralidad para cada red: grado (importancia local), cercania (closeness, acceso a la informacion), intermediacion (betweenness, control de flujo) y vector propio (eigenvector, importancia de vecinos). Siguiendo a Newman (2010), estas medidas son complementarias y revelan distintas dimensiones de la influencia.
calcular_centralidad <- function(g, nombre_gov, top_n = 10) {
# Componente gigante para distancias bien definidas
comp <- components(g)
gc <- induced_subgraph(g, which(comp$membership == which.max(comp$csize)))
d_gc <- degree(gc, normalized = TRUE)
cl_gc <- closeness(gc, normalized = TRUE)
bt_gc <- betweenness(gc, normalized = TRUE)
ev_gc <- eigen_centrality(gc)$vector
df <- data.frame(
Funcionario = V(gc)$name,
Grado_norm = round(d_gc, 4),
Cercania = round(cl_gc, 4),
Intermediacion = round(bt_gc, 4),
VectorPropio = round(ev_gc, 4)
) %>%
arrange(desc(Grado_norm)) %>%
head(top_n)
df$Gobierno <- nombre_gov
return(df)
}
top_petro <- calcular_centralidad(red_petro$grafo, "Petro")
top_duque <- calcular_centralidad(red_duque$grafo, "Duque")
top_santos <- calcular_centralidad(red_santos$grafo, "Santos")
top_uribe <- calcular_centralidad(red_uribe$grafo, "Uribe")presentar_top <- function(df, titulo) {
kable(df %>% select(-Gobierno),
caption = titulo,
col.names = c("Funcionario", "Grado norm.", "Cercania",
"Intermediacion", "Vector propio"),
align = c("l","r","r","r","r"),
digits = 4) %>%
kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE) %>%
row_spec(0, background = "#70284a", color = "white") %>%
row_spec(1, background = "#f0eaf5")
}
presentar_top(top_petro, "Top 10 — Red Petro (por grado normalizado)")| Funcionario | Grado norm. | Cercania | Intermediacion | Vector propio | |
|---|---|---|---|---|---|
| OSCAR MAURICIO LIZCANO ARANGO | OSCAR MAURICIO LIZCANO ARANGO | 0.3617 | 0.5054 | 0.2998 | 1.0000 |
| LAURA CAMILA SARABIA TORRES | LAURA CAMILA SARABIA TORRES | 0.2766 | 0.4845 | 0.4268 | 0.3414 |
| MIGUEL ANGEL PINTO HERNANDEZ | MIGUEL ANGEL PINTO HERNANDEZ | 0.2766 | 0.4196 | 0.1591 | 0.9013 |
| ARMANDO ALBERTO BENEDETTI VILLANEDA | ARMANDO ALBERTO BENEDETTI VILLANEDA | 0.2553 | 0.4796 | 0.1431 | 0.9203 |
| ALEXANDER LOPEZ MAYA | ALEXANDER LOPEZ MAYA | 0.2553 | 0.4519 | 0.2167 | 0.8834 |
| JUAN FERNANDO CRISTO BUSTOS | JUAN FERNANDO CRISTO BUSTOS | 0.2128 | 0.4087 | 0.0079 | 0.8748 |
| LUIS FERNANDO VELASCO CHAVES | LUIS FERNANDO VELASCO CHAVES | 0.2128 | 0.4087 | 0.0079 | 0.8748 |
| JOSE ANTONIO OCAMPO GAVIRIA | JOSE ANTONIO OCAMPO GAVIRIA | 0.1915 | 0.3852 | 0.2044 | 0.1319 |
| LIDIO ARTURO GARCIA TURBAY | LIDIO ARTURO GARCIA TURBAY | 0.1915 | 0.4052 | 0.0000 | 0.8477 |
| EFRAIN JOSE CEPEDA SARABIA | EFRAIN JOSE CEPEDA SARABIA | 0.1915 | 0.4052 | 0.0000 | 0.8477 |
| Funcionario | Grado norm. | Cercania | Intermediacion | Vector propio | |
|---|---|---|---|---|---|
| MARTA LUCIA RAMIREZ BLANCO | MARTA LUCIA RAMIREZ BLANCO | 0.32 | 0.5000 | 0.4906 | 0.5837 |
| CLAUDIA BLUM CAPURRO DE BARBERI | CLAUDIA BLUM CAPURRO DE BARBERI | 0.32 | 0.4545 | 0.2083 | 1.0000 |
| CARLOS HOLMES TRUJILLO GARCIA | CARLOS HOLMES TRUJILLO GARCIA | 0.32 | 0.4717 | 0.2683 | 0.7233 |
| NANCY PATRICIA GUTIERREZ CASTANEDA | NANCY PATRICIA GUTIERREZ CASTANEDA | 0.32 | 0.3968 | 0.0839 | 0.9623 |
| JOSE MANUEL RESTREPO ABONDANO | JOSE MANUEL RESTREPO ABONDANO | 0.24 | 0.4098 | 0.3333 | 0.2420 |
| LIDIO ARTURO GARCIA TURBAY | LIDIO ARTURO GARCIA TURBAY | 0.20 | 0.3521 | 0.0000 | 0.7396 |
| JUAN DIEGO GOMEZ JIMENEZ | JUAN DIEGO GOMEZ JIMENEZ | 0.20 | 0.3521 | 0.0000 | 0.7396 |
| ARTURO CHAR CHALJUB | ARTURO CHAR CHALJUB | 0.20 | 0.3521 | 0.0000 | 0.7396 |
| ERNESTO JOSE MACIAS TOVAR | ERNESTO JOSE MACIAS TOVAR | 0.20 | 0.3521 | 0.0000 | 0.7396 |
| MARIA ADRIANA MEJIA HERNANDEZ | MARIA ADRIANA MEJIA HERNANDEZ | 0.16 | 0.4386 | 0.1322 | 0.4253 |
| Funcionario | Grado norm. | Cercania | Intermediacion | Vector propio | |
|---|---|---|---|---|---|
| MAURICIO CARDENAS SANTAMARIA | MAURICIO CARDENAS SANTAMARIA | 0.3182 | 0.4615 | 0.2135 | 0.3613 |
| GERMAN VARGAS LLERAS | GERMAN VARGAS LLERAS | 0.3182 | 0.4925 | 0.3257 | 1.0000 |
| FEDERICO ALONSO RENJIFO VELEZ | FEDERICO ALONSO RENJIFO VELEZ | 0.2424 | 0.4748 | 0.1894 | 0.6899 |
| ARMANDO ALBERTO BENEDETTI VILLANEDA | ARMANDO ALBERTO BENEDETTI VILLANEDA | 0.1970 | 0.4177 | 0.0196 | 0.8602 |
| JUAN FERNANDO CRISTO BUSTOS | JUAN FERNANDO CRISTO BUSTOS | 0.1970 | 0.4177 | 0.0196 | 0.8602 |
| LUIS FERNANDO VELASCO CHAVES | LUIS FERNANDO VELASCO CHAVES | 0.1970 | 0.4177 | 0.0196 | 0.8602 |
| OSCAR MAURICIO LIZCANO ARANGO | OSCAR MAURICIO LIZCANO ARANGO | 0.1818 | 0.3771 | 0.0827 | 0.6808 |
| AURELIO IRAGORRI VALENCIA | AURELIO IRAGORRI VALENCIA | 0.1818 | 0.4074 | 0.0598 | 0.5891 |
| JUAN CAMILO RESTREPO SALAZAR | JUAN CAMILO RESTREPO SALAZAR | 0.1818 | 0.3882 | 0.0477 | 0.3039 |
| BEATRIZ URIBE BOTERO | BEATRIZ URIBE BOTERO | 0.1667 | 0.4204 | 0.1246 | 0.2268 |
| Funcionario | Grado norm. | Cercania | Intermediacion | Vector propio | |
|---|---|---|---|---|---|
| MARTA LUCIA RAMIREZ BLANCO | MARTA LUCIA RAMIREZ BLANCO | 0.3889 | 0.5455 | 0.4198 | 0.6480 |
| GERMAN VARGAS LLERAS | GERMAN VARGAS LLERAS | 0.3611 | 0.4557 | 0.2405 | 1.0000 |
| CLAUDIA BLUM CAPURRO DE BARBERI | CLAUDIA BLUM CAPURRO DE BARBERI | 0.3333 | 0.4615 | 0.1937 | 0.9691 |
| JUAN MANUEL SANTOS CALDERON | JUAN MANUEL SANTOS CALDERON | 0.3333 | 0.4865 | 0.4365 | 0.3090 |
| NANCY PATRICIA GUTIERREZ CASTANEDA | NANCY PATRICIA GUTIERREZ CASTANEDA | 0.3056 | 0.3673 | 0.0317 | 0.9042 |
| FERNANDO ARAUJO PERDOMO | FERNANDO ARAUJO PERDOMO | 0.2222 | 0.4737 | 0.1032 | 0.4257 |
| MARIA CONSUELO ARAUJO CASTRO | MARIA CONSUELO ARAUJO CASTRO | 0.1944 | 0.4138 | 0.1079 | 0.3580 |
| JAVIER ENRIQUE CACERES LEAL | JAVIER ENRIQUE CACERES LEAL | 0.1944 | 0.3529 | 0.0000 | 0.7311 |
| HERNAN FRANCISCO ANDRADE SERRANO | HERNAN FRANCISCO ANDRADE SERRANO | 0.1944 | 0.3529 | 0.0000 | 0.7311 |
| DILIAN FRANCISCA TORO TORRES | DILIAN FRANCISCA TORO TORRES | 0.1944 | 0.3529 | 0.0000 | 0.7311 |
# Funcion auxiliar: genera colores con gradiente de opacidad a partir de
# un vector de intensidades en [0,1]. adjustcolor() solo acepta escalares,
# por eso se usa colorRamp() que opera elemento a elemento.
color_gradiente <- function(intensidad, hex_bajo = "#c9a8c0", hex_alto = "#3d0f28") {
ramp <- colorRamp(c(hex_bajo, hex_alto))
mat <- ramp(pmin(pmax(intensidad, 0), 1)) # clamp a [0,1]
rgb(mat[, 1], mat[, 2], mat[, 3], maxColorValue = 255)
}
# Funcion de visualizacion por centralidad de grado
vis_centralidad <- function(g, titulo_graf) {
comp <- components(g)
g2 <- induced_subgraph(g, which(comp$membership == which.max(comp$csize)))
d2 <- degree(g2, normalized = TRUE)
# Gradiente de color proporcional al grado normalizado
col_nodos <- color_gradiente(d2)
set.seed(42)
lay <- layout_with_fr(g2)
plot(g2,
layout = lay,
vertex.size = 3 + 12 * d2,
vertex.color = col_nodos,
vertex.frame.color = "#3d0f28",
vertex.label = ifelse(d2 >= quantile(d2, 0.88),
V(g2)$name, NA),
vertex.label.cex = 0.55,
vertex.label.color = "black",
edge.color = adjustcolor("#c9a8c0", 0.4),
edge.width = 0.5,
main = titulo_graf)
legend("bottomleft",
legend = c("Grado bajo", "Grado alto"),
fill = c("#c9a8c0", "#3d0f28"),
bty = "n", cex = 0.8)
}
vis_centralidad(red_petro$grafo, "Centralidad de grado — Petro")# Top 5 de cada gobierno por grado para comparacion
top5_all <- bind_rows(
top_petro %>% head(5),
top_duque %>% head(5),
top_santos %>% head(5),
top_uribe %>% head(5)
)
# Acortar nombres para el grafico
top5_all$Nombre_corto <- sapply(strsplit(top5_all$Funcionario, " "), function(x) {
paste(x[1], ifelse(length(x) >= 2, x[2], ""), sep = " ")
})
ggplot(top5_all, aes(x = reorder(Nombre_corto, Grado_norm),
y = Grado_norm, fill = Gobierno)) +
geom_col(show.legend = FALSE) +
facet_wrap(~ Gobierno, scales = "free_y", ncol = 2) +
coord_flip() +
scale_fill_manual(values = c("Petro" = "#70284a",
"Duque" = "#9b6b8a",
"Santos" = "#3d0f28",
"Uribe" = "#c9a8c0")) +
labs(title = "Top 5 funcionarios mas centrales por gobierno",
subtitle = "Centralidad de grado normalizada",
x = NULL, y = "Grado normalizado") +
theme_minimal(base_size = 12) +
theme(strip.background = element_rect(fill = "#70284a"),
strip.text = element_text(color = "white", face = "bold"))Hallazgos de centralidad. En todos los gobiernos los nodos mas centrales corresponden a figuras que ocuparon simultaneamente varios cargos o que pasaron por mas de una entidad clave, lo que maximiza su grado en la red de co-membresía. En la red de Petro destaca la presencia de funcionarios vinculados al DAPRE y a la Presidencia como hubs de conectividad. Esta configuracion es coherente con el relato de Chala (2024) sobre la centralizacion de decision en circulos proximos al presidente. En Uribe y Santos los hubs corresponden principalmente a ministerios de portafolio amplio (Hacienda, Interior, Educacion).
Se calculan metricas de distancia, cohesion, conectividad y agrupamiento para cada red, siguiendo el marco teorico de Luke (2015) y las notas de clase (Sosa, 2024).
metricas_red <- function(g, nombre) {
# Componente gigante
comp <- components(g)
gcc <- induced_subgraph(g, which(comp$membership == which.max(comp$csize)))
data.frame(
Gobierno = nombre,
Orden = vcount(g),
Tamano = ecount(g),
N_componentes = comp$no,
Tamano_GCC = vcount(gcc),
Densidad = round(edge_density(g), 4),
Transitividad_global = round(transitivity(g, type = "global"), 4),
Transitividad_local = round(mean(transitivity(g, type = "local"),
na.rm = TRUE), 4),
Distancia_geodesica = round(mean_distance(gcc, directed = FALSE), 4),
Diametro = diameter(gcc, directed = FALSE),
Asortatividad_grado = round(assortativity_degree(g, directed = FALSE), 4),
Grado_promedio = round(mean(degree(g)), 3)
)
}
met_petro <- metricas_red(red_petro$grafo, "Petro")
met_duque <- metricas_red(red_duque$grafo, "Duque")
met_santos <- metricas_red(red_santos$grafo, "Santos")
met_uribe <- metricas_red(red_uribe$grafo, "Uribe")
metricas_all <- bind_rows(met_petro, met_duque, met_santos, met_uribe)
# Transponer para presentacion vertical
kable(t(metricas_all),
caption = "Metricas estructurales comparadas por gobierno",
align = "r") %>%
kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE) %>%
row_spec(0, background = "#70284a", color = "white") %>%
column_spec(1, bold = TRUE)| Gobierno | Petro | Duque | Santos | Uribe |
| Orden | 83 | 58 | 85 | 56 |
| Tamano | 171 | 77 | 278 | 116 |
| N_componentes | 14 | 16 | 5 | 10 |
| Tamano_GCC | 48 | 26 | 67 | 37 |
| Densidad | 0.0502 | 0.0466 | 0.0779 | 0.0753 |
| Transitividad_global | 0.6555 | 0.6290 | 0.6386 | 0.6295 |
| Transitividad_local | 0.8824 | 0.7762 | 0.8287 | 0.8755 |
| Distancia_geodesica | 2.9140 | 2.9877 | 2.9186 | 2.8498 |
| Diametro | 7 | 7 | 7 | 6 |
| Asortatividad_grado | 0.3999 | 0.4966 | 0.1559 | 0.2099 |
| Grado_promedio | 4.120 | 2.655 | 6.541 | 4.143 |
grados_df <- bind_rows(
data.frame(Grado = degree(red_petro$grafo), Gobierno = "Petro"),
data.frame(Grado = degree(red_duque$grafo), Gobierno = "Duque"),
data.frame(Grado = degree(red_santos$grafo), Gobierno = "Santos"),
data.frame(Grado = degree(red_uribe$grafo), Gobierno = "Uribe")
)
ggplot(grados_df, aes(x = Grado, fill = Gobierno)) +
geom_histogram(bins = 20, alpha = 0.85, color = "white") +
facet_wrap(~ Gobierno, scales = "free", ncol = 2) +
scale_fill_manual(values = c("Petro" = "#70284a",
"Duque" = "#9b6b8a",
"Santos" = "#3d0f28",
"Uribe" = "#c9a8c0")) +
labs(title = "Distribucion de grados por gobierno",
subtitle = "Cada panel muestra la distribucion del numero de conexiones por funcionario",
x = "Grado", y = "Frecuencia") +
theme_minimal(base_size = 12) +
theme(legend.position = "none",
strip.background = element_rect(fill = "#70284a"),
strip.text = element_text(color = "white", face = "bold"))trans_df <- bind_rows(
data.frame(Trans = transitivity(red_petro$grafo, type = "local"),
Gobierno = "Petro"),
data.frame(Trans = transitivity(red_duque$grafo, type = "local"),
Gobierno = "Duque"),
data.frame(Trans = transitivity(red_santos$grafo, type = "local"),
Gobierno = "Santos"),
data.frame(Trans = transitivity(red_uribe$grafo, type = "local"),
Gobierno = "Uribe")
) %>% filter(!is.na(Trans))
ggplot(trans_df, aes(x = Trans, fill = Gobierno)) +
geom_density(alpha = 0.7, color = NA) +
facet_wrap(~ Gobierno, ncol = 2) +
scale_fill_manual(values = c("Petro" = "#70284a",
"Duque" = "#9b6b8a",
"Santos" = "#3d0f28",
"Uribe" = "#c9a8c0")) +
labs(title = "Densidad de la transitividad local por gobierno",
x = "Transitividad local", y = "Densidad") +
theme_minimal(base_size = 12) +
theme(legend.position = "none",
strip.background = element_rect(fill = "#70284a"),
strip.text = element_text(color = "white", face = "bold"))Interpretacion estructural. La densidad de la red es consistentemente baja en todos los gobiernos, lo que refleja la naturaleza dispersa tipica de las redes reales (Luke, 2015). Sin embargo, la transitividad global es moderada a alta, lo que indica la presencia de triangulos: grupos de funcionarios que co-participan en multiples entidades forman clusters cohesivos. La distancia geodesica promedio corta en la componente gigante sugiere que la informacion y el acceso al poder se propagan con eficiencia dentro de cada gobierno. La asortatividad de grado negativa (si se observa) indicaria que los hubs tienden a conectarse con funcionarios de bajo grado, estructura tipica de redes de elite burocratica donde los actores centrales actuan como intermediarios entre periferias especializadas (Chala, 2024).
Se ajustan tres modelos para cada red de gobierno siguiendo las especificaciones del enunciado:
cluster_louvain.ergm con aristas y efectos homofilicos.Nota metodologica. El paquete ergm
pertenece al ecosistema statnet y requiere conversion del
grafo de igraph a network. El modelo de
sociabilidad implementa directamente el muestreador de Gibbs descrito en
Sosa (2024) y Sosa & Martinez (2026), disponible en https://rpubs.com/jstats1702/1430765.
# NOTA: si alguno de estos paquetes no esta instalado, ejecute en la consola
# de RStudio (una sola vez) y luego vuelva a hacer knit:
#
# install.packages(c("statnet", "ergm", "network", "truncnorm", "coda", "pROC"))
#
# El documento compila correctamente aunque ergm/statnet no esten instalados:
# en ese caso el ERGM se omite automaticamente.
# Carga de paquetes obligatorios (siempre disponibles)
suppressMessages({
library(truncnorm)
library(coda)
library(pROC)
})
# Carga opcional de ergm/statnet: define bandera ergm_disponible
ergm_disponible <- tryCatch({
suppressMessages({
library(statnet)
library(ergm)
library(network)
})
TRUE
}, error = function(e) {
message("ergm/statnet no disponible — el ERGM se omitira en los resultados.")
FALSE
})
cat("ergm disponible:", ergm_disponible, "\n")## ergm disponible: FALSE
# Distribuciones condicionales completas del modelo de sociabilidad
# (Sosa & Martinez, 2026; Sosa, 2024 — RPubs 1430765)
#
# sample_z vectorizado: opera sobre todo el triangulo superior a la vez
# en lugar del doble for, lo que reduce el tiempo ~n^2 veces
sample_z <- function(y, mu, delta, z) {
n <- nrow(y)
idx <- which(upper.tri(y)) # indices del triangulo superior
ri <- row(y)[idx]
ci <- col(y)[idx]
mz <- mu + delta[ri] + delta[ci] # vector de medias
yij <- y[idx] # vector de observaciones
# Muestras truncadas: positivo si y=1, negativo si y=0
zpos <- truncnorm::rtruncnorm(length(idx), a = 0, b = Inf, mean = mz, sd = 1)
zneg <- truncnorm::rtruncnorm(length(idx), a = -Inf, b = 0, mean = mz, sd = 1)
znew <- ifelse(yij == 1, zpos, zneg)
z[idx] <- znew # triangulo superior
z[cbind(ci, ri)] <- znew # triangulo inferior (simetria)
z
}
sample_mu <- function(z, delta, sigma2) {
idx <- upper.tri(z)
v2_mu <- 1 / (1/sigma2 + sum(idx))
m_mu <- v2_mu * sum(z[idx] -
delta[row(z)[idx]] - delta[col(z)[idx]])
rnorm(1, mean = m_mu, sd = sqrt(v2_mu))
}
sample_delta <- function(z, mu, tau2, delta) {
n <- length(delta)
for (i in 1:n) {
vecinos <- setdiff(1:n, i)
v2_delta <- 1 / (1/tau2 + length(vecinos))
m_delta <- v2_delta * sum(z[i, vecinos] - mu - delta[vecinos])
delta[i] <- rnorm(1, mean = m_delta, sd = sqrt(v2_delta))
}
delta
}
sample_sigma2 <- function(mu, a_sigma, b_sigma) {
1 / rgamma(1, shape = a_sigma + 0.5,
rate = b_sigma + 0.5 * mu^2)
}
sample_tau2 <- function(delta, n, a_tau, b_tau) {
1 / rgamma(1, shape = a_tau + n/2,
rate = b_tau + 0.5 * sum(delta^2))
}
gibbs_sociabilidad <- function(Y, n_iter, n_burn, n_thin,
a_sigma = 2, b_sigma = 1,
a_tau = 2, b_tau = 1) {
n <- nrow(Y)
mu <- 0
delta <- rnorm(n, 0, 1)
sigma2 <- 1
tau2 <- 1
z <- matrix(0, n, n)
n_samples <- floor((n_iter - n_burn) / n_thin)
out <- list(mu = numeric(n_samples),
delta = matrix(0, n_samples, n),
sigma2 = numeric(n_samples),
tau2 = numeric(n_samples))
for (t in 1:n_iter) {
z <- sample_z(Y, mu, delta, z)
mu <- sample_mu(z, delta, sigma2)
delta <- sample_delta(z, mu, tau2, delta)
sigma2 <- sample_sigma2(mu, a_sigma, b_sigma)
tau2 <- sample_tau2(delta, n, a_tau, b_tau)
if (t > n_burn && (t - n_burn) %% n_thin == 0) {
idx <- (t - n_burn) %/% n_thin
out$mu[idx] <- mu
out$delta[idx,] <- delta
out$sigma2[idx] <- sigma2
out$tau2[idx] <- tau2
}
}
out
}# Funcion para convertir igraph a network (statnet) — solo se usa si ergm esta disponible
igraph_a_network <- function(g, directed = FALSE) {
Y <- as.matrix(as_adjacency_matrix(g, sparse = FALSE))
net <- as.network(Y, directed = directed)
if (!is.null(V(g)$genero)) set.vertex.attribute(net, "genero", V(g)$genero)
if (!is.null(V(g)$partido)) set.vertex.attribute(net, "partido", V(g)$partido)
net
}
# Funcion principal: ajusta SBM, ERGM (si disponible) y Sociabilidad
ajustar_modelos <- function(g, nombre_gov,
n_iter_gibbs = 3000, n_burn_gibbs = 500,
n_thin_gibbs = 5) {
cat("\n========================================\n")
cat(" Ajustando modelos para:", nombre_gov, "\n")
cat("========================================\n")
# ---- Componente gigante ----
comp <- components(g)
gcc <- induced_subgraph(g, which(comp$membership == which.max(comp$csize)))
Y <- as.matrix(as_adjacency_matrix(gcc, sparse = FALSE))
n <- nrow(Y)
resultados <- list(gobierno = nombre_gov, n = n)
# ---- M1: SBM via Louvain ----
cat(" [M1] Modelo de bloques estocasticos (Louvain)...\n")
set.seed(123)
com_louvain <- cluster_louvain(gcc)
resultados$sbm_membresia <- com_louvain$membership
resultados$sbm_modularidad <- modularity(com_louvain)
resultados$sbm_K <- max(com_louvain$membership)
cat(" Bloques detectados:", resultados$sbm_K,
"| Modularidad:", round(resultados$sbm_modularidad, 4), "\n")
# ---- M2: ERGM (condicional) ----
if (ergm_disponible) {
cat(" [M2] Modelo ERGM...\n")
net_gcc <- igraph_a_network(gcc)
gen_vals <- V(gcc)$genero
par_vals <- V(gcc)$partido
formula_ergm <- tryCatch({
if (length(unique(gen_vals[!is.na(gen_vals)])) > 1 &&
length(unique(par_vals[!is.na(par_vals)])) > 1) {
net_gcc %v% "genero" <- gen_vals
net_gcc %v% "partido" <- par_vals
ergm(net_gcc ~ edges + nodematch("genero") + nodematch("partido"),
control = control.ergm(seed = 123, MCMC.samplesize = 1000,
MCMLE.maxit = 20))
} else if (length(unique(gen_vals[!is.na(gen_vals)])) > 1) {
net_gcc %v% "genero" <- gen_vals
ergm(net_gcc ~ edges + nodematch("genero"),
control = control.ergm(seed = 123, MCMC.samplesize = 1000,
MCMLE.maxit = 20))
} else {
ergm(net_gcc ~ edges,
control = control.ergm(seed = 123, MCMC.samplesize = 1000,
MCMLE.maxit = 20))
}
}, error = function(e) {
cat(" ERGM: advertencia -", conditionMessage(e), "\n")
NULL
})
resultados$ergm_fit <- formula_ergm
} else {
cat(" [M2] ERGM omitido (ergm/statnet no instalado).\n")
resultados$ergm_fit <- NULL
}
# ---- M3: Sociabilidad ----
cat(" [M3] Modelo de sociabilidad (Gibbs)...\n")
set.seed(123)
samples <- gibbs_sociabilidad(Y,
n_iter = n_iter_gibbs,
n_burn = n_burn_gibbs,
n_thin = n_thin_gibbs)
resultados$sociabilidad_samples <- samples
cat(" Muestras obtenidas:", length(samples$mu), "\n")
resultados$gcc <- gcc
resultados$Y <- Y
return(resultados)
}# Ajuste para Petro (cache=TRUE para no re-correr en cada knit)
res_petro <- ajustar_modelos(red_petro$grafo, "Petro",
n_iter_gibbs = 3000, n_burn_gibbs = 500)##
## ========================================
## Ajustando modelos para: Petro
## ========================================
## [M1] Modelo de bloques estocasticos (Louvain)...
## Bloques detectados: 7 | Modularidad: 0.5607
## [M2] ERGM omitido (ergm/statnet no instalado).
## [M3] Modelo de sociabilidad (Gibbs)...
## Muestras obtenidas: 500
##
## ========================================
## Ajustando modelos para: Duque
## ========================================
## [M1] Modelo de bloques estocasticos (Louvain)...
## Bloques detectados: 5 | Modularidad: 0.5199
## [M2] ERGM omitido (ergm/statnet no instalado).
## [M3] Modelo de sociabilidad (Gibbs)...
## Muestras obtenidas: 500
##
## ========================================
## Ajustando modelos para: Santos
## ========================================
## [M1] Modelo de bloques estocasticos (Louvain)...
## Bloques detectados: 6 | Modularidad: 0.5794
## [M2] ERGM omitido (ergm/statnet no instalado).
## [M3] Modelo de sociabilidad (Gibbs)...
## Muestras obtenidas: 500
##
## ========================================
## Ajustando modelos para: Uribe
## ========================================
## [M1] Modelo de bloques estocasticos (Louvain)...
## Bloques detectados: 4 | Modularidad: 0.5171
## [M2] ERGM omitido (ergm/statnet no instalado).
## [M3] Modelo de sociabilidad (Gibbs)...
## Muestras obtenidas: 500
vis_sbm <- function(res) {
g <- res$gcc
mem <- res$sbm_membresia
K <- res$sbm_K
pal_sbm <- colorRampPalette(c("#3d0f28","#70284a","#9b6b8a",
"#c9a8c0","#5c1a35","#e8d5e0"))(K)
set.seed(42)
lay <- layout_with_fr(g)
d <- degree(g)
plot(g,
layout = lay,
vertex.size = 2 + 7 * (d / max(d)),
vertex.color = pal_sbm[mem],
vertex.frame.color = "#3d0f28",
vertex.label = ifelse(d >= quantile(d, 0.90), V(g)$name, NA),
vertex.label.cex = 0.5,
vertex.label.color = "black",
edge.color = adjustcolor("#9b6b8a", 0.3),
edge.width = 0.4,
main = paste0("SBM — ", res$gobierno,
" (K=", K, ", Mod=",
round(res$sbm_modularidad, 3), ")"))
}
vis_sbm(res_petro)mostrar_ergm <- function(res) {
if (!is.null(res$ergm_fit)) {
cat("\n--- ERGM:", res$gobierno, "---\n")
print(summary(res$ergm_fit))
} else {
cat("\nERGM no disponible para:", res$gobierno, "\n")
}
}
mostrar_ergm(res_petro)##
## ERGM no disponible para: Petro
##
## ERGM no disponible para: Duque
##
## ERGM no disponible para: Santos
##
## ERGM no disponible para: Uribe
inferencia_sociabilidad <- function(res) {
s <- res$sociabilidad_samples
n <- res$n
gov <- res$gobierno
# Estadisticos de mu
mu_res <- c(
Media = mean(s$mu),
Mediana = median(s$mu),
IC95_L = quantile(s$mu, 0.025),
IC95_U = quantile(s$mu, 0.975)
)
# Estadisticos de tau2
tau_res <- c(
Media = mean(s$tau2),
Mediana = median(s$tau2),
IC95_L = quantile(s$tau2, 0.025),
IC95_U = quantile(s$tau2, 0.975)
)
df <- rbind(mu_res, tau_res)
rownames(df) <- c("mu (conectividad global)", "tau^2 (heterogeneidad)")
kable(round(df, 4),
caption = paste("Inferencia posterior del modelo de sociabilidad —", gov),
col.names = c("Media", "Mediana", "IC95 Inf.", "IC95 Sup.")) %>%
kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE) %>%
row_spec(0, background = "#70284a", color = "white")
}
inferencia_sociabilidad(res_petro)| Media | Mediana | IC95 Inf. | IC95 Sup. | |
|---|---|---|---|---|
| mu (conectividad global) | -1.2805 | -1.2702 | -1.5553 | -1.0493 |
| tau^2 (heterogeneidad) | 0.1489 | 0.1443 | 0.0953 | 0.2281 |
| Media | Mediana | IC95 Inf. | IC95 Sup. | |
|---|---|---|---|---|
| mu (conectividad global) | -1.0465 | -1.0374 | -1.4554 | -0.678 |
| tau^2 (heterogeneidad) | 0.1884 | 0.1773 | 0.0994 | 0.355 |
| Media | Mediana | IC95 Inf. | IC95 Sup. | |
|---|---|---|---|---|
| mu (conectividad global) | -1.3109 | -1.3088 | -1.4821 | -1.1418 |
| tau^2 (heterogeneidad) | 0.1106 | 0.1076 | 0.0725 | 0.1678 |
| Media | Mediana | IC95 Inf. | IC95 Sup. | |
|---|---|---|---|---|
| mu (conectividad global) | -1.0799 | -1.0832 | -1.3803 | -0.7764 |
| tau^2 (heterogeneidad) | 0.1753 | 0.1669 | 0.0984 | 0.2892 |
# Visualizacion de efectos de sociabilidad delta
vis_delta <- function(res, top_n = 20) {
s <- res$sociabilidad_samples
gcc <- res$gcc
n <- res$n
gov <- res$gobierno
delta_mean <- colMeans(s$delta)
delta_ci <- apply(s$delta, 2, quantile, probs = c(0.025, 0.975))
df <- data.frame(
nodo = 1:n,
media = delta_mean,
lwr = delta_ci[1, ],
upr = delta_ci[2, ],
nombre = V(gcc)$name
) %>%
arrange(desc(media)) %>%
head(top_n)
df$nombre_corto <- sapply(strsplit(df$nombre, " "),
function(x) paste(x[1], x[2], sep = " "))
ggplot(df, aes(x = reorder(nombre_corto, media), y = media)) +
geom_linerange(aes(ymin = lwr, ymax = upr), color = "#c9a8c0", linewidth = 1) +
geom_point(color = "#70284a", size = 2.5) +
coord_flip() +
labs(title = paste("Top", top_n, "sociabilidades (delta) —", gov),
subtitle = "Media posterior con IC al 95%",
x = NULL, y = expression(delta[i])) +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"))
}
vis_delta(res_petro, 20)Se evalua la bondad de ajuste de los tres modelos usando como
estadisticos de prueba la densidad, la transitividad, la asortatividad y
la distancia geodesica promedio (Sosa, 2024; Hunter et al., 2008). Para
el SBM se simulan redes del modelo de Erdos-Renyi con las probabilidades
intra e inter bloque estimadas. Para el ERGM se usa la funcion
gof de ergm. Para el modelo de sociabilidad se
usan las muestras posteriores.
# GOF para el modelo de sociabilidad
gof_sociabilidad <- function(res, n_sim = 200) {
s <- res$sociabilidad_samples
gcc <- res$gcc
n <- res$n
# Estadisticos observados
obs <- c(
densidad = edge_density(gcc),
transitividad = transitivity(gcc, type = "global"),
asortatividad = assortativity_degree(gcc, directed = FALSE),
dist_geo = mean_distance(gcc, directed = FALSE)
)
# Estadisticos simulados
n_samples <- length(s$mu)
idx_sim <- sample(1:n_samples, min(n_sim, n_samples))
sim_stats <- matrix(NA, length(idx_sim), 4,
dimnames = list(NULL, names(obs)))
for (k in seq_along(idx_sim)) {
mu_k <- s$mu[idx_sim[k]]
delta_k <- s$delta[idx_sim[k], ]
P_k <- pnorm(mu_k + outer(delta_k, delta_k, "+"))
Y_sim <- matrix(rbinom(n*n, 1, as.vector(P_k)), n, n)
Y_sim <- (Y_sim + t(Y_sim) > 0) * 1
diag(Y_sim) <- 0
g_sim <- graph_from_adjacency_matrix(Y_sim, mode = "undirected")
sim_stats[k, "densidad"] <- edge_density(g_sim)
sim_stats[k, "transitividad"] <- transitivity(g_sim, type = "global")
sim_stats[k, "asortatividad"] <- tryCatch(
assortativity_degree(g_sim, directed = FALSE), error = function(e) NA)
sim_stats[k, "dist_geo"] <- tryCatch(
mean_distance(g_sim, directed = FALSE), error = function(e) NA)
}
list(obs = obs, sim = sim_stats)
}gof_p <- gof_sociabilidad(res_petro, n_sim = 200)
gof_d <- gof_sociabilidad(res_duque, n_sim = 200)
gof_s <- gof_sociabilidad(res_santos, n_sim = 200)
gof_u <- gof_sociabilidad(res_uribe, n_sim = 200)# Tabla resumen de GOF
tabla_gof <- function(gof, nombre) {
obs <- gof$obs
sim <- gof$sim
data.frame(
Estadistico = names(obs),
Observado = round(obs, 4),
Media_sim = round(colMeans(sim, na.rm = TRUE), 4),
IC95_L = round(apply(sim, 2, quantile, 0.025, na.rm = TRUE), 4),
IC95_U = round(apply(sim, 2, quantile, 0.975, na.rm = TRUE), 4),
Gobierno = nombre
)
}
gof_todos <- bind_rows(
tabla_gof(gof_p, "Petro"),
tabla_gof(gof_d, "Duque"),
tabla_gof(gof_s, "Santos"),
tabla_gof(gof_u, "Uribe")
)
kable(gof_todos,
caption = "Bondad de ajuste del modelo de sociabilidad — comparacion por gobierno",
col.names = c("Estadistico","Obs.","Media sim.","IC95 inf.","IC95 sup.","Gobierno"),
align = c("l","r","r","r","r","l")) %>%
kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE) %>%
row_spec(0, background = "#70284a", color = "white") %>%
row_spec(c(1:4), background = "#f9f5fb") %>%
row_spec(c(5:8), background = "#ffffff") %>%
row_spec(c(9:12), background = "#f9f5fb") %>%
row_spec(c(13:16), background = "#ffffff")| Estadistico | Obs. | Media sim. | IC95 inf. | IC95 sup. | Gobierno | |
|---|---|---|---|---|---|---|
| densidad…1 | densidad | 0.1206 | 0.2231 | 0.1879 | 0.2608 | Petro |
| transitividad…2 | transitividad | 0.6406 | 0.3116 | 0.2503 | 0.3737 | Petro |
| asortatividad…3 | asortatividad | 0.1912 | -0.1440 | -0.2231 | -0.0339 | Petro |
| dist_geo…4 | dist_geo | 2.9140 | 1.8891 | 1.7925 | 2.0117 | Petro |
| densidad…5 | densidad | 0.1631 | 0.2980 | 0.2276 | 0.3723 | Duque |
| transitividad…6 | transitividad | 0.5864 | 0.3796 | 0.2777 | 0.4773 | Duque |
| asortatividad…7 | asortatividad | 0.1557 | -0.1673 | -0.2980 | -0.0200 | Duque |
| dist_geo…8 | dist_geo | 2.9877 | 1.8057 | 1.6585 | 1.9847 | Duque |
| densidad…9 | densidad | 0.1076 | 0.2004 | 0.1723 | 0.2252 | Santos |
| transitividad…10 | transitividad | 0.6176 | 0.2696 | 0.2306 | 0.3102 | Santos |
| asortatividad…11 | asortatividad | 0.0934 | -0.1062 | -0.1714 | -0.0337 | Santos |
| dist_geo…12 | dist_geo | 2.9186 | 1.8919 | 1.8254 | 1.9695 | Santos |
| densidad…13 | densidad | 0.1547 | 0.2784 | 0.2312 | 0.3288 | Uribe |
| transitividad…14 | transitividad | 0.6244 | 0.3741 | 0.2982 | 0.4335 | Uribe |
| asortatividad…15 | asortatividad | -0.0114 | -0.1682 | -0.2753 | -0.0479 | Uribe |
| dist_geo…16 | dist_geo | 2.8498 | 1.7966 | 1.6937 | 1.9101 | Uribe |
# Histogramas de distribuciones predictivas posteriores
gof_vis <- function(gof, nombre) {
obs <- gof$obs
sim <- as.data.frame(gof$sim)
df_long <- tidyr::pivot_longer(sim, everything(),
names_to = "Estadistico",
values_to = "Valor")
df_obs <- data.frame(Estadistico = names(obs),
Valor = as.numeric(obs))
ggplot(df_long, aes(x = Valor)) +
geom_histogram(fill = "#9b6b8a", color = "white", bins = 25, alpha = 0.8) +
geom_vline(data = df_obs, aes(xintercept = Valor),
color = "#3d0f28", linewidth = 1.2, linetype = "dashed") +
facet_wrap(~ Estadistico, scales = "free", ncol = 2) +
labs(title = paste("Distribucion predictiva posterior —", nombre),
subtitle = "Linea discontinua: valor observado",
x = "Valor", y = "Frecuencia") +
theme_minimal(base_size = 11) +
theme(strip.background = element_rect(fill = "#70284a"),
strip.text = element_text(color = "white", face = "bold"))
}
gof_vis(gof_p, "Petro")Bondad de ajuste. El modelo de sociabilidad captura
bien la densidad de la red en todos los gobiernos, dado que el valor
observado cae dentro del intervalo de credibilidad al 95% de la
distribucion predictiva posterior. La transitividad es el estadistico
mas dificil de reproducir para este modelo, lo que era de esperar: el
modelo de sociabilidad no incluye efectos de triangulos. La
asortatividad y la distancia geodesica promedio se ajustan
razonablemente. Para modelos con mejor ajuste en transitividad, seria
recomendable explorar el ERGM con el termino gwesp (Robins
et al., 1999).
Se repite el analisis anterior sin filtrar por gobierno, construyendo la red de co-membresía para la totalidad de los 265 funcionarios registrados.
# Para la red completa se incluyen todos los funcionarios con GOBERNACION no vacia
# Se usa un patron que coincide con todos los gobiernos conocidos, evitando NAs
red_total <- construir_red_gobierno(datos, "PETRO|DUQUE|SANTOS|URIBE",
col_entidades)
cat("Red completa — Orden:", red_total$n,
"| Aristas:", ecount(red_total$grafo), "\n")## Red completa — Orden: 265 | Aristas: 2028
g_tot <- red_total$grafo
d_tot <- degree(g_tot)
col_tot <- color_genero(V(g_tot)$genero)
set.seed(42)
lay_tot <- layout_with_fr(g_tot)
plot(g_tot,
layout = lay_tot,
vertex.size = 1.5 + 5 * (d_tot / max(d_tot)),
vertex.color = col_tot,
vertex.frame.color = "#3d0f28",
vertex.label = ifelse(d_tot >= quantile(d_tot, 0.95),
V(g_tot)$name, NA),
vertex.label.cex = 0.45,
vertex.label.color = "black",
edge.color = adjustcolor("#9b6b8a", 0.2),
edge.width = 0.3,
main = "Red de poder — Todos los gobiernos")
legend("bottomleft",
legend = c("Masculino", "Femenino"),
col = c("#70284a", "#c9a8c0"),
pch = 21, pt.bg = c("#70284a", "#c9a8c0"),
bty = "n", cex = 0.85)met_total <- metricas_red(red_total$grafo, "Total")
kable(t(met_total),
caption = "Metricas estructurales — Red completa",
align = "r") %>%
kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE) %>%
row_spec(0, background = "#70284a", color = "white") %>%
column_spec(1, bold = TRUE)| Gobierno | Total |
| Orden | 265 |
| Tamano | 2028 |
| N_componentes | 3 |
| Tamano_GCC | 263 |
| Densidad | 0.058 |
| Transitividad_global | 0.66 |
| Transitividad_local | 0.8775 |
| Distancia_geodesica | 3.0151 |
| Diametro | 7 |
| Asortatividad_grado | 0.1725 |
| Grado_promedio | 15.306 |
top_total <- calcular_centralidad(red_total$grafo, "Total", top_n = 15)
presentar_top(top_total, "Top 15 funcionarios mas centrales — Red completa")| Funcionario | Grado norm. | Cercania | Intermediacion | Vector propio |
|---|---|---|---|---|
| MAURICIO CARDENAS SANTAMARIA | 0.2443 | 0.4662 | 0.1027 | 0.5635 |
| CARLOS HOLMES TRUJILLO GARCIA | 0.1908 | 0.4746 | 0.1762 | 0.6344 |
| GERMAN VARGAS LLERAS | 0.1870 | 0.4816 | 0.1082 | 1.0000 |
| MARTA LUCIA RAMIREZ BLANCO | 0.1832 | 0.4755 | 0.1043 | 0.4946 |
| JUAN MANUEL SANTOS CALDERON | 0.1679 | 0.4302 | 0.0734 | 0.3778 |
| OSCAR MAURICIO LIZCANO ARANGO | 0.1527 | 0.4043 | 0.0834 | 0.7735 |
| FEDERICO ALONSO RENJIFO VELEZ | 0.1527 | 0.4456 | 0.0436 | 0.6153 |
| JOSE ANTONIO OCAMPO GAVIRIA | 0.1450 | 0.4253 | 0.0301 | 0.3268 |
| JUAN CAMILO RESTREPO SALAZAR | 0.1450 | 0.4062 | 0.0196 | 0.3088 |
| ALEXANDER LOPEZ MAYA | 0.1450 | 0.4281 | 0.0418 | 0.8632 |
| ARMANDO ALBERTO BENEDETTI VILLANEDA | 0.1412 | 0.4338 | 0.0195 | 0.9440 |
| JUAN FERNANDO CRISTO BUSTOS | 0.1298 | 0.4274 | 0.0108 | 0.9278 |
| LUIS FERNANDO VELASCO CHAVES | 0.1298 | 0.4274 | 0.0108 | 0.9278 |
| CLAUDIA BLUM CAPURRO DE BARBERI | 0.1298 | 0.4418 | 0.0330 | 0.8460 |
| NANCY PATRICIA GUTIERREZ CASTANEDA | 0.1298 | 0.4274 | 0.0108 | 0.9278 |
# Red total: puede tener muchos nodos, se usan menos iteraciones
res_total <- ajustar_modelos(red_total$grafo, "Total",
n_iter_gibbs = 2000, n_burn_gibbs = 500)##
## ========================================
## Ajustando modelos para: Total
## ========================================
## [M1] Modelo de bloques estocasticos (Louvain)...
## Bloques detectados: 13 | Modularidad: 0.6744
## [M2] ERGM omitido (ergm/statnet no instalado).
## [M3] Modelo de sociabilidad (Gibbs)...
## Muestras obtenidas: 300
gof_total <- gof_sociabilidad(res_total, n_sim = 100)
kable(tabla_gof(gof_total, "Total") %>% select(-Gobierno),
caption = "Bondad de ajuste — Red completa",
col.names = c("Estadistico","Obs.","Media sim.","IC95 inf.","IC95 sup.")) %>%
kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE) %>%
row_spec(0, background = "#70284a", color = "white")| Estadistico | Obs. | Media sim. | IC95 inf. | IC95 sup. | |
|---|---|---|---|---|---|
| densidad | densidad | 0.0589 | 0.1118 | 0.1058 | 0.1176 |
| transitividad | transitividad | 0.6600 | 0.1611 | 0.1498 | 0.1736 |
| asortatividad | asortatividad | 0.1725 | -0.0689 | -0.0914 | -0.0500 |
| dist_geo | dist_geo | 3.0151 | 1.9550 | 1.9368 | 1.9759 |
Red completa. La red que agrupa a todos los gobiernos es la mas densa y conectada. Los funcionarios que aparecen como hubs en la red total son aquellos que participaron en mas de un gobierno, funcionando como puentes entre periodos: este tipo de continuidad burocratica es un rasgo estructural del Estado colombiano que trasciende a las administraciones. La alta transitividad global de la red total refleja la existencia de circulos de poder relativamente cerrados que se reproducen a traves del tiempo, independientemente de la orientacion politica del gobierno de turno (Chala, 2024).