Una innovación fundamental en el modelado de datos relacionales es la incorporación de variables latentes, que representan características no observadas de los vértices. Estas variables, aunque no se observan directamente, desempeñan un papel crucial en la determinación de las probabilidades de interacción en el grafo.
Bajo este enfoque, las entradas de la matriz de adyacencia \(\mathbf{Y} = [y_{i,j}]\), que representa el grafo \(G = (V, E)\), se modelan como condicionalmente independientes, con una distribución Bernoulli dada por: \[ y_{i,j} \mid \mu, \boldsymbol{\beta}, \mathbf{x}_{i,j}, \boldsymbol{u}_i, \boldsymbol{u}_j \overset{\text{ind}}{\sim} \textsf{Bernoulli}\left( g\left( \mu + \boldsymbol{\beta}^\textsf{T}\mathbf{x}_{i,j} + \alpha(\boldsymbol{u}_i, \boldsymbol{u}_j) \right) \right), \] donde:
Para implementar este modelo, es necesario especificar:
Dado que estos modelos tienen una estructura jerárquica, un enfoque Bayesiano resulta natural. Este enfoque permite modelar la incertidumbre y priorizar las distribuciones de los parámetros mediante el uso de métodos de inferencia basados en cadenas de Markov de Monte Carlo (Markov chain Monte Carlo, MCMC) o métodos variacionales, los cuales son herramientas eficaces para explorar la distribución posterior de los parámetros y las variables latentes.
El modelo de distancia estándar se define como: \[ y_{i,j} \mid \mu, \boldsymbol{u}_i, \boldsymbol{u}_j \overset{\text{ind}}{\sim} \textsf{Bernoulli}\left( g\left( \mu - \| \boldsymbol{u}_i - \boldsymbol{u}_j \| \right) \right), \] donde:
Este modelo, introducido por Hoff, Raftery y Handcock (2002), es ampliamente utilizado en el análisis de redes sociales para capturar la proximidad latente entre nodos, donde la probabilidad de conexión decrece a medida que aumenta la distancia euclidiana entre sus posiciones latentes.
Hoff, P. D., Raftery, A. E., & Handcock, M. S. (2002). Latent space approaches to social network analysis. Journal of the American Statistical Association, 97(460), 1090-1098.
Se examinan las relaciones matrimoniales y comerciales como mecanismos fundamentales para la estructuración y consolidación del poder entre las familias élite de Florencia durante el Renacimiento.
Mediante un enfoque histórico-social, Padgett explora cómo estas conexiones moldearon la formación de alianzas estratégicas, fortalecieron la perpetuación de jerarquías sociales y dinamizaron las relaciones políticas en un periodo crucial de transformación social y económica.
Padgett, J. F. (1994). Marriage and Elite Structure in Renaissance Florence, 1282-1500. Ponencia presentada en la reunión de la Social Science History Association, Atlanta, Georgia.
# librerías
suppressMessages(suppressWarnings(library(igraph)))
suppressMessages(suppressWarnings(library(latentnet)))
# datos
data(florentine, package = "ergm")
flomarriage
## Network attributes:
## vertices = 16
## directed = FALSE
## hyper = FALSE
## loops = FALSE
## multiple = FALSE
## bipartite = FALSE
## total edges= 20
## missing edges= 0
## non-missing edges= 20
##
## Vertex attribute names:
## priorates totalties vertex.names wealth
##
## No edge attributes
class(flomarriage)
## [1] "network"
# matriz de adyacencia
# remover vértice 12 porque es aislado (distancia Inf)
A <- network::as.matrix.network.adjacency(x = flomarriage)
(index_rm <- which(colSums(A) == 0))
## Pucci
## 12
A <- A[-index_rm,-index_rm]
class(A)
## [1] "matrix" "array"
# variable exogena: riqueza
wealth <- flomarriage %v% "wealth"
(wealth <- wealth[-index_rm])
## [1] 10 36 55 44 20 32 8 42 103 48 49 27 10 146 48
# tipo igraph
g <- igraph::graph_from_adjacency_matrix(adjmatrix = A, mode = "undirected")
class(g)
## [1] "igraph"
# tipo network
flomarriage <- network::as.network.matrix(A)
class(flomarriage)
## [1] "network"
# gráfico
set.seed(42)
plot(g, vertex.size = 1.2*sqrt(wealth), vertex.label = NA, main = "Familias Florentinas")
# ajuste del modelo
# d = 2 : dimensión latente bidimensional
# G = 0 : sin factores de agrupamiento
# Ajustar el modelo con parámetros específicos
fit <- ergmm(
formula = flomarriage ~ euclidean(d = 2, G = 0),
seed = 42,
control = ergmm.control(
burnin = 1000, # Iteraciones de burn-in
interval = 10, # Thinning: guardar 1 muestra cada 5 iteraciones
sample.size = 5000 # Total de muestras efectivas para inferencia
)
)
# resumen del ajuste
summary(fit)
## NOTE: It is not certain whether it is appropriate to use latentnet's BIC to select latent space dimension, whether or not to include actor-specific random effects, and to compare clustered models with the unclustered model.
##
## ==========================
## Summary of model fit
## ==========================
##
## Formula: flomarriage ~ euclidean(d = 2, G = 0)
## Attribute: edges
## Model: Bernoulli
## MCMC sample of size 5000, draws are 10 iterations apart, after burnin of 1000 iterations.
## Covariate coefficients posterior means:
## Estimate 2.5% 97.5% 2*min(Pr(>0),Pr(<0))
## (Intercept) 5.0862 2.5079 8.1521 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Overall BIC: 243.5475
## Likelihood BIC: 81.5672
## Latent space/clustering BIC: 161.9803
##
## Covariate coefficients MKL:
## Estimate
## (Intercept) 3.107105
suppressMessages(suppressWarnings(library(ggplot2)))
# Datos simulados (asegúrate de usar tu propio `fit$sample$lpY`)
x <- c(fit$sample$lpY)
iterations <- 1:length(x)
# Crear un dataframe para ggplot
data <- data.frame(Iteration = iterations, LogLikelihood = x)
# Calcular estadísticas
mean_x <- mean(x)
quantiles_x <- quantile(x, c(0.025, 0.975))
# Generar el gráfico
ggplot(data, aes(x = Iteration, y = LogLikelihood)) +
geom_point(alpha = 0.3, size = 0.5, color = "black") + # Puntos con transparencia
geom_hline(yintercept = mean_x, color = "blue", linetype = "dashed", linewidth = 1) + # Línea de la media
geom_hline(yintercept = quantiles_x, color = "red", linetype = "dotted", linewidth = 1) + # Líneas de los cuantiles
labs(x = "Iteración", y = "Log-verosimilitud", title = "") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5)) # Centrar el título si es necesario
# Cargar librerías con supresión de mensajes y advertencias
suppressMessages(suppressWarnings(library(ggplot2)))
suppressMessages(suppressWarnings(library(gridExtra)))
# Datos simulados (reemplaza con fit$sample$beta)
x <- c(fit$sample$beta)
iterations <- 1:length(x)
data_chain <- data.frame(Iteration = iterations, Beta = x)
# Estadísticas
mean_x <- mean(x)
quantiles_x <- quantile(x, c(0.025, 0.975))
# Gráfico de la cadena
p_chain <- ggplot(data_chain, aes(x = Iteration, y = Beta)) +
geom_point(alpha = 0.3, size = 0.5, color = "black") + # Puntos con transparencia
geom_hline(yintercept = mean_x, color = "blue", linetype = "dashed", linewidth = 1) + # Línea de la media
geom_hline(yintercept = quantiles_x, color = "red", linetype = "dotted", linewidth = 1) + # Líneas de los cuantiles
labs(x = "Iteración", y = expression(beta), title = "Cadena") +
theme_minimal()
# Histograma de la distribución marginal
data_hist <- data.frame(Beta = x)
p_hist <- ggplot(data_hist, aes(x = Beta)) +
geom_histogram(aes(y = ..density..), bins = 30, fill = "gray90", color = "gray90") + # Histograma
geom_vline(xintercept = mean_x, color = "blue", linetype = "dashed", linewidth = 1) + # Línea de la media
geom_vline(xintercept = quantiles_x, color = "red", linetype = "dotted", linewidth = 1) + # Líneas de los cuantiles
labs(x = expression(beta), y = "Densidad", title = "Distr. marginal") +
theme_minimal()
# Combinar los gráficos
grid.arrange(p_chain, p_hist, ncol = 2)
## Warning: The dot-dot notation (`..density..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(density)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
# media posterior del intercepto
(beta_pm <- mean(fit$sample$beta))
## [1] 5.086211
# probabilidad de interacción basal
1/(1 + exp(-beta_pm))
## [1] 0.9938566
# Cargar librerías con supresión de mensajes y advertencias
suppressMessages(suppressWarnings(library(ggplot2)))
suppressMessages(suppressWarnings(library(gridExtra)))
suppressMessages(suppressWarnings(library(MCMCpack)))
# Transformación de Procrustes
B <- dim(fit$sample$Z)[1] # Número de muestras MCMC
n <- dim(fit$sample$Z)[2] # Número de vértices
d <- dim(fit$sample$Z)[3] # Dimensión latente
U0 <- scale(fit$mcmc.mle$Z, scale = TRUE, center = TRUE)
U.array <- array(data = NA, dim = c(B, n, d))
for (b in 1:B) {
U.array[b,,] <- MCMCpack::procrustes(
X = scale(fit$sample$Z[b,,], scale = TRUE, center = TRUE),
Xstar = U0,
translation = TRUE,
dilation = TRUE
)$X.new
}
U.pm <- apply(X = U.array, MARGIN = c(2, 3), FUN = mean)
# Colores (Asegurando que los valores estén en [0, 1])
rr <- atan2(U0[, 2], U0[, 1])
rr <- (rr - min(rr)) / (max(rr) - min(rr)) # Escalar a [0,1]
gg <- 1 - rr # Complemento
bb <- (U0[, 2]^2 + U0[, 1]^2)
bb <- (bb - min(bb)) / (max(bb)) # Escalar a [0,1]
aa <- 0.4 # Transparencia fija
# Adelgazamiento de la cadena
nthin <- 10
index_thin <- seq(from = nthin, to = B, by = nthin)
thinned_data <- do.call(rbind, lapply(index_thin, function(b) {
data.frame(
Dim1 = U.array[b,,1],
Dim2 = U.array[b,,2],
Vertex = factor(1:n),
Color = rgb(rr, gg, bb, alpha = aa)
)
}))
# Datos para las posiciones promedio
U_pm_df <- data.frame(
Dim1 = U.pm[, 1],
Dim2 = U.pm[, 2],
Vertex = 1:n,
Color = rgb(rr, gg, bb, alpha = 1)
)
# Primer panel: Con trayectorias y etiquetas
p1 <- ggplot() +
geom_point(data = thinned_data, aes(x = Dim1, y = Dim2, color = Color),
shape = 15, size = 0.8) + # Puntos de trayectorias
geom_text(data = U_pm_df, aes(x = Dim1, y = Dim2, label = Vertex),
color = "black", size = 3, fontface = "bold") + # Etiquetas en negro
scale_color_identity() +
labs(
x = "Dimensión 1",
y = "Dimensión 2",
title = "Posiciones latentes con trayectorias"
) +
theme_minimal()
# Segundo panel: Solo posiciones promedio con etiquetas pequeñas
p2 <- ggplot() +
geom_point(data = U_pm_df, aes(x = Dim1, y = Dim2, color = "blue"),
size = 3) + # Puntos tradicionales
geom_text(data = U_pm_df, aes(x = Dim1, y = Dim2, label = Vertex),
color = "black", size = 3, vjust = 1.5) + # Etiquetas pequeñas debajo de los puntos
scale_color_identity() +
labs(
x = "Dimensión 1",
y = "Dimensión 2",
title = "Posiciones promedio con etiquetas"
) +
theme_minimal()
# Combinar gráficos en dos paneles
grid.arrange(p1, p2, ncol = 2)
# función expit
expit <- function(x) 1/(1+exp(-x))
# probabilidades de interacción (media posterior)
Pi <- matrix(0, n, n)
for (b in 1:B) {
bet <- fit$sample$beta[b]
for (i in 1:(n-1)) {
for (j in (i+1):n) {
lat <- sqrt(sum((fit$sample$Z[b,i,] - fit$sample$Z[b,j,])^2))
Pi[i,j] <- Pi[j,i] <- Pi[i,j] + expit(bet - lat)/B
}
}
}
# gráfico
rownames(A) <- colnames(A) <- 1:n
par(mfrow = c(1,2))
corrplot::corrplot(corr = A, type = "full", col.lim = c(0,1), method = "shade", addgrid.col = "gray90", tl.col = "black")
corrplot::corrplot(corr = Pi, type = "full", col.lim = c(0,1), method = "shade", addgrid.col = "gray90", tl.col = "black")
# bondad de ajuste
B <- dim(fit$sample$Z)[1]
n <- dim(fit$sample$Z)[2]
d <- dim(fit$sample$Z)[3]
stat <- matrix(NA, B, 6)
set.seed(42)
for (b in 1:B) {
# intercepto
bet <- fit$sample$beta[b]
# simular datos
Ar <- matrix(0, n, n)
for (i in 1:(n-1)) {
for (j in (i+1):n){
lat <- sqrt(sum((fit$sample$Z[b,i,] - fit$sample$Z[b,j,])^2))
Ar[i,j] <- Ar[j,i] <- rbinom(n = 1, size = 1, prob = expit(bet - lat))
}
}
gr <- igraph::graph_from_adjacency_matrix(adjmatrix = Ar, mode = "undirected")
# calcular estadísticos
stat[b,1] <- igraph::edge_density(graph = gr, loops = F)
stat[b,2] <- igraph::transitivity(graph = gr, type = "global")
stat[b,3] <- igraph::assortativity_degree(graph = gr, directed = F)
stat[b,4] <- igraph::mean_distance(graph = gr, directed = F)
stat[b,5] <- mean(igraph::degree(graph = gr))
stat[b,6] <- sd(igraph::degree(graph = gr))
}
# Cargar librerías con supresión de mensajes y advertencias
suppressMessages(suppressWarnings(library(ggplot2)))
suppressMessages(suppressWarnings(library(gridExtra)))
# valores observados
dens_obs <- igraph::edge_density(graph = g, loops = F)
tran_obs <- igraph::transitivity(graph = g, type = "global")
asso_obs <- igraph::assortativity_degree(graph = g, directed = F)
mdis_obs <- igraph::mean_distance(graph = g, directed = F)
mdeg_obs <- mean(igraph::degree(graph = g))
sdeg_obs <- sd(igraph::degree(graph = g))
# Crear un dataframe con las estadísticas simuladas
stat_df <- data.frame(
Densidad = stat[,1],
Transitividad = stat[,2],
Asortatividad = stat[,3],
DistanciaProm = stat[,4],
GradoProm = stat[,5],
GradoDE = stat[,6]
)
# Crear un dataframe con los valores observados
obs_values <- data.frame(
Estadístico = c("Densidad", "Transitividad", "Asortatividad", "DistanciaProm", "GradoProm", "GradoDE"),
Observado = c(dens_obs, tran_obs, asso_obs, mdis_obs, mdeg_obs, sdeg_obs)
)
# Calcular intervalos de confianza
ci_values <- data.frame(
Estadístico = c("Densidad", "Transitividad", "Asortatividad", "DistanciaProm", "GradoProm", "GradoDE"),
CI_Lower = c(quantile(stat[,1], 0.025), quantile(stat[,2], 0.025), quantile(stat[,3], 0.025),
quantile(stat[,4], 0.025), quantile(stat[,5], 0.025), quantile(stat[,6], 0.025)),
CI_Upper = c(quantile(stat[,1], 0.975), quantile(stat[,2], 0.975), quantile(stat[,3], 0.975),
quantile(stat[,4], 0.975), quantile(stat[,5], 0.975), quantile(stat[,6], 0.975))
)
# Función para crear cada histograma
create_histogram <- function(data, column, obs, ci_lower, ci_upper, x_label, title) {
ggplot(data, aes(x = .data[[column]])) +
geom_histogram(aes(y = ..density..), bins = 30, fill = "gray90", color = "gray90") +
geom_vline(xintercept = obs, color = "blue", linetype = "dashed", linewidth = 1) + # Línea del valor observado
geom_vline(xintercept = c(ci_lower, ci_upper), color = "red", linetype = "dotted", linewidth = 1) + # Líneas IC
labs(x = x_label, y = "Densidad", title = title) +
theme_minimal()
}
# Crear gráficos individuales
p1 <- create_histogram(stat_df, "Densidad", dens_obs, ci_values$CI_Lower[1], ci_values$CI_Upper[1], "Densidad", "Densidad")
p2 <- create_histogram(stat_df, "Transitividad", tran_obs, ci_values$CI_Lower[2], ci_values$CI_Upper[2], "Transitividad", "Transitividad")
p3 <- create_histogram(stat_df, "Asortatividad", asso_obs, ci_values$CI_Lower[3], ci_values$CI_Upper[3], "Asortatividad", "Asortatividad")
p4 <- create_histogram(stat_df, "DistanciaProm", mdis_obs, ci_values$CI_Lower[4], ci_values$CI_Upper[4], "Distancia prom.", "Distancia promedio")
p5 <- create_histogram(stat_df, "GradoProm", mdeg_obs, ci_values$CI_Lower[5], ci_values$CI_Upper[5], "Grado prom.", "Grado promedio")
p6 <- create_histogram(stat_df, "GradoDE", sdeg_obs, ci_values$CI_Lower[6], ci_values$CI_Upper[6], "Grado DE", "Grado DE")
# Combinar gráficos en un diseño de 2x3
grid.arrange(p1, p2, p3, p4, p5, p6, ncol = 3)
El modelo que incorpora covariables y proximidad latente se define como:
\[ y_{i,j} \mid \mu, \boldsymbol{\beta}, \mathbf{x}_{i,j}, \boldsymbol{u}_i, \boldsymbol{u}_j \overset{\text{ind}}{\sim} \textsf{Bernoulli}\left( \textsf{expit}\left( \mu + \boldsymbol{\beta}^\textsf{T} \mathbf{x}_{i,j} - \| \boldsymbol{u}_i - \boldsymbol{u}_j \| \right) \right), \]
donde:
Este modelo, propuesto por Hoff, Raftery y Handcock (2002), combina la representación de espacio latente con efectos de covariables exógenas, permitiendo capturar la probabilidad de interacción entre nodos como una combinación de:
Es ampliamente utilizado para modelar redes sociales donde la distancia latente y las características de los nodos influyen conjuntamente en las conexiones.
Hoff, P. D., Raftery, A. E., & Handcock, M. S. (2002). Latent space approaches to social network analysis. Journal of the American Statistical Association, 97(460), 1090-1098.
# librerias
suppressMessages(suppressWarnings(library(igraph)))
suppressMessages(suppressWarnings(library(latentnet)))
# datos
data(florentine, package = "ergm")
flomarriage
## Network attributes:
## vertices = 16
## directed = FALSE
## hyper = FALSE
## loops = FALSE
## multiple = FALSE
## bipartite = FALSE
## total edges= 20
## missing edges= 0
## non-missing edges= 20
##
## Vertex attribute names:
## priorates totalties vertex.names wealth
##
## No edge attributes
# clase
class(flomarriage)
## [1] "network"
# matriz de adyacencia
# remover vertice 12 porque es ailado (distancia Inf)
A <- network::as.matrix.network.adjacency(x = flomarriage)
(index_rm <- which(colSums(A) == 0))
## Pucci
## 12
A <- A[-index_rm,-index_rm]
class(A)
## [1] "matrix" "array"
# variable exogena: riqueza
wealth <- flomarriage %v% "wealth"
(wealth <- wealth[-index_rm])
## [1] 10 36 55 44 20 32 8 42 103 48 49 27 10 146 48
# tipo igraph
g <- igraph::graph_from_adjacency_matrix(adjmatrix = A, mode = "undirected")
class(g)
## [1] "igraph"
# tipo network
flomarriage <- network::as.network.matrix(A)
class(flomarriage)
## [1] "network"
# Cargar librerías con supresión de mensajes y advertencias
suppressMessages(suppressWarnings(library(latentnet)))
# Construir las covariables
x <- abs(outer(X = wealth, Y = wealth, FUN = "-"))
# Ajustar el modelo con los mismos parámetros de configuración
fit <- ergmm(
formula = flomarriage ~ euclidean(d = 2, G = 0) + edgecov(x), # Modelo con covariables
seed = 42, # Fijar semilla para reproducibilidad
control = ergmm.control(
burnin = 1000, # Iteraciones de burn-in
interval = 10, # Thinning: guardar 1 muestra cada 10 iteraciones
sample.size = 5000 # Total de muestras efectivas para inferencia
)
)
# resumen del ajuste
summary(fit)
##
## ==========================
## Summary of model fit
## ==========================
##
## Formula: flomarriage ~ euclidean(d = 2, G = 0) + edgecov(x)
## Attribute: edges
## Model: Bernoulli
## MCMC sample of size 5000, draws are 10 iterations apart, after burnin of 1000 iterations.
## Covariate coefficients posterior means:
## Estimate 2.5% 97.5% 2*min(Pr(>0),Pr(<0))
## (Intercept) 4.861221 2.115049 8.4390 < 2.2e-16 ***
## edgecov.x 0.078701 0.035459 0.1386 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Overall BIC: 236.9137
## Likelihood BIC: 56.32111
## Latent space/clustering BIC: 180.5926
##
## Covariate coefficients MKL:
## Estimate
## (Intercept) 2.70038055
## edgecov.x 0.04481434
# Cargar librerías con supresión de mensajes y advertencias
suppressMessages(suppressWarnings(library(ggplot2)))
suppressMessages(suppressWarnings(library(gridExtra)))
# Datos de la cadena (reemplaza con fit$sample$beta[,2])
x <- c(fit$sample$beta[,2])
iterations <- 1:length(x)
data_chain <- data.frame(Iteration = iterations, Beta = x)
# Estadísticas
mean_x <- mean(x)
quantiles_x <- quantile(x, c(0.025, 0.975))
# Gráfico de la cadena con puntos
p_chain <- ggplot(data_chain, aes(x = Iteration, y = Beta)) +
geom_point(alpha = 0.3, size = 0.5, color = "black") + # Puntos con transparencia
geom_hline(yintercept = mean_x, color = "blue", linetype = "dashed", linewidth = 1) + # Línea de la media
geom_hline(yintercept = quantiles_x, color = "red", linetype = "dotted", linewidth = 1) + # Líneas de los cuantiles
labs(x = "Iteración", y = expression(beta), title = "Cadena") +
theme_minimal()
# Histograma de la distribución marginal
data_hist <- data.frame(Beta = x)
p_hist <- ggplot(data_hist, aes(x = Beta)) +
geom_histogram(aes(y = ..density..), bins = 30, fill = "gray90", color = "gray90") + # Histograma
geom_vline(xintercept = mean_x, color = "blue", linetype = "dashed", linewidth = 1) + # Línea de la media
geom_vline(xintercept = quantiles_x, color = "red", linetype = "dotted", linewidth = 1) + # Líneas de los cuantiles
labs(x = expression(beta), y = "Densidad", title = "Distr. marginal") +
theme_minimal()
# Combinar los gráficos
grid.arrange(p_chain, p_hist, ncol = 2)
# Cargar librerías con supresión de mensajes y advertencias
suppressMessages(suppressWarnings(library(dplyr)))
suppressMessages(suppressWarnings(library(kableExtra)))
# Extraer las muestras
mu_samples <- fit$sample$beta[,1]
beta_samples <- fit$sample$beta[,2]
# Calcular estadísticas
results <- data.frame(
Parameter = c("mu", "beta"),
Mean = c(mean(mu_samples), mean(beta_samples)),
SD = c(sd(mu_samples), sd(beta_samples)),
`2.5%` = c(quantile(mu_samples, 0.025), quantile(beta_samples, 0.025)),
`97.5%` = c(quantile(mu_samples, 0.975), quantile(beta_samples, 0.975))
)
# Crear la tabla estilizada
results %>%
kable(format = "html", digits = 3, col.names = c("Parameter", "Media", "Desviación", "L. Inferior", "L. Superior")) %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
add_header_above(c(" " = 1, "Inferencia posterior" = 4))
Parameter | Media | Desviación | L. Inferior | L. Superior |
---|---|---|---|---|
mu | 4.861 | 1.639 | 2.115 | 8.439 |
beta | 0.079 | 0.026 | 0.035 | 0.139 |
El modelo de distancia con agrupaciones se define como:
\[ y_{i,j} \mid \mu, \boldsymbol{u}_i, \boldsymbol{u}_j \overset{\text{ind}}{\sim} \textsf{Bernoulli}\left( \textsf{expit}\left( \mu - \| \boldsymbol{u}_i - \boldsymbol{u}_j \| \right) \right), \]
donde:
Este modelo, propuesto por Krivitsky, Handcock, Raftery y Hoff (2009), introduce agrupaciones latentes mediante una mezcla de distribuciones normales. Esto permite capturar patrones de:
Es especialmente útil para modelar redes sociales donde las interacciones están influenciadas por la proximidad latente y las estructuras de agrupamiento.
Krivitsky, P. N., Handcock, M. S., Raftery, A. E., & Hoff, P. D. (2009). Representing degree distributions, clustering, and homophily in social networks with latent cluster random effects models. Social Networks, 31(3), 204-213.
Sampson (1969) documentó las interacciones sociales entre un grupo de monjes durante su residencia en un claustro, con un interés particular en las relaciones de afecto positivo.
En el estudio, cada monje seleccionó sus tres principales opciones (o cuatro, en caso de empate) de relaciones de “agrado”. Se considera que existe una arista dirigida del monje A al monje B si A incluyó a B entre sus principales elecciones. Este enfoque permite capturar la estructura de las preferencias sociales dentro del grupo.
Sampson, S. F. (1968). A novitiate in a period of change: An experimental and case study of relationships. Tesis doctoral no publicada, Departamento de Sociología, Cornell University.
# datos
data(sampson)
samplike
## Network attributes:
## vertices = 18
## directed = TRUE
## hyper = FALSE
## loops = FALSE
## multiple = FALSE
## total edges= 88
## missing edges= 0
## non-missing edges= 88
##
## Vertex attribute names:
## cloisterville group vertex.names
##
## Edge attribute names:
## nominations
class(samplike)
## [1] "network"
# matriz de adyacencia
# no hay vértices conectados
A <- network::as.matrix.network.adjacency(x = samplike)
class(A)
## [1] "matrix" "array"
# tipo igraph
g <- igraph::graph_from_adjacency_matrix(adjmatrix = A, mode = "directed")
class(g)
## [1] "igraph"
# gráfico
set.seed(42)
plot(g, vertex.size = 10, vertex.label = NA, edge.arrow.size = 0.5, main = "Monjes")
# ajuste del modelo para varios valores de G
fit1 <- ergmm(samplike ~ euclidean(d = 2, G = 1))
fit2 <- ergmm(samplike ~ euclidean(d = 2, G = 2))
fit3 <- ergmm(samplike ~ euclidean(d = 2, G = 3))
fit4 <- ergmm(samplike ~ euclidean(d = 2, G = 4))
# modelos
fits <- list(fit1, fit2, fit3, fit4)
# calcular BICs
bics <- reshape(as.data.frame(t(sapply(fits, function(x) c(G = x$model$G, unlist(bic.ergmm(x))[c("Y","Z","overall")])))),
list(c("Y","Z","overall")),
idvar = "G",
v.names = "BIC",
timevar = "Component",
times = c("likelihood","clustering","overall"),
direction = "long")
bics
## G Component BIC
## 1.likelihood 1 likelihood 252.71846
## 2.likelihood 2 likelihood 252.69548
## 3.likelihood 3 likelihood 254.77604
## 4.likelihood 4 likelihood 253.59231
## 1.clustering 1 clustering 127.31644
## 2.clustering 2 clustering 121.23954
## 3.clustering 3 clustering 81.42011
## 4.clustering 4 clustering 90.16484
## 1.overall 1 overall 380.03490
## 2.overall 2 overall 373.93502
## 3.overall 3 overall 336.19615
## 4.overall 4 overall 343.75715
# grafico de BIC vs no.de clusters
with(bics, interaction.plot(G, Component, BIC, type = "b", xlab = "Clusters", ylab = "BIC"))
# G optimo
bestG <- with(bics[bics$Component == "overall",], G[which.min(BIC)])
bestG
## [1] 3
# resumen del modelo para G = 3
summary(fit3)
##
## ==========================
## Summary of model fit
## ==========================
##
## Formula: samplike ~ euclidean(d = 2, G = 3)
## Attribute: edges
## Model: Bernoulli
## MCMC sample of size 4000, draws are 10 iterations apart, after burnin of 10000 iterations.
## Covariate coefficients posterior means:
## Estimate 2.5% 97.5% 2*min(Pr(>0),Pr(<0))
## (Intercept) 2.0158 1.3620 2.7 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Overall BIC: 336.1961
## Likelihood BIC: 254.776
## Latent space/clustering BIC: 81.42011
##
## Covariate coefficients MKL:
## Estimate
## (Intercept) 1.163787
# grafico densidad variables latentes
plot(fit3, what = "density")
# grafico posiciones latentes
plot(fit3, what = "pmean", print.formula = F, main = "Media post. posiciones latentes")
# gráfico posiciones latentes
plot(fit3, pie = TRUE, vertex.cex = 3, print.formula = F, main = "Media post. posiciones latentes")
# asignacion de los clusters
fit3$mcmc.mle$Z.K
## [1] 1 1 3 2 2 2 1 2 2 2 1 1 1 1 1 1 3 3