Comunidad: grupo de vértices que se conectan mucho entre sí y poco con el resto.
Modularidad\(Q\): cuántas aristas caen dentro de comunidades respecto a lo que esperaría una red aleatoria con los mismos grados.
\(Q \to 1\) = comunidades nítidas; \(Q \approx 0\) = no hay estructura.
Interpretación:
En redes de lavado, las comunidades son circuitos operativos: grupos de entidades que operan en los mismos momentos, con redundancia y poca dependencia del exterior.
Un circuito absorbe la caída de un miembro sin dejar de operar.
Conocer los circuitos es previo a decidir dónde intervenir.
Comparamos los principales algoritmos sobre el chronnet (no dirigido colapsado cuando el método lo requiere), resolviendo el mismo problema desde ángulos distintos (agrupamiento greedy, difusión, caminos aleatorios, óptimo local). El edge-betweenness queda fuera: recalcula la intermediación de todas las aristas tras cada fusión, un coste inviable sobre las ~31k aristas del chronnet (y nunca ganó en modularidad):
La fila ★ es la de mayor modularidad — el método que mejor separa los circuitos.
Ojo: modularidad muy alta con cientos de comunidades puede significar sobre-partición (cortar circuitos que operan juntos); por eso se reporta junto al número de comunidades.
Lectura criminal
Cada comunidad detectada es una hipótesis de investigación: “estas entidades operan como un circuito”.
Los puentes entre comunidades son donde la actividad cambia de circuito.
Ahí es donde una intervención corta el mayor flujo con el menor esfuerzo (módulo 6).
2. Tabla descriptiva por comunidad
La tabla completa (una fila por comunidad) no se puede leer.
Resumen correcto: estadísticos del tamaño + tabla interactiva filtrable de las más grandes:
Code
tam <- comunidad_df %>%count(comunidad, name ="n_bancos") %>%pull(n_bancos)tibble( Estadístico =c("Comunidades", "Bancos en la comunidad más grande", "Mediana de tamaño", "Comunidades de 2-3 bancos", "Bancos en comunidades de 2-3"),Valor =c(length(tam), max(tam), median(tam),sum(tam <=3), sum(tam[tam <=3]))) %>% knitr::kable()
Estadístico
Valor
Comunidades
10.0
Bancos en la comunidad más grande
864.0
Mediana de tamaño
148.5
Comunidades de 2-3 bancos
0.0
Bancos en comunidades de 2-3
0.0
Lectura:
Comunidades pequeñas (2-3 bancos) = pares de facto: solo comparten ventanas temporales entre sí y con casi nadie más — típicamente un corresponsal y su cliente recurrente.
Comunidades grandes = circuitos de procesamiento con muchos participantes.
La detección usa toda la serie (2000–2017); no hay filtro temporal “en vivo” dentro del algoritmo.
Para períodos — “¿cambió la estructura antes y después de 2012?” — el procedimiento es: filtrar tx por fecha → reconstruir el chronnet (R/03_chronnet.R) → re-ejecutar R/05_comunidades.R por ventana.
La temporalidad se introduce por diseño de la muestra, no por el algoritmo.
3. Dendrograma de la jerarquía
Fast Greedy fusiona iterativamente comunidades; el árbol muestra a qué altura se unen.
Fusiones a baja altura = comunidades gemelas.
Fusiones a gran altura = macro-circuitos que solo se tocan por puentes puntuales.
Code
hc <-as.hclust(comunidades$fast_greedy)dendro <-color_branches(as.dendrogram(hc), k =12, col =brewer.pal(12, "Paired"))labels(dendro) <-""plot(dendro, ylab ="Altura de fusión", main ="Jerarquía de comunidades (Fast Greedy)")
4. ¿Son reales las estructuras o son azar? — Modelo nulo
Cualquiera puede calcular métricas; la pregunta seria es: ¿son sorprendentes?
Una red con hubs extremos tendrá caminos cortos y alguna modularidad por pura heterogeneidad de grado.
El modelo nulo de configuración compara lo observado contra esa explicación mínima del azar:
Generamos \(N = 100\) réplicas aleatorias del chronnet, preservando exactamente los grados entrantes y salientes de cada vértice (doble intercambio de aristas, \(5|E|\) veces por réplica).
Self-loops permitidos durante el rewiring — el chronnet observado los contiene.
Cada arista re-cableada retiene su peso: solo cambia la colocación de los pesos.
Sobre cada réplica se calculan las métricas, y el valor observado se compara contra esa distribución nula.
test_null %>%ggplot(aes(x =reorder(metrica, z), y = z)) +geom_col(fill ="#0D9488") +geom_hline(yintercept =c(-1.96, 1.96), linetype ="dashed", color ="#B45309") +coord_flip() +labs(x ="", y ="z-score vs. modelo nulo",caption ="Líneas discontinuas: umbrales ±1.96 (significancia 5%)") +theme_minimal()
Lectura criminal
Métricas con \(|z|\) por encima de 1.96 = propiedades intrínsecas de la red — ni el azar ni la sola heterogeneidad de grado las producen.
La organización en comunidades y la separación temporal (diámetro, caminos) son el terreno real para planear disrupción.
Si todo hubiera sido no significativo, la conclusión sería la opuesta: “no vemos estructura, solo ruido de reporte” — y no tendría sentido intervenir nada.
5. ¿Es estable en el tiempo? — Block bootstrap
Hilo conductor de esta sección:
Ya sabemos que la red tiene estructura (modelo nulo, sección 4).
La pregunta ahora: ¿esa estructura depende de los días concretos en que se registraron los datos?
Si reordenar el tiempo desmoronara la red, todo lo anterior sería un artefacto del calendario de reporte.
La actividad de un día depende de la del anterior (autocorrelación): un bootstrap sobre eventos independientes sería inválido.
Usamos bootstrap por bloques de 7 días con \(B = 50\) réplicas (parámetro de demo; el paper usa 200):
Se parte la línea de tiempo en bloques contiguos de 7 días.
Se remuestrean bloques con reemplazo hasta cubrir el horizonte (así se conservan las semanas operativas enteras: la estructura semanal del lavado no se rompe).
Se reconstruye el chronnet sobre la secuencia remuestreada.
Se recalculan las métricas.
Una métrica es estable si el observado cae dentro del IC 95% bootstrap — es decir, no depende de qué días específicos se interceptaron.
Bootstrap y métricas observadas corren sobre el mismo objeto (el chronnet), así que la comparación es directa.