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:

library(igraph)
library(knitr)
library(RColorBrewer)
library(ggplot2)
library(ggraph)
library(tidygraph)
library(dplyr)
library(corrplot)

Punto 1. Grados a partir de la matriz de adyacencia

Enunciado

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}}\).

Demostración

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\)


Punto 2. Media global de las interacciones

La media global se define como: \[\bar{y} = \frac{1}{n(n-1)} \sum_{i,j:\, i\neq j} y_{i,j}\]

a) Media global = media de triángulos superior e inferior (red no dirigida)

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\]

b) La media global es la densidad de la red

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.

c) \((n-1)\bar{y} = \bar{d}^{\text{out}} = \bar{d}^{\text{in}}\)

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\)


Punto 3. Grafo círculo y grafo estrella

a) Grafo círculo de orden \(n\): grado promedio \(\bar{d} = 2\)

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\]

b) Grafo estrella de orden \(n\): grado promedio \(\bar{d} = 2\frac{n-1}{n} \to 2\)

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\)

Verificación computacional

# 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
cat("Fórmula 2*(n-1)/n con n=9:", 2*(9-1)/9, "\n")
## 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)


Punto 4. Caminatas, senderos, circuitos y ciclos

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:

  • Caminata: secuencia de nodos donde cada par consecutivo es una arista (se permiten repeticiones de nodos y aristas).
  • Sendero (trail): caminata sin aristas repetidas.
  • Camino (path): caminata sin nodos repetidos.
  • Circuito: caminata cerrada (inicia y termina en el mismo nodo) sin aristas repetidas.
  • Ciclo: camino cerrado (inicia y termina en el mismo nodo) sin nodos internos repetidos.
# 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
cat("\n=== Secuencia b: 2-1-6-3-4-1-5 ===\n")
## 
## === 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
cat("\n=== Secuencia c: 2-1-2-5-1-4 ===\n")
## 
## === 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

Clasificación

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.

  • Caminata (secuencia válida de aristas)
  • Sendero (sin aristas repetidas)
  • Camino (sin nodos repetidos)
  • ❌ No es circuito ni ciclo (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.

  • No es caminata (arista inexistente)

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.

  • No es caminata (arista \(\{1,4\}\) inexistente)

Punto 5. Análisis de comercio internacional (comtrade)

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
cat("Número de países:", n, "\n")
## Número de países: 30

Media global \(\bar{y}\)

# 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.

Promedios fila \(\bar{y}_{i\bullet}\) (sociabilidad)

# 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.

Promedios columna \(\bar{y}_{\bullet j}\) (popularidad)

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 los promedios (tendencia local)

cat("Media de promedios fila:   ", round(mean(y_fila), 6), "\n")
## Media de promedios fila:    0.037784
cat("Media de promedios columna:", round(mean(y_col), 6), "\n")
## Media de promedios columna: 0.037784
cat("Media global y_bar:        ", round(y_bar, 6), "\n")
## 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.

Desviación estándar (heterogeneidad local)

cat("DE de promedios fila:   ", round(sd(y_fila), 6), "\n")
## DE de promedios fila:    0.0302
cat("DE de promedios columna:", round(sd(y_col), 6), "\n")
## 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.

Correlación y dispersograma

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.


Punto 6. Análisis de conflictos internacionales

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
cat("Tamaño (conflictos):", ecount(g_conflict), "\n")
## Tamaño (conflictos): 203

a) Visualización decorada de la red de conflictos

# 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))

b) Media global

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
cat("Densidad de la red:", round(graph.density(g_conflict), 6), "\n")
## 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.

c) Distribución del out-degree e in-degree

out_deg <- degree(g_conflict, mode = "out")
in_deg  <- degree(g_conflict, mode = "in")

cat("--- Out-degree ---\n")
## --- Out-degree ---
cat("Media:", round(mean(out_deg), 3), "| DE:", round(sd(out_deg), 3), "\n")
## Media: 1.562 | DE: 3.589
cat("--- In-degree ---\n")
## --- In-degree ---
cat("Media:", round(mean(in_deg), 3), "| DE:", round(sd(in_deg), 3), "\n")
## 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.

d) Correlación out-degree vs. in-degree

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.

e) Países más activos

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")
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

Punto 7. Centralidad en cuatro grafos

# 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)
}
tabla_centralidad(g7_1, "Grafo 1 - Cruz")

=== Grafo 1 - Cruz ===

Centralidades — 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
Resumen — Grafo 1 - Cruz
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
tabla_centralidad(g7_2, "Grafo 2 - Tridente")

=== Grafo 2 - Tridente ===

Centralidades — 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
Resumen — Grafo 2 - Tridente
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
tabla_centralidad(g7_3, "Grafo 3 - Camino lineal")

=== Grafo 3 - Camino lineal ===

Centralidades — 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
Resumen — Grafo 3 - Camino lineal
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
tabla_centralidad(g7_4, "Grafo 4 - Cola+Ciclo")

=== Grafo 4 - Cola+Ciclo ===

Centralidades — 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
Resumen — Grafo 4 - Cola+Ciclo
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:

  • Grado: En grafos con estructura estrella/cruz, el nodo central domina; en el camino lineal los extremos tienen grado 1.
  • Cercanía: Los nodos centrales tienen mayor cercanía al poder alcanzar al resto en pocos pasos.
  • Intermediación: Los nodos puente (que conectan partes de la red) tienen alta intermediación; el nodo central de la cruz concentra toda la intermediación.
  • Centralidad propia (eigenvector): Refleja la importancia de estar conectado a nodos importantes; en estructuras estrella el hub tiene centralidad propia máxima.

Punto 8. Caracterización de la red de conflictos (simetrizada)

# 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
cat("Tamaño:", ecount(g_sym), "\n")
## Tamaño: 160

a) Visualización decorada

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)

b) Caracterización local y estructural

cat("=== DISTANCIA ===\n")
## === DISTANCIA ===
diam <- diameter(g_sym, weights = NA)
avg_path <- mean_distance(g_sym, directed = FALSE)
cat("Diámetro:", diam, "\n")
## Diámetro: 9
cat("Longitud media de camino:", round(avg_path, 4), "\n")
## Longitud media de camino: 4.7558
cat("=== CENTRALIDAD ===\n")
## === 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")
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
cat("=== COHESIÓN Y CONECTIVIDAD ===\n")
## === COHESIÓN Y CONECTIVIDAD ===
cat("Densidad:", round(graph.density(g_sym), 4), "\n")
## Densidad: 0.0391
cat("Transitividad (clustering global):", round(transitivity(g_sym, type = "global"), 4), "\n")
## Transitividad (clustering global): 0.1634
cat("Clustering promedio (local):", round(transitivity(g_sym, type = "average"), 4), "\n")
## Clustering promedio (local): 0.4243
cat("¿Conectado?:", is.connected(g_sym), "\n")
## 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
cat("Número de componentes:", components(g_sym)$no, "\n")
## Número de componentes: 5
comp <- components(g_sym)
cat("Tamaño del componente mayor:", max(comp$csize), "\n")
## Tamaño del componente mayor: 83
cat("=== DETECCIÓN DE COMUNIDADES ===\n")
## === 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:

  • La red simetrizada es relativamente densa para ser una red de conflictos, con un diámetro pequeño indicando alta conectividad entre los países involucrados.
  • El coeficiente de clustering indica la existencia de subgrupos de países con conflictos mutuos frecuentes, probablemente reflejando disputas regionales.
  • La detección de comunidades revela bloques geopolíticos con patrones de conflicto similares.

Punto 9. Síntesis de Capítulos 6, 8 y 9 de Luke (2015)

Capítulo 6: Gráficos Avanzados de Redes

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.

Réplica: Diagrama de cuerda con circlize

if (!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")
}

Réplica: Heatmap de la red

# 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)

Capítulo 8: Subgrupos

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.

Réplica con red de conflictos

cat("=== K-CORES ===\n")
## === K-CORES ===
coreness_vals <- coreness(g_sym)
cat("Distribución de coreness:\n")
## Distribución de coreness:
print(table(coreness_vals))
## 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
cat("Países:", paste(V(g_core)$name, collapse = ", "), "\n")
## 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"))

cat("=== COMUNIDADES ===\n")
## === 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)")
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

Capítulo 9: Redes de Afiliación

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 de réplica

# 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")

# Proyección sobre países
proj_paises <- bipartite.projection(g_bip)[[1]]
## 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.
cat("\nProyección sobre países:\n")
## 
## Proyección sobre países:
cat("Nodos:", vcount(proj_paises), "| Aristas:", ecount(proj_paises), "\n")
## 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")


Punto 10. Distribuciones del grado

Descripción de las distribuciones

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.

Ejemplo: Ajuste de distribuciones a la red de conflictos

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")

cat("=== Evaluación de ajuste (AIC aproximado) ===\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.


Punto 11. Medidas de centralidad adicionales

Descripción de las medidas

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.

Cálculo y comparación en la red de conflictos

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")
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:

  • PageRank vs. Grado: Algunos países con grado moderado suben en PageRank si están conectados a países muy activos en conflictos — son amplificadores de la red de conflictos.
  • Centralidad armónica vs. Betweenness: Países periféricos que conectan regiones tienen alta betweenness pero menor centralidad armónica; países bien embebidos en el núcleo tienen el patrón inverso.
  • VoteRank: Identifica los spreaders más efectivos para propagar un conflicto en la red, que no necesariamente coinciden con los nodos de mayor grado (evita seleccionar nodos vecinos entre sí).

Referencias

  • Kolaczyk, E. D., & Csárdi, G. (2020). Statistical Analysis of Network Data with R (2nd ed.). Springer.
  • Luke, D. A. (2015). A User’s Guide to Network Analysis in R. Springer UseR! Series. Capítulos 6, 8 y 9.
  • Csárdi, G., & Nepusz, T. (2006). The igraph software package for complex network research. InterJournal, Complex Systems, 1695.
  • Feld, S. (1991). Why your friends have more friends than you do. American Journal of Sociology, 96(6), 1464-1477.
  • Barabási, A.-L., & Albert, R. (1999). Emergence of scaling in random networks. Science, 286(5439), 509-512.