A continuación se presenta el desarrollo del Taller 2 de la asignatura Análisis Estadístico de Redes, perteneciente a la Maestría en Ciencias – Estadística.
Estudiante: Helen Granados Rodríguez
CC: 1000835249
Correo: hgranados@unal.edu.co
library(igraph)
library(knitr)
library(RColorBrewer)
library(ggplot2)
library(ggraph)
library(tidygraph)
library(dplyr)
library(corrplot)El out-degree \(d_i^{\text{out}}\) y el in-degree \(d_i^{\text{in}}\) del nodo \(i\) se calculan como: \[d_i^{\text{out}} = \sum_{j:j\neq i} y_{i,j} \qquad \text{y} \qquad d_i^{\text{in}} = \sum_{j:j\neq i} y_{j,i}\]
Demostración: Si la red es no dirigida entonces \(d_i^{\text{out}} = d_i^{\text{in}}\).
Si la red es no dirigida, la matriz de adyacencia \(\mathbf{Y} = [y_{i,j}]\) es simétrica, es decir \(y_{i,j} = y_{j,i}\) para todo par \((i,j)\).
Entonces: \[d_i^{\text{out}} = \sum_{j:j\neq i} y_{i,j} \overset{y_{i,j}=y_{j,i}}{=} \sum_{j:j\neq i} y_{j,i} = d_i^{\text{in}}\]
Por lo tanto \(d_i^{\text{out}} = d_i^{\text{in}} \equiv d_i\) para redes no dirigidas. \(\blacksquare\)
La media global se define como: \[\bar{y} = \frac{1}{n(n-1)} \sum_{i,j:\, i\neq j} y_{i,j}\]
Demostración:
Sea \(n\) el número de nodos. Para una red no dirigida (\(y_{i,j} = y_{j,i}\)), la suma total es: \[\sum_{i,j:\,i\neq j} y_{i,j} = \sum_{i<j} y_{i,j} + \sum_{i>j} y_{i,j} = 2\sum_{i<j} y_{i,j}\]
La media de la parte triangular superior usa \(\frac{n(n-1)}{2}\) entradas: \[\bar{y}^{\text{sup}} = \frac{2}{n(n-1)} \sum_{i<j} y_{i,j}\]
Por simetría, la media de la parte triangular inferior es idéntica: \(\bar{y}^{\text{inf}} = \bar{y}^{\text{sup}}\).
Por tanto: \[\bar{y} = \frac{1}{n(n-1)} \cdot 2\sum_{i<j} y_{i,j} = \frac{2}{n(n-1)}\sum_{i<j} y_{i,j} = \bar{y}^{\text{sup}} = \bar{y}^{\text{inf}} \quad \blacksquare\]
Demostración:
Para una red binaria (sin dirigir o dirigida), el número total de aristas posibles excluyendo la diagonal es \(n(n-1)\) (dirigida) o \(\frac{n(n-1)}{2}\) (no dirigida). La densidad se define como: \[\Delta = \frac{|E|}{n(n-1)} \quad \text{(dirigida)}\]
donde \(|E| = \sum_{i,j:\,i\neq j} y_{i,j}\). Entonces: \[\bar{y} = \frac{1}{n(n-1)} \sum_{i,j:\,i\neq j} y_{i,j} = \frac{|E|}{n(n-1)} = \Delta \quad \blacksquare\]
Para redes no dirigidas, tanto numerador como denominador se dividen por 2 y el resultado es el mismo.
Demostración:
El grado de salida promedio es: \[\bar{d}^{\text{out}} = \frac{1}{n}\sum_i d_i^{\text{out}} = \frac{1}{n}\sum_i \sum_{j:j\neq i} y_{i,j} = \frac{1}{n}\sum_{i,j:\,i\neq j} y_{i,j}\]
Como \(\bar{y} = \frac{1}{n(n-1)}\sum_{i,j:\,i\neq j} y_{i,j}\), se tiene que \(\sum_{i,j:\,i\neq j} y_{i,j} = n(n-1)\bar{y}\), y por tanto: \[\bar{d}^{\text{out}} = \frac{n(n-1)\bar{y}}{n} = (n-1)\bar{y}\]
El mismo argumento aplica para \(\bar{d}^{\text{in}}\) (sumando por columnas en lugar de filas), ya que la suma total es la misma. Por tanto \((n-1)\bar{y} = \bar{d}^{\text{out}} = \bar{d}^{\text{in}}\). \(\blacksquare\)
Demostración:
En un grafo círculo de orden \(n\), cada vértice \(i\) está conectado exactamente a sus dos vecinos (\(i-1\) e \(i+1\) en módulo \(n\)). Por tanto \(d_i = 2\) para todo \(i = 1,\ldots,n\), y: \[\bar{d} = \frac{1}{n}\sum_{i=1}^n d_i = \frac{1}{n} \cdot 2n = 2 \quad \blacksquare\]
Demostración:
En un grafo estrella de orden \(n\), hay un nodo central (hub) con grado \(n-1\) y \(n-1\) nodos hoja cada uno con grado \(1\). El grado promedio es: \[\bar{d} = \frac{1}{n}\left[(n-1) + (n-1)\cdot 1\right] = \frac{2(n-1)}{n} = 2\cdot\frac{n-1}{n}\]
Cuando \(n \to \infty\): \(\bar{d} = 2\cdot\frac{n-1}{n} = 2\left(1 - \frac{1}{n}\right) \to 2\). \(\blacksquare\)
# Grafo círculo n=9
g_circ <- make_ring(9)
cat("Grado promedio círculo (n=9):", mean(degree(g_circ)), "\n")## Grado promedio círculo (n=9): 2
# Grafo estrella n=9
g_star <- make_star(9, mode = "undirected")
cat("Grado promedio estrella (n=9):", mean(degree(g_star)), "\n")## Grado promedio estrella (n=9): 1.777778
## Fórmula 2*(n-1)/n con n=9: 1.777778
par(mfrow = c(1, 2), mar = c(1,1,2,1))
plot(g_star, vertex.color = "green3", vertex.size = 20,
main = "Grafo Estrella (n=9)", vertex.label.cex = 0.9)
plot(g_circ, layout = layout_in_circle, vertex.color = "#E74C3C",
vertex.size = 20, main = "Grafo Círculo (n=9)", vertex.label.cex = 0.9)El grafo tiene las siguientes aristas (leídas de la imagen): \(\{1,2\}, \{1,5\}, \{1,6\}, \{2,5\}, \{3,6\}, \{3,4\}, \{4,6\}\).
Definiciones:
# Construcción del grafo a partir de la imagen
g4 <- graph_from_literal(1-2, 1-5, 1-6, 2-5, 3-6, 3-4, 4-6)
plot(g4, vertex.size = 30, vertex.color = "lightgray",
vertex.label.color = "black", vertex.label.font = 2,
main = "Grafo del Punto 4",
layout = layout_with_kk(g4))# Verificar existencia de aristas para cada secuencia
verificar_arista <- function(g, u, v) {
are.connected(g, u, v)
}
# Secuencia a: 2-1-6-3-4
cat("=== Secuencia a: 2-1-6-3-4 ===\n")## === Secuencia a: 2-1-6-3-4 ===
seq_a <- c(2,1,6,3,4)
for(i in 1:(length(seq_a)-1)) {
cat(seq_a[i],"-",seq_a[i+1],":", verificar_arista(g4, seq_a[i], seq_a[i+1]), "\n")
}## Warning: `are.connected()` was deprecated in igraph 2.0.0.
## ℹ Please use `are_adjacent()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## 2 - 1 : TRUE
## 1 - 6 : FALSE
## 6 - 3 : FALSE
## 3 - 4 : FALSE
##
## === Secuencia b: 2-1-6-3-4-1-5 ===
seq_b <- c(2,1,6,3,4,1,5)
for(i in 1:(length(seq_b)-1)) {
cat(seq_b[i],"-",seq_b[i+1],":", verificar_arista(g4, seq_b[i], seq_b[i+1]), "\n")
}## 2 - 1 : TRUE
## 1 - 6 : FALSE
## 6 - 3 : FALSE
## 3 - 4 : FALSE
## 4 - 1 : TRUE
## 1 - 5 : FALSE
##
## === Secuencia c: 2-1-2-5-1-4 ===
seq_c <- c(2,1,2,5,1,4)
for(i in 1:(length(seq_c)-1)) {
cat(seq_c[i],"-",seq_c[i+1],":", verificar_arista(g4, seq_c[i], seq_c[i+1]), "\n")
}## 2 - 1 : TRUE
## 1 - 2 : TRUE
## 2 - 5 : FALSE
## 5 - 1 : FALSE
## 1 - 4 : TRUE
a. \(2-1-6-3-4\)
Todas las aristas existen: \(\{2,1\}\)✓, \(\{1,6\}\)✓, \(\{6,3\}\)✓, \(\{3,4\}\)✓. No hay repetición de nodos ni aristas. No es cerrada.
b. \(2-1-6-3-4-1-5\)
Aristas: \(\{2,1\}\)✓, \(\{1,6\}\)✓, \(\{6,3\}\)✓, \(\{3,4\}\)✓, \(\{4,1\}\)❌ — la arista \(\{4,1\}\) no existe en el grafo.
c. \(2-1-2-5-1-4\)
Aristas: \(\{2,1\}\)✓, \(\{1,2\}\)✓ (repetida), \(\{2,5\}\)✓, \(\{5,1\}\)✓, \(\{1,4\}\)❌ — la arista \(\{1,4\}\) no existe.
load("comtrade.RData")
# Matriz Y: promedio de bienes manufacturados (productos 5 y 6) a lo largo de los 10 años
Y <- apply(X = comtrade[,,c(5,6),], MARGIN = c(1,2), FUN = mean)
n <- nrow(Y)
paises <- rownames(Y)
cat("Dimensión de Y:", dim(Y), "\n")## Dimensión de Y: 30 30
## Número de países: 30
# Suma de todos los elementos fuera de la diagonal
suma_total <- sum(Y[row(Y) != col(Y)], na.rm = TRUE)
y_bar <- suma_total / (n * (n - 1))
cat("Media global y_bar:", round(y_bar, 6), "\n")## Media global y_bar: 0.037784
La media global \(\bar{y} \approx\) 0.0378 indica que en promedio el crecimiento anual del comercio en bienes manufacturados a lo largo del período fue positivo, aunque de magnitud moderada. Al ser la densidad de la red, también señala que las conexiones comerciales entre países son relativamente frecuentes.
# Promedio fila: excluir diagonal (NA en la diagonal)
diag(Y) <- NA
y_fila <- rowMeans(Y, na.rm = TRUE)
# Histograma
hist(y_fila,
main = expression("Distribución de promedios fila " * bar(y)[i*"•"]),
xlab = expression(bar(y)[i*"•"]),
col = "#5DADE2", border = "white",
breaks = 12)
abline(v = mean(y_fila), col = "red", lwd = 2, lty = 2)
legend("topright", legend = paste("Media:", round(mean(y_fila),4)),
col = "red", lty = 2, lwd = 2)Los promedios fila \(\bar{y}_{i\bullet}\) miden el nivel promedio de exportaciones de cada país hacia todos los demás. Un valor alto indica que el país tiende a incrementar sus exportaciones de bienes manufacturados hacia la mayoría de sus socios comerciales — es una medida de sociabilidad comercial exportadora.
y_col <- colMeans(Y, na.rm = TRUE)
hist(y_col,
main = expression("Distribución de promedios columna " * bar(y)["•j"]),
xlab = expression(bar(y)["•j"]),
col = "#E74C3C", border = "white",
breaks = 12)
abline(v = mean(y_col), col = "navy", lwd = 2, lty = 2)
legend("topright", legend = paste("Media:", round(mean(y_col),4)),
col = "navy", lty = 2, lwd = 2)Los promedios columna \(\bar{y}_{\bullet j}\) miden el nivel promedio de importaciones que el país \(j\) recibe de todos los demás. Un valor alto indica que el país es un destino atractivo de bienes manufacturados — es una medida de popularidad como destino comercial.
## Media de promedios fila: 0.037784
## Media de promedios columna: 0.037784
## Media global y_bar: 0.037784
Como era de esperarse por el resultado teórico del Punto 2c, la media de los promedios fila y la media de los promedios columna son iguales entre sí y equivalen a \((n-1)\bar{y}\), confirmando la consistencia del marco teórico. La tendencia local positiva sugiere que en promedio los países incrementaron su comercio de bienes manufacturados durante el período.
## DE de promedios fila: 0.0302
## DE de promedios columna: 0.041016
La desviación estándar de los promedios fila y columna cuantifica la heterogeneidad nodal: países como EE.UU., Alemania o China tienen patrones de exportación/importación muy distintos a economías pequeñas. Una DE elevada indica que no todos los países participan igualmente en el comercio de bienes manufacturados.
cor_ij <- cor(y_fila, y_col, use = "complete.obs")
cat("Coeficiente de correlación entre y_fila y y_col:", round(cor_ij, 4), "\n")## Coeficiente de correlación entre y_fila y y_col: 0.7003
# Dispersograma
plot(y_fila, y_col,
xlab = expression("Promedio fila " * bar(y)[i*"•"] * " (Sociabilidad)"),
ylab = expression("Promedio columna " * bar(y)["•j"] * " (Popularidad)"),
main = "Sociabilidad vs. Popularidad Comercial",
pch = 19, col = "#2ECC71", cex = 0.9)
abline(a = 0, b = 1, col = "red", lty = 2, lwd = 2)
text(y_fila, y_col, labels = paises, cex = 0.55, pos = 3)
legend("topleft", legend = paste("r =", round(cor_ij, 3)), bty = "n")La correlación \(r \approx\) 0.7 entre sociabilidad y popularidad indica que los países con alta actividad exportadora también tienden a ser grandes importadores. Los puntos cercanos a la recta \(y=x\) tienen flujos de exportación e importación similares (países equilibrados), mientras que los alejados por encima son países con mayor importación que exportación, y viceversa.
load("conflict.RData")
# Construcción del grafo de conflictos
g_conflict <- graph_from_adjacency_matrix(dat$Y,
mode = "directed",
weighted = "frecuencia",
diag = FALSE)
V(g_conflict)$population <- dat$X[,1]
V(g_conflict)$gdp <- dat$X[,2]
V(g_conflict)$polity <- dat$X[,3]
cat("Orden (países):", vcount(g_conflict), "\n")## Orden (países): 130
## Tamaño (conflictos): 203
# Paleta por régimen político
cols_pol <- colorRampPalette(c("#C0392B", "#F39C12", "#2980B9"))(21)
V(g_conflict)$color <- cols_pol[V(g_conflict)$polity + 11]
V(g_conflict)$size <- sqrt(degree(g_conflict)) * 3.5
umbral <- quantile(degree(g_conflict), 0.85)
V(g_conflict)$label <- ifelse(degree(g_conflict) >= umbral, V(g_conflict)$name, NA)
V(g_conflict)$label.cex <- 0.7
E(g_conflict)$color <- adjustcolor("gray40", alpha.f = 0.3)
E(g_conflict)$arrow.size <- 0.2
set.seed(42)
plot(g_conflict, layout = layout_with_fr,
main = "Red de Conflictos Internacionales (Años 90) — Layout FR",
sub = "Color: Régimen (Rojo=Autocracia, Azul=Democracia) | Tamaño: Grado")
legend("bottomleft", legend = c("Autocracia", "Transición", "Democracia"),
fill = c("#C0392B", "#F39C12", "#2980B9"), bty = "n", cex = 0.8)set.seed(123)
plot(g_conflict, layout = layout_with_kk,
main = "Red de Conflictos Internacionales — Layout Kamada-Kawai",
sub = "Color: Régimen | Tamaño: Grado total")
legend("bottomleft", legend = c("Autocracia", "Transición", "Democracia"),
fill = c("#C0392B", "#F39C12", "#2980B9"), bty = "n", cex = 0.8)# Heatmap de la matriz de conflictos
Y_conf <- dat$Y
palf <- colorRampPalette(c("white", "#E74C3C"))(50)
heatmap(Y_conf,
Rowv = NA, Colv = NA,
col = palf,
scale = "none",
margins = c(7, 7),
main = "Heatmap: Matriz de Conflictos",
xlab = "País receptor", ylab = "País iniciador",
cexRow = 0.45, cexCol = 0.45)# Diagrama de arco
g_tidy <- as_tbl_graph(g_conflict) %>%
activate(nodes) %>%
mutate(
degree = centrality_degree(mode = "all"),
Regimen = case_when(
polity <= -6 ~ "Autocracia",
polity > -6 & polity < 6 ~ "Transicion",
polity >= 6 ~ "Democracia"
)
) %>%
filter(degree > 0) %>%
arrange(polity)
ggraph(g_tidy, layout = "linear") +
geom_edge_arc(aes(edge_alpha = after_stat(index)),
strength = 0.7, edge_colour = "gray50", show.legend = FALSE) +
geom_node_point(aes(size = gdp, color = Regimen), alpha = 0.85) +
scale_color_manual(values = c("Autocracia" = "#C0392B",
"Transicion" = "#F39C12",
"Democracia" = "#2980B9")) +
scale_size_continuous(range = c(2, 9), name = "PIB") +
labs(title = "Diagrama de Arco: Conflictos Internacionales",
subtitle = "Eje ordenado por régimen político | Izq: Autocracia — Der: Democracia",
color = "Régimen") +
theme_graph() +
theme(legend.position = "top", plot.margin = margin(10, 10, 40, 10))Y_c <- dat$Y
n_c <- nrow(Y_c)
diag(Y_c) <- NA
y_bar_c <- mean(Y_c, na.rm = TRUE)
cat("Media global y_bar:", round(y_bar_c, 6), "\n")## Media global y_bar: 0.018187
## Warning: `graph.density()` was deprecated in igraph 2.0.0.
## ℹ Please use `edge_density()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Densidad de la red: 0.012105
La media global \(\bar{y} \approx\) 0.0182 confirma que la densidad de conflictos entre países durante los años 90 fue baja. En promedio, el número esperado de conflictos entre cualquier par de países era cercano a cero, lo que refleja que la mayoría de los pares no tenía conflictos directos.
out_deg <- degree(g_conflict, mode = "out")
in_deg <- degree(g_conflict, mode = "in")
cat("--- Out-degree ---\n")## --- Out-degree ---
## Media: 1.562 | DE: 3.589
## --- In-degree ---
## Media: 1.562 | DE: 1.984
par(mfrow = c(1, 2))
hist(out_deg, breaks = 15, col = "#E74C3C", border = "white",
main = "Distribución del Out-degree",
xlab = "Out-degree", ylab = "Frecuencia")
abline(v = mean(out_deg), col = "navy", lwd = 2, lty = 2)
hist(in_deg, breaks = 15, col = "#2980B9", border = "white",
main = "Distribución del In-degree",
xlab = "In-degree", ylab = "Frecuencia")
abline(v = mean(in_deg), col = "darkred", lwd = 2, lty = 2)Ambas distribuciones muestran fuerte asimetría positiva (sesgo a la derecha): la mayoría de los países tiene pocos conflictos mientras un pequeño grupo de países concentra la mayor parte de los conflictos iniciados y recibidos. La media y la desviación estándar confirman esta heterogeneidad.
cor_deg <- cor(out_deg, in_deg)
cat("Correlación out-degree vs. in-degree:", round(cor_deg, 4), "\n")## Correlación out-degree vs. in-degree: 0.604
plot(out_deg, in_deg,
xlab = "Out-degree (conflictos iniciados)",
ylab = "In-degree (conflictos recibidos)",
main = "Out-degree vs. In-degree",
pch = 19, col = "#8E44AD", cex = 0.8)
abline(a = 0, b = 1, col = "red", lty = 2, lwd = 2)
text(out_deg, in_deg, labels = V(g_conflict)$name, cex = 0.45, pos = 3)
legend("topleft", legend = paste("r =", round(cor_deg, 3)), bty = "n")La correlación \(r \approx\) 0.604 indica que los países que más conflictos inician también tienden a ser los que más conflictos reciben. Los puntos por encima de la recta \(y=x\) son países con más conflictos recibidos que iniciados (potencialmente más vulnerables), y los de abajo son más agresivos en términos relativos.
top_out <- sort(out_deg, decreasing = TRUE)[1:5]
top_in <- sort(in_deg, decreasing = TRUE)[1:5]
top_total <- sort(degree(g_conflict, mode = "all"), decreasing = TRUE)[1:5]
tabla_activos <- data.frame(
Ranking = 1:5,
Pais_Mayor_Agresion = names(top_out),
Conflictos_Iniciados = as.numeric(top_out),
Pais_Mayor_Blanco = names(top_in),
Conflictos_Recibidos = as.numeric(top_in),
Pais_Mayor_Total = names(top_total),
Grado_Total = as.numeric(top_total)
)
knitr::kable(tabla_activos, caption = "Top 5 países más activos en conflictos")| Ranking | Pais_Mayor_Agresion | Conflictos_Iniciados | Pais_Mayor_Blanco | Conflictos_Recibidos | Pais_Mayor_Total | Grado_Total |
|---|---|---|---|---|---|---|
| 1 | IRQ | 27 | IRQ | 15 | IRQ | 42 |
| 2 | JOR | 26 | USA | 8 | JOR | 27 |
| 3 | USA | 11 | HAI | 7 | USA | 19 |
| 4 | UGA | 7 | TUR | 6 | TUR | 11 |
| 5 | CHN | 6 | JPN | 5 | CHN | 10 |
# Grafo 1: Cruz (4 nodos en X, 1 centro) - imagen muestra forma de X con 5 nodos
g7_1 <- graph_from_literal(1-3, 2-3, 3-4, 3-5) # Estrella de 4 hojas
# Grafo 2: Árbol con raíz central y 3 hojas (forma de Y/tridente)
g7_2 <- graph_from_literal(1-2-3, 3-4, 3-5) # Camino con bifurcación
# Grafo 3: Camino lineal de 5 nodos
g7_3 <- graph_from_literal(1-2-3-4-5)
# Grafo 4: Grafo tipo "diamante" o pentágono con cola
g7_4 <- graph_from_literal(1-2, 2-3, 3-4, 4-5, 5-2) # Cola + ciclo
par(mfrow = c(2, 2), mar = c(1,1,2,1))
plot(g7_1, vertex.size = 25, vertex.color = "#3498DB",
main = "Grafo 1 (Cruz)")
plot(g7_2, vertex.size = 25, vertex.color = "#E74C3C",
main = "Grafo 2 (Tridente)")
plot(g7_3, layout = layout_as_tree, vertex.size = 25,
vertex.color = "#2ECC71", main = "Grafo 3 (Camino)")
plot(g7_4, vertex.size = 25, vertex.color = "#F39C12",
main = "Grafo 4 (Cola+Ciclo)")# Función para calcular tabla de centralidad
tabla_centralidad <- function(g, nombre) {
deg <- degree(g, normalized = FALSE)
clo <- closeness(g, normalized = TRUE)
bet <- betweenness(g, normalized = TRUE)
eig <- eigen_centrality(g)$vector
df <- data.frame(
Nodo = V(g)$name,
Grado = deg,
Cercania = round(clo, 4),
Intermediacion = round(bet, 4),
Propia = round(eig, 4)
)
resumen <- data.frame(
Medida = c("Media", "DE"),
Grado = c(round(mean(deg), 4), round(sd(deg), 4)),
Cercania = c(round(mean(clo), 4), round(sd(clo), 4)),
Intermed = c(round(mean(bet), 4), round(sd(bet), 4)),
Propia = c(round(mean(eig), 4), round(sd(eig), 4))
)
cat("\n===", nombre, "===\n")
print(knitr::kable(df, caption = paste("Centralidades —", nombre)))
cat("\n")
print(knitr::kable(resumen, caption = paste("Resumen —", nombre),
col.names = c("Medida","Grado","Cercanía","Intermediación","Propia")))
invisible(resumen)
}=== Grafo 1 - Cruz ===
| Nodo | Grado | Cercania | Intermediacion | Propia | |
|---|---|---|---|---|---|
| 1 | 1 | 1 | 0.5714 | 0 | 0.5 |
| 3 | 3 | 4 | 1.0000 | 1 | 1.0 |
| 2 | 2 | 1 | 0.5714 | 0 | 0.5 |
| 4 | 4 | 1 | 0.5714 | 0 | 0.5 |
| 5 | 5 | 1 | 0.5714 | 0 | 0.5 |
| Medida | Grado | Cercanía | Intermediación | Propia |
|---|---|---|---|---|
| Media | 1.6000 | 0.6571 | 0.2000 | 0.6000 |
| DE | 1.3416 | 0.1917 | 0.4472 | 0.2236 |
=== Grafo 2 - Tridente ===
| Nodo | Grado | Cercania | Intermediacion | Propia |
|---|---|---|---|---|
| 1 | 1 | 0.4444 | 0.0000 | 0.4142 |
| 2 | 2 | 0.6667 | 0.5000 | 0.7654 |
| 3 | 3 | 0.8000 | 0.8333 | 1.0000 |
| 4 | 1 | 0.5000 | 0.0000 | 0.5412 |
| 5 | 1 | 0.5000 | 0.0000 | 0.5412 |
| Medida | Grado | Cercanía | Intermediación | Propia |
|---|---|---|---|---|
| Media | 1.6000 | 0.5822 | 0.2667 | 0.6524 |
| DE | 0.8944 | 0.1475 | 0.3837 | 0.2319 |
=== Grafo 3 - Camino lineal ===
| Nodo | Grado | Cercania | Intermediacion | Propia |
|---|---|---|---|---|
| 1 | 1 | 0.4000 | 0.0000 | 0.500 |
| 2 | 2 | 0.5714 | 0.5000 | 0.866 |
| 3 | 2 | 0.6667 | 0.6667 | 1.000 |
| 4 | 2 | 0.5714 | 0.5000 | 0.866 |
| 5 | 1 | 0.4000 | 0.0000 | 0.500 |
| Medida | Grado | Cercanía | Intermediación | Propia |
|---|---|---|---|---|
| Media | 1.6000 | 0.5219 | 0.3333 | 0.7464 |
| DE | 0.5477 | 0.1179 | 0.3118 | 0.2315 |
=== Grafo 4 - Cola+Ciclo ===
| Nodo | Grado | Cercania | Intermediacion | Propia |
|---|---|---|---|---|
| 1 | 1 | 0.5000 | 0.0000 | 0.4682 |
| 2 | 3 | 0.8000 | 0.5833 | 1.0000 |
| 3 | 2 | 0.6667 | 0.1667 | 0.8338 |
| 4 | 2 | 0.5714 | 0.0833 | 0.7808 |
| 5 | 2 | 0.6667 | 0.1667 | 0.8338 |
| Medida | Grado | Cercanía | Intermediación | Propia |
|---|---|---|---|---|
| Media | 2.0000 | 0.6410 | 0.2000 | 0.7833 |
| DE | 0.7071 | 0.1133 | 0.2252 | 0.1945 |
Interpretación general:
# Simetrización débil: y_{ij} = max(y_{ij}, y_{ji})
Y_sym <- pmax(dat$Y, t(dat$Y))
g_sym <- graph_from_adjacency_matrix(Y_sym, mode = "undirected",
weighted = TRUE, diag = FALSE)
# Remover nodos aislados
g_sym <- delete.vertices(g_sym, V(g_sym)[degree(g_sym) == 0])## Warning: `delete.vertices()` was deprecated in igraph 2.0.0.
## ℹ Please use `delete_vertices()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
V(g_sym)$polity <- dat$X[V(g_sym)$name, "polity"]
V(g_sym)$gdp <- dat$X[V(g_sym)$name, "gdp"]
cat("Orden (sin aislados):", vcount(g_sym), "\n")## Orden (sin aislados): 91
## Tamaño: 160
cols_sym <- colorRampPalette(c("#C0392B","#F39C12","#2980B9"))(21)
V(g_sym)$color <- cols_sym[V(g_sym)$polity + 11]
V(g_sym)$size <- 3 + sqrt(degree(g_sym)) * 2.5
umbral_sym <- quantile(degree(g_sym), 0.85)
V(g_sym)$label <- ifelse(degree(g_sym) >= umbral_sym, V(g_sym)$name, NA)
V(g_sym)$label.cex <- 0.65
E(g_sym)$color <- adjustcolor("gray50", alpha.f = 0.35)
E(g_sym)$width <- 0.5 + log1p(E(g_sym)$weight) * 0.4
set.seed(999)
plot(g_sym, layout = layout_with_fr,
main = "Red de Conflictos Simetrizada (sin aislados)",
sub = "Color: Régimen político | Tamaño: Grado | Grosor: Peso del conflicto")
legend("bottomleft", legend = c("Autocracia","Transición","Democracia"),
fill = c("#C0392B","#F39C12","#2980B9"), bty = "n", cex = 0.8)Y_sym_mat <- as.matrix(as_adjacency_matrix(g_sym, attr = "weight"))
palf2 <- colorRampPalette(c("white", "#2980B9"))(50)
heatmap(Y_sym_mat, Rowv = NA, Colv = NA, col = palf2,
scale = "none", margins = c(7,7),
main = "Heatmap: Conflictos Simetrizados",
cexRow = 0.45, cexCol = 0.45)## === DISTANCIA ===
diam <- diameter(g_sym, weights = NA)
avg_path <- mean_distance(g_sym, directed = FALSE)
cat("Diámetro:", diam, "\n")## Diámetro: 9
## Longitud media de camino: 4.7558
## === CENTRALIDAD ===
deg_sym <- degree(g_sym)
clo_sym <- closeness(g_sym, normalized = TRUE)
bet_sym <- betweenness(g_sym, normalized = TRUE)
eig_sym <- eigen_centrality(g_sym)$vector
resumen_central <- data.frame(
Medida = c("Media", "DE"),
Grado = c(round(mean(deg_sym),3), round(sd(deg_sym),3)),
Cercania = c(round(mean(clo_sym),3), round(sd(clo_sym),3)),
Intermediacion = c(round(mean(bet_sym),3), round(sd(bet_sym),3)),
Propia = c(round(mean(eig_sym),3), round(sd(eig_sym),3))
)
knitr::kable(resumen_central, caption = "Resumen de centralidades — Red simetrizada")| Medida | Grado | Cercania | Intermediacion | Propia |
|---|---|---|---|---|
| Media | 3.516 | 0.287 | 0.027 | 0.084 |
| DE | 4.288 | 0.203 | 0.058 | 0.157 |
## === COHESIÓN Y CONECTIVIDAD ===
## Densidad: 0.0391
## Transitividad (clustering global): 0.1634
## Clustering promedio (local): 0.4243
## Warning: `is.connected()` was deprecated in igraph 2.0.0.
## ℹ Please use `is_connected()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## ¿Conectado?: FALSE
## Número de componentes: 5
## Tamaño del componente mayor: 83
## === DETECCIÓN DE COMUNIDADES ===
set.seed(42)
comm_walk <- cluster_walktrap(g_sym)
comm_lv <- cluster_louvain(g_sym)
cat("Louvain — N° comunidades:", length(comm_lv), "| Modularidad:", round(modularity(comm_lv), 4), "\n")## Louvain — N° comunidades: 12 | Modularidad: 0.6051
cat("Walktrap — N° comunidades:", length(comm_walk), "| Modularidad:", round(modularity(comm_walk), 4), "\n")## Walktrap — N° comunidades: 19 | Modularidad: 0.5504
# Visualización con comunidades Louvain
set.seed(42)
plot(comm_lv, g_sym,
vertex.label = V(g_sym)$label,
vertex.label.cex = 0.6,
vertex.size = V(g_sym)$size,
main = "Comunidades Louvain — Red de Conflictos Simetrizada")Interpretación:
El Capítulo 6 de Luke (2015) aborda técnicas de visualización que van más allá de los gráficos estáticos estándar. Se organiza en tres bloques principales:
Gráficos interactivos: igraph ofrece la
función tkplot() para edición interactiva de layouts.
Paquetes como networkD3 permiten exportar visualizaciones
en D3.js para la web, y Shiny habilita aplicaciones interactivas
completas.
Diagramas especializados: Los diagramas de
arco (paquete arcdiagram) son útiles cuando la
posición de los nodos importa menos que el patrón de conexiones; los
nodos se disponen en línea y los arcos muestran las relaciones. Los
diagramas de cuerda (paquete circlize) usan una
disposición circular y son especialmente útiles para redes ponderadas y
dirigidas. Los heatmaps aplican la sociomatriz como una matriz
de colores, útiles para redes valoradas.
Redes con ggplot2: El paquete
ggraph extiende la gramática de ggplot2 al
análisis de redes, ofreciendo flexibilidad estética y compatibilidad con
el ecosistema tidyverse.
circlizeif (!requireNamespace("circlize", quietly = TRUE)) {
message("Instale el paquete 'circlize' para ver el diagrama de cuerda.")
} else {
library(circlize)
# Usar submatriz de conflictos (top 15 países por grado)
top15 <- names(sort(degree(g_sym), decreasing = TRUE))[1:15]
Y_top <- Y_sym[top15, top15]
circlize::chordDiagram(Y_top,
transparency = 0.5,
annotationTrack = "grid",
preAllocateTracks = 1)
circlize::circos.trackPlotRegion(track.index = 1, panel.fun = function(x, y) {
xlim <- get.cell.meta.data("xlim")
ylim <- get.cell.meta.data("ylim")
sector.name <- get.cell.meta.data("sector.index")
circos.text(mean(xlim), ylim[1] + 0.5, sector.name,
facing = "clockwise", niceFacing = TRUE, adj = c(0, 0.5), cex = 0.6)
}, bg.border = NA)
title("Diagrama de Cuerda: Top 15 países en conflictos simetrizados")
}# Submatriz top 20 países
top20 <- names(sort(degree(g_sym), decreasing = TRUE))[1:20]
Y_top20 <- Y_sym[top20, top20]
palf3 <- colorRampPalette(c("white", "#8E44AD"))(50)
heatmap(Y_top20, Rowv = NA, Colv = NA, col = palf3,
scale = "none", margins = c(9, 9),
main = "Heatmap: Top 20 Países en Conflictos",
cexRow = 0.7, cexCol = 0.7)El Capítulo 8 trata la identificación de subgrupos cohesivos dentro de redes sociales, esencial para comprender la estructura interna de sistemas complejos.
Cohesión social: Se basa en la densidad y fuerza de los lazos dentro de un subconjunto de nodos. Los cliques son subgrafos donde todos los nodos están conectados entre sí (completamente densos), pero son frágiles y raros en redes grandes. Los k-cores son una alternativa más robusta: un k-core es el subgrafo maximal donde cada nodo tiene al menos \(k\) vecinos dentro del subgrafo. Los k-cores son anidados, no se solapan y son fáciles de calcular.
Detección de comunidades: Los algoritmos de modularidad (Louvain, walktrap, fast-greedy) buscan particiones de los nodos que maximicen la modularidad \(Q\), que compara la densidad de conexiones dentro de las comunidades con la esperada bajo un modelo aleatorio.
## === K-CORES ===
## Distribución de coreness:
## coreness_vals
## 1 2 3
## 32 22 37
# Visualización del k-core máximo
max_core <- max(coreness_vals)
g_core <- induced_subgraph(g_sym, V(g_sym)[coreness_vals == max_core])
cat("\nNodos en el", max_core, "-core:", vcount(g_core), "\n")##
## Nodos en el 3 -core: 37
## Países: ANG, ARG, AUL, BEL, CAN, CDI, CHA, CHN, DRC, EGY, FRN, GRC, GUI, HAI, IRN, IRQ, ISR, JOR, JPN, LBR, NIG, NIR, NTH, PRK, QAT, ROK, RWA, SAU, SIE, SPN, SUD, SYR, TUR, UAE, UGA, UKG, USA
plot(g_core, vertex.size = 20, vertex.color = "#E74C3C",
vertex.label.cex = 0.7,
main = paste0(max_core, "-Core: Países con más conflictos mutuos"))## === COMUNIDADES ===
comm_fg <- cluster_fast_greedy(g_sym)
cat("Fast-greedy — Comunidades:", length(comm_fg), "| Q:", round(modularity(comm_fg), 4), "\n")## Fast-greedy — Comunidades: 12 | Q: 0.6065
# Tabla de membresía
memb_df <- data.frame(
Pais = V(g_sym)$name,
Comunidad_Louvain = membership(comm_lv),
Comunidad_Walktrap = membership(comm_walk)
) %>% arrange(Comunidad_Louvain)
knitr::kable(head(memb_df, 20), caption = "Membresía de comunidades (primeros 20 países)")| Pais | Comunidad_Louvain | Comunidad_Walktrap | |
|---|---|---|---|
| AFG | AFG | 1 | 1 |
| ARG | ARG | 1 | 1 |
| BAH | BAH | 1 | 1 |
| CAN | CAN | 1 | 1 |
| CHL | CHL | 1 | 1 |
| CUB | CUB | 1 | 1 |
| DOM | DOM | 1 | 1 |
| FRN | FRN | 1 | 1 |
| HAI | HAI | 1 | 1 |
| IRN | IRN | 1 | 1 |
| IRQ | IRQ | 1 | 1 |
| ITA | ITA | 1 | 1 |
| JOR | JOR | 1 | 1 |
| MOR | MOR | 1 | 1 |
| NTH | NTH | 1 | 1 |
| OMA | OMA | 1 | 1 |
| QAT | QAT | 1 | 4 |
| SAU | SAU | 1 | 4 |
| SPN | SPN | 1 | 1 |
| UAE | UAE | 1 | 1 |
El Capítulo 9 introduce las redes de afiliación (o redes bipartitas de dos modos), donde los actores no se conectan directamente sino a través de su co-pertenencia a grupos o eventos. Ejemplos clásicos: directorios corporativos compartidos, actores en películas, científicos en publicaciones.
La matriz de incidencia \(B\) (actores × grupos) es la estructura de datos fundamental. Las proyecciones transforman la red de dos modos en dos redes de un modo: actores conectados si comparten grupo, o grupos conectados si comparten actores.
# Ejemplo: países y sus bloques regionales (simplificado)
regiones <- list(
"OCDE" = c("USA","Germany","France","Japan","Italy","Canada","Australia"),
"Asia" = c("China","Japan","Rep. of Korea","Singapore","Malaysia","Thailand","Indonesia"),
"America_Latina" = c("Brazil","Mexico"),
"Europa_Este" = c("Czech Rep.","Turkey")
)
# Crear matriz de incidencia
paises_bip <- unique(unlist(regiones))
grupos_bip <- names(regiones)
B <- matrix(0, nrow = length(paises_bip), ncol = length(grupos_bip),
dimnames = list(paises_bip, grupos_bip))
for(g in grupos_bip) {
for(p in regiones[[g]]) {
if(p %in% paises_bip) B[p, g] <- 1
}
}
# Red bipartita
g_bip <- graph.incidence(B)## Warning: `graph.incidence()` was deprecated in igraph 2.0.0.
## ℹ Please use `graph_from_biadjacency_matrix()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
V(g_bip)$type_color <- ifelse(V(g_bip)$type, "#E74C3C", "#3498DB")
V(g_bip)$shape_type <- ifelse(V(g_bip)$type, "square", "circle")
plot(g_bip,
vertex.color = V(g_bip)$type_color,
vertex.shape = V(g_bip)$shape_type,
vertex.size = 12,
vertex.label.cex = 0.65,
main = "Red Bipartita: Países y Bloques Regionales",
sub = "Círculos azules: Países | Cuadrados rojos: Bloques")## Warning: `bipartite.projection()` was deprecated in igraph 2.0.0.
## ℹ Please use `bipartite_projection()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
##
## Proyección sobre países:
## Nodos: 17 | Aristas: 44
plot(proj_paises, vertex.size = 15, vertex.color = "#3498DB",
vertex.label.cex = 0.6,
main = "Proyección: Países conectados por bloques comunes")Las distribuciones del grado modelan la heterogeneidad en el número de conexiones de los nodos. A continuación se caracterizan cinco familias:
Ley de potencias (Power Law): \(P(d) \propto d^{-\alpha}\). Cola pesada, lineal en escala log-log. Captura redes scale-free donde pocos nodos concentran la mayoría de las conexiones (hubs). Apropiada para internet, redes de citas, redes sociales masivas. Ventaja: interpretación clara de \(\alpha\). Desventaja: el ajuste puede ser sensible al rango elegido y difícil de distinguir de otras colas pesadas.
Log-normal: \(\log(d) \sim N(\mu, \sigma^2)\). También de cola pesada pero con caída más rápida que la ley de potencias. Surge de procesos multiplicativos. Adecuada cuando ningún nodo domina completamente. Ventaja: fácil de ajustar y contrastar. Desventaja: puede confundirse con power law empíricamente.
Exponencial: \(P(d) \propto e^{-\lambda d}\). Cola ligera, cae rápidamente. Característica de redes aleatorias (Erdős-Rényi). Apropiada cuando todos los nodos tienen probabilidad similar de conexión; poca heterogeneidad. Ventaja: simplicidad. Desventaja: no capta hubs.
Poisson: \(P(d) = \frac{\lambda^d e^{-\lambda}}{d!}\). También de cola ligera, surge del modelo \(G(n,p)\) con \(p\) pequeña. Apropiada para redes muy esparsas sin estructura especial. Ventaja: un solo parámetro. Desventaja: asume independencia entre grados.
Ley de potencias con corte exponencial: \(P(d) \propto d^{-\alpha} e^{-d/\kappa}\). Combina el comportamiento power-law para grados bajos/medios con una caída exponencial para grados muy altos. Más realista para redes donde los hubs están limitados por capacidad o costo. Ventaja: flexibilidad. Desventaja: mayor complejidad de ajuste.
¿Para qué sirve modelar la distribución del grado? Permite entender la estructura de heterogeneidad de la red, comparar redes empíricas con modelos nulos, y tiene implicaciones prácticas: redes power-law son robustas a fallos aleatorios pero vulnerables a ataques a hubs.
deg_data <- degree(g_sym)
deg_data <- deg_data[deg_data > 0]
# Histograma con ajustes
x_seq <- seq(1, max(deg_data), length.out = 200)
hist(deg_data, breaks = 15, freq = FALSE,
col = "lightgray", border = "white",
main = "Distribución del Grado — Red de Conflictos Simetrizada",
xlab = "Grado", ylab = "Densidad")
# Ajuste exponencial
lambda_exp <- 1/mean(deg_data)
lines(x_seq, dexp(x_seq, rate = lambda_exp), col = "#E74C3C", lwd = 2)
# Ajuste log-normal
mu_ln <- mean(log(deg_data))
sd_ln <- sd(log(deg_data))
lines(x_seq, dlnorm(x_seq, meanlog = mu_ln, sdlog = sd_ln), col = "#2980B9", lwd = 2)
# Ajuste Poisson
lambda_poi <- mean(deg_data)
lines(0:max(deg_data), dpois(0:max(deg_data), lambda = lambda_poi),
col = "#27AE60", lwd = 2, type = "b", pch = 20, cex = 0.5)
legend("topright",
legend = c("Exponencial", "Log-normal", "Poisson"),
col = c("#E74C3C", "#2980B9", "#27AE60"),
lwd = 2, bty = "n")# Gráfico log-log para evaluar power law
deg_table <- table(deg_data)
plot(as.numeric(names(deg_table)), as.numeric(deg_table),
log = "xy", pch = 19, col = "#8E44AD",
xlab = "Grado (log)", ylab = "Frecuencia (log)",
main = "Distribución del grado en escala log-log")
# Ajuste lineal en log-log (power law)
lm_fit <- lm(log(as.numeric(deg_table)) ~ log(as.numeric(names(deg_table))))
abline(lm_fit, col = "#E74C3C", lwd = 2, lty = 2)
legend("topright", legend = paste("Power law: alpha =",
round(-coef(lm_fit)[2], 3)), col = "#E74C3C", lty = 2, bty = "n")## === Evaluación de ajuste (AIC aproximado) ===
# Log-verosimilitud log-normal
ll_ln <- sum(dlnorm(deg_data, mu_ln, sd_ln, log = TRUE))
cat("Log-verosimilitud Log-normal:", round(ll_ln, 2), "| AIC:", round(-2*ll_ln + 4, 2), "\n")## Log-verosimilitud Log-normal: -188.34 | AIC: 380.68
# Log-verosimilitud exponencial
ll_exp <- sum(dexp(deg_data, lambda_exp, log = TRUE))
cat("Log-verosimilitud Exponencial:", round(ll_exp, 2), "| AIC:", round(-2*ll_exp + 2, 2), "\n")## Log-verosimilitud Exponencial: -205.43 | AIC: 412.86
# Log-verosimilitud Poisson
ll_poi <- sum(dpois(deg_data, lambda_poi, log = TRUE))
cat("Log-verosimilitud Poisson:", round(ll_poi, 2), "| AIC:", round(-2*ll_poi + 2, 2), "\n")## Log-verosimilitud Poisson: -261.36 | AIC: 524.73
Interpretación: La distribución log-normal ofrece el mejor ajuste (menor AIC) para la distribución del grado en esta red de conflictos. Esto sugiere que el proceso de formación de conflictos sigue una dinámica multiplicativa: cada conflicto tiende a generar más conflictos de manera proporcional, pero con una cota superior natural. El exponente \(\alpha\) estimado para la ley de potencias indica una cola relativamente ligera para una red de este tipo.
PageRank: Asigna importancia a un nodo proporcionalmente a la importancia de sus vecinos, con un factor de amortiguación \(\alpha\) (usualmente 0.85). Originalmente diseñado para la web: una página es importante si muchas páginas importantes la enlazan. Prioriza nodos en el “núcleo” de la red con muchos vecinos influyentes.
Centralidad de Katz: \(C_K(i) = \sum_{k=1}^{\infty} \sum_j \beta^k (A^k)_{ji}\), con \(\beta < 1/\lambda_{\max}\). Suma todos los caminos ponderados exponencialmente por longitud. A diferencia del eigenvector, no requiere que el nodo esté en el componente gigante y asigna un crédito base a todos los nodos. Útil para redes con componentes múltiples.
Centralidad armónica (Harmonic): \(C_H(i) = \sum_{j \neq i} \frac{1}{d(i,j)}\), donde \(d(i,j) = \infty\) contribuye con cero. Variante de la cercanía robusta a grafos desconectados; prioriza nodos que pueden alcanzar al resto eficientemente.
VoteRank: Algoritmo iterativo de selección de spreaders: en cada ronda se selecciona el nodo con mayor “votos” (suma de capacidades de vecinos), luego se penalizan sus vecinos reduciendo su capacidad de voto. Identifica un conjunto de spreaders independientes (no vecinos entre sí), útil para estrategias de difusión o vacunación.
pr <- page_rank(g_sym, damping = 0.85)$vector
katz <- alpha_centrality(g_sym, alpha = 0.5/max(eigen(as_adjacency_matrix(g_sym, sparse=FALSE))$values))
harm <- harmonic_centrality(g_sym)
# VoteRank: implementación manual
voterank_manual <- function(g, n_select = NULL) {
n <- vcount(g)
if (is.null(n_select)) n_select <- n
caps <- rep(1, n)
selected <- integer(0)
for (iter in seq_len(n_select)) {
scores <- sapply(seq_len(n), function(i) {
if (i %in% selected) return(-Inf)
nbrs <- as.integer(neighbors(g, i))
sum(caps[nbrs])
})
best <- which.max(scores)
if (scores[best] <= 0) break
selected <- c(selected, best)
# Penalizar vecinos del seleccionado
nbrs_best <- as.integer(neighbors(g, best))
caps[nbrs_best] <- pmax(0, caps[nbrs_best] - 1/degree(g)[nbrs_best])
caps[best] <- 0
}
selected
}
vr <- voterank_manual(g_sym, n_select = min(20, vcount(g_sym)))
voterank_score <- rep(0, vcount(g_sym))
if (length(vr) > 0) voterank_score[vr] <- rev(seq_along(vr))
# Rankings
rank_pr <- rank(-pr)
rank_katz <- rank(-katz)
rank_harm <- rank(-harm)
rank_vr <- rank(-voterank_score)
rank_bet <- rank(-bet_sym)
rank_deg <- rank(-deg_sym)
tabla_central_ext <- data.frame(
Pais = V(g_sym)$name,
PageRank = round(pr, 5),
Katz = round(katz, 4),
Harmonic = round(harm, 3),
VoteRank = voterank_score,
Rank_PR = rank_pr,
Rank_Katz = rank_katz,
Rank_Harm = rank_harm,
Rank_Grado = rank_deg
) %>% arrange(Rank_PR)
knitr::kable(head(tabla_central_ext, 15),
caption = "Top 15 países por PageRank — medidas adicionales")| Pais | PageRank | Katz | Harmonic | VoteRank | Rank_PR | Rank_Katz | Rank_Harm | Rank_Grado | |
|---|---|---|---|---|---|---|---|---|---|
| IRQ | IRQ | 0.07717 | -1047.8345 | 42.782 | 20 | 1 | 91 | 2 | 1.0 |
| JOR | JOR | 0.04366 | -330.2916 | 46.279 | 19 | 2 | 85 | 1 | 2.0 |
| USA | USA | 0.03498 | -600.9932 | 36.174 | 18 | 3 | 89 | 3 | 3.0 |
| TUR | TUR | 0.02854 | -696.3711 | 28.163 | 9 | 4 | 90 | 30 | 11.0 |
| UGA | UGA | 0.02703 | -10.1631 | 24.241 | 17 | 5 | 42 | 41 | 4.5 |
| CHN | CHN | 0.02626 | -193.6679 | 20.917 | 16 | 6 | 80 | 56 | 4.5 |
| PRK | PRK | 0.02286 | -223.2249 | 24.625 | 7 | 7 | 82 | 39 | 6.5 |
| NIG | NIG | 0.02090 | -10.4817 | 26.428 | 15 | 8 | 44 | 33 | 11.0 |
| VEN | VEN | 0.02018 | -37.2523 | 25.102 | 8 | 9 | 51 | 37 | 25.0 |
| IRN | IRN | 0.01816 | -514.8840 | 25.963 | 2 | 10 | 88 | 36 | 11.0 |
| NIC | NIC | 0.01770 | 0.8898 | 12.715 | 14 | 11 | 14 | 75 | 25.0 |
| RWA | RWA | 0.01669 | -1.4178 | 24.024 | 6 | 12 | 27 | 45 | 11.0 |
| GRC | GRC | 0.01644 | -413.6939 | 28.996 | 0 | 13 | 87 | 20 | 25.0 |
| DRC | DRC | 0.01549 | -4.3024 | 26.254 | 13 | 14 | 35 | 34 | 11.0 |
| THI | THI | 0.01432 | 0.2410 | 10.713 | 4 | 15 | 21 | 80 | 54.5 |
# Correlación entre rankings
cor_matrix <- cor(data.frame(
Grado = rank_deg,
Betw = rank_bet,
PR = rank_pr,
Katz = rank_katz,
Harm = rank_harm
), method = "spearman")
corrplot(cor_matrix,
method = "color",
addCoef.col = "black",
tl.col = "black",
col = colorRampPalette(c("#2980B9","white","#E74C3C"))(200),
title = "Correlación de Spearman entre rankings de centralidad",
mar = c(0,0,2,0))V(g_sym)$pr_size <- 3 + pr * 1000
set.seed(42)
plot(g_sym,
vertex.size = V(g_sym)$pr_size,
vertex.color = V(g_sym)$color,
vertex.label = ifelse(rank_pr <= 10, V(g_sym)$name, NA),
vertex.label.cex = 0.65,
edge.color = adjustcolor("gray50", alpha.f = 0.3),
main = "PageRank — Red de Conflictos Simetrizada",
sub = "Tamaño de nodo proporcional al PageRank | Top 10 etiquetados",
layout = layout_with_fr)Interpretación de diferencias en rankings:
La alta correlación de Spearman entre PageRank, Katz y centralidad armónica confirma que estas medidas capturan aspectos similares de la importancia estructural. Sin embargo, existen diferencias importantes: