Capítulo 39 Análisis de grafos y redes sociales
José J. Galán
Universidad Complutense de Madrid
39.1 Introducción
El origen de la teoría de grafos se debe al problema de los siete puentes de Königsberg, planteado en Euler (1736), que es considerado el primer artículo sobre teoría de grafos. El problema se centra en la ciudad Königsberg (antigua Prusia), ahora Kaliningrado (Rusia), donde existen varios puentes y el problema consiste en trazar una ruta que cruce todos los puentes una única vez (véase Fig. 39.1). Euler, mediante el uso de grafos, demostró que no era posible.
Pero ¿qué relación tiene un concepto creado en 1736, el de grafo, con algo tan reciente como las redes sociales? Sin ser extremadamente rigurosos, se puede hablar de las redes sociales (RR. SS.) como las relaciones existentes entre personas, es decir, un hilo invisible que une a las personas que tienen algo en común. En algunos casos es muy evidente porque se crean grupos específicos de personas que comparten una afición y, en otros, es menos evidente porque, por ejemplo, pueden compartir un amigo en común sin saberlo. Estos hilos “invisibles” se unen y forman una red que se puede representar como un grafo, el mismo concepto de grafo que describió Euler y que permite establecer diferentes caminos para unir a las personas que forman la red.
39.2 Teoría de grafos
Un grafo es un conjunto de nodos (vértices) que pueden estar unidos por aristas (enlaces). Si se piensa en cada nodo como una persona y en cada arista como la relación que los une, entonces una red social puede representarse mediante grafos (véase Fig. 39.3).
En el primer ejemplo que se plantea en este capítulo, relativo a una red social, se utiliza el conjunto datos_facebook
incluido en el paquete CDR
. Para llevarlo a cabo, primeramente se cargan las librerías y se representan las relaciones entre los miembros que la componen mediante un grafo (Fig. 39.2).
grafo_facebook <- graph.data.frame(datos_facebook, directed = F)
plot(grafo_facebook, vertex.label = NA, vertex.size = 8)
Formalmente, una red social puede modelizarse con una estructura de red invisible (relación familiar, amistad, trabajo…) que une a distintos actores con intereses o valores comunes, estableciendo una relación personal entre los individuos o grupos de individuos conectados. Existen distintos tipos de grafos dependiendo de las características de la red social representada. Algunos ejemplos son:
- El grafo de amistad (véase Fig. 39.3), donde cada nodo representa una persona y una arista conecta dos personas si dentro de la red social son amigos.
amistades <- data.frame(
persona = c("A", "B", "C", "D", "E"), amigo = c("B", "C", "A", "E", "A")
)
grafo_amistades <- graph_from_data_frame(amistades)
- El grafo de comunidades (véase Fig. 39.3), donde también cada nodo representa una persona y una arista conecta dos personas si dentro de la red social pertenecen a la misma comunidad, entendiendo por comunidad un grupo de individuos que comparten intereses o características en común.
comunidades <- data.frame(
persona = c("A", "B", "C", "D", "E"), comunidad = c("1", "2", "1", "2", "1")
)
grafo_comunidades <- graph_from_data_frame(comunidades)
par(mfrow = c(1, 2))
plot(grafo_amistades, vertex.label = V(grafo_amistades)$name, main = "Grafo amistades")
plot(grafo_comunidades, vertex.label = V(grafo_comunidades)$name, main = "Grafo comunidades")
39.3 Elementos de un grafo
El análisis de RR. SS. mediante la teoría de grafos requiere conocer previamente una serie de conceptos básicos (Pérez-Solà & Casas-Roma, 2021) que se listan y definen a continuación:
los vértices: representan nodos que se unen mediante aristas. En una red social cada vértice representa una de las personas de dicha red, unidas en ocasiones por intereses comunes a otras;
las aristas: son las relaciones que unen los nodos. Son dirigidas (Fig. 39.4) si tienen un sentido definido y no dirigidas (Fig. 39.4) en caso contrario.
El siguiente código genera un grafo dirigido:
grafo_dirigido <- graph.data.frame(datos_grafos, directed = T)
Para un grafo no dirigido basta con especificar directed = F
:
grafo_nodirigido <- graph.data.frame(datos_grafos, directed = F)
En otras RR. SS., como LinkedIn, las aristas podrían representar la relación que une a las personas. Las personas forman parte de un grupo con intereses comunes, formando un grafo no dirigido. Pero también se puede seguir a alguien sin necesariamente ser seguido; en ese caso las relaciones se pueden representar como un grafo dirigido.
- Un grafo es un conjunto de vértices y aristas que se puede representar mediante \(G = (V, E)\), donde \(V\) es el conjunto de nodos o vértices del grafo y \(E\) es un conjunto de aristas, arcos o edges. En la Fig. 39.4 se incluye un grafo “sencillo”, representado por el objeto
grafo
(solo se indican las aristas y el software R es capaz de interpretar los vértices), sobre el cual se explican a continuación los conceptos de matriz de adyacencia, grado y camino:
par(mfrow = c(1, 3), mai = c(0, 0, 0, 0))
plot(grafo_dirigido, vertex.label = V(grafo_dirigido)$name, main = " ")
plot(grafo_nodirigido, vertex.label = V(grafo_nodirigido)$name, main = " ")
plot(grafo, main = " ")
- La información recogida en un grafo también se puede expresar mediante números, organizados en una matriz denominada matriz de adyacencia, \({\bf A}_{n\times n}\) (donde \(n\) es el número de nodos), lo que facilita los cálculos computacionales en grandes redes. Cada entrada de la matriz, \(a_{ij}\), indica el número de aristas que salen del \(i\)-ésimo vértice y llegan al \(j\)-ésimo; cada fila de la matriz indica el número de aristas que salen del vértice \(i\)-ésimo con destino a cada uno de los otros vértices. En los grafos no dirigidos \({\bf A}_{n \times n}\) es simétrica, ya que si el vértice o nodo \(1\) conecta con el \(2\), entonces el \(2\) también conecta con el \(1\). En los grafos dirigidos, donde cada arista tiene una orientación, esto no tiene por qué ocurrir: el vértice o nodo \(1\) puede conectar con el \(2\), pero no al revés. En este tipo de grafos la matriz de adyacencia no es simétrica. A continuación se obtiene la matriz de adyacencia del objeto
grafo
:
matriz_adyacencia <- get.adjacency(grafo, sparse = FALSE)
matriz_adyacencia
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0 1 1 1 0
#> [2,] 0 0 0 1 0
#> [3,] 0 0 0 0 1
#> [4,] 0 0 0 0 1
#> [5,] 0 0 0 0 0
- El grado o valencia de un nodo x en un grafo es el número de aristas que concurren en él (que “entran a” o “salen de” dicho nodo), y se representa mediante \(grado(x)\), \(g(x)\) o \(gr(x)\), siendo un vértice de grado 0 un vértice aislado. En un grafo \(G\), el grado máximo (\(\Delta (G)\)) es el valor más grande entre los grados de los nodos, mientras que el grado mínimo (\(\delta (G)\)) es el valor más pequeño. El grado de un grafo \(g(G)\) es la suma de los grados de los vértices, lo que mide la cantidad de conexiones en el grafo. En redes sociales, el grado representa el número de relaciones.
La función degree()
proporciona un vector que contiene los grados de cada vértice en el grafo, ayudando a analizar la estructura y la importancia de los nodos en la red. A continuación se muestra el grado de cada uno de los 5 vértices anteriores, así como el grado del grafo (mediante la función sum()
) y los grados máximo y mínimo mediante las funciones max()
y min()
:
grado_nodos <- degree(grafo)
grado_nodos
#> [1] 3 2 2 3 2
sum(grado_nodos)
#> [1] 12
max(grado_nodos)
#> [1] 3
min(grado_nodos)
#> [1] 2
- Un camino es un conjunto de aristas no recursivas. La longitud de un camino es su número de aristas. Entre dos vértices puede haber más de dos caminos y se puede incluir el mismo vértice en el camino más de una vez. Evidentemente, siempre habrá un camino más corto, que será aquel que menos aristas ha recorrido. Si entre todos los pares de vértices existe un camino, entonces el grafo se denomina conexo. El siguiente código muestra el camino más corto entre los vértices 2 y 5 del objeto
grafo
utilizado de ejemplo.
# Camino más corto entre el nodo 2 y el 5
caminos <- get.shortest.paths(grafo, from = "2", to = "5")
V(grafo)[caminos$vpath[[1]]]
#> + 3/5 vertices, from fff3aee:
#> [1] 2 4 5
39.4 Procedimiento con R: el paquete igraph
Existen diversos paquetes en R para representar grafos, pero el más utilizado y popularizado, por su sencillez y eficacia, es igraph
(Csardi & Nepusz, 2006). Se trata de un paquete que permite crear y manipular grafos para analizar redes en R de forma muy sencilla (Fig. 39.5). A continuación se muestra un sencillo ejemplo sobre cómo crear un grafo dirigido a partir de un dataframe usando la función graph_from_data_frame()
con los siguientes argumentos:
edges
: dataframe en el que las dos primeras columnas representan una lista o conjunto de aristas.directed
: un valor lógico que indica si es un grafo dirigido o no dirigido.vertices
: un dataframe con los valores de los vértices o NULL.
nodes <- data.frame("nodos" = c("A", "B", "C", "D", "E"))
edges <- data.frame(
"from" = c("A", "C", "B", "A", "A", "A"),
"to" = c("B", "D", "C", "C", "D", "E")
)
red <- graph_from_data_frame(edges, directed = TRUE, vertices = nodes)
plot(red, vertex.size = 50)
A continuación se muestra otro ejemplo con las relaciones entre los actores de dos películas.
nodes
contiene el nombre de cada actor y su descripción, es decir, si se trata de un actor o una actriz (es imprescindible que los nombres que más adelante se introducen en edges
existan en nodes
). Sin embargo, no es obligatorio declarar los nodos, ya que pueden ser extraídos de las relaciones.
edges
contiene la relaciones, from
y to
, además de la película donde coinciden. No obstante, este último dato es meramente descriptivo y no resulta necesario.
nodes <- data.frame("actores" = c("Jim Carrey", "Arnold Schwarzenegger", "George Clooney", "Cameron Díaz"),
"descripcion" = c("actor", "actor", "actor", "actriz")
)
edges <- data.frame(
"from" = c("Jim Carrey", "Jim Carrey", "George Clooney", "Jim Carrey"),
"to" = c("Arnold Schwarzenegger", "George Clooney", "Arnold Schwarzenegger", "Cameron Díaz"),
"pelicula" = c("Batman y Robin", "Batman y Robin", "Batman y Robin", "La mascara")
)
red <- graph_from_data_frame(edges, directed = F, vertices = nodes)
plot(red, vertex.size = 50)
En la Fig. 39.6 se puede observar que el actor Jim Carrey tuvo relación con todos los actores de la red propuesta, mientras que la actriz Cameron Díaz solo participó con uno de ellos (el propio Jim Carrey).
39.5 Análisis de influencia en un grafo aplicado a RR. SS.
Existen paquetes para obtener información de distintas RR. SS.; por ejemplo, en R se puede utilizar el paquete Rfacebook
para conectarse a Facebook y obtener información de los contactos existentes. Para ello, es necesario activar la API, Interfaz de Programación de Aplicaciones, desde https://developers.facebook.com. La información necesaria se puede encontrar en su página web https://developers.facebook.com/docs. Para ilustrar un ejemplo didáctico, sin que el lector necesite conocimientos de desarrollo para descargar los datos, se ha simulado la relación entre amigos de una red social, como podría ser Facebook, en el conjunto de datos datos_facebook
contenido en el paquete CDR
del libro.
En esta ocasión se utiliza la función graph.data.frame()
del paquete igraph
para crear un objeto de tipo grafo, dirigido en este caso, a partir de un dataframe en R. Seguidamente, mediante plot()
, se muestra el grafo al mismo tiempo que se establecen sus propiedades (véase Fig. 39.7).
datos_faceboook <- graph.data.frame(datos_facebook, directed = F)
# datos_faceboook # descomentar para ver las relaciones
grafo_facebook <- graph.data.frame(datos_facebook, directed = T)
plot.igraph(grafo_facebook,
layout = layout.fruchterman.reingold,
vertex.label = NA, vertex.label.cex = 1, vertex.size = 3, edge.curved = TRUE
)
Nota
Obsérvese cómo varía la estructura de los datos de entrada para construir un grafo.
Siguiendo con el ejemplo, a continuación se analizan las relaciones de la red social. Como se avanzó anteriormente, el número de aristas que entran o salen de cada vértice indica el número de personas con las que se relaciona el individuo representado por dicho vértice. En la salida de debajo se muestra cuántos vértices (segunda línea) tiene cada grado específico (primera línea):
table(degree(grafo_facebook))
#>
#> 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
#> 1 2 4 4 7 4 8 14 19 15 14 19 22 15 11 13 9 5 3 3 4 1 4
Ahora se procede a personalizar los nodos. Las RR. SS. son enormes y, por tanto, es útil centrarse en una subred para estudios concretos. A modo de ejemplo, para focalizar este caso de estudio, se tendrán en cuenta únicamente aquellos vértices o nodos cuyo grado sea igual o superior a 26, asignándoles su nombre.
bad_network <- V(grafo_facebook)[degree(grafo_facebook) <= 26]
grafo_facebook <- delete.vertices(grafo_facebook, bad_network)
V(grafo_facebook)$name <- c(
"Gema", "Patricia", "Ramon", "José", "Maria", "Ángeles",
"Gabriel", "Javier", "Victor", "Leonor", "Ana", "Isabel", "Cristóbal", "Rosa", "Aurora"
)
plot(grafo_facebook, vertex.size = 20)
En la Fig. 39.8 se pueden ver las relaciones entre las personas incluidas en el grafo (a quién siguen y por quiénes son seguidas). Por ejemplo, a Gema no la sigue nadie. Leonor, otro caso extremo igual que Gema, es seguida por Aurora, Ángeles y Gabriel, pero ella no sigue a nadie.
39.5.1 Centralidad
En teoría de grafos y análisis de RR. SS., el concepto de centralidad se refiere a la importancia o prominencia de los vértices o nodos en un determinado grafo o red social. En el caso de una red social de amigos, como Facebook, la centralidad de un nodo (persona) representa el número de amigos que tiene.
Un grafo no tiene una centralidad real porque no tiene coordenadas (Easley & Kleinberg, 2010), pero existen distintas medidas de centralidad que, en una red/grafo social, permiten identificar el networking social de cada individuo, es decir, su influencia.
Son innumerables las medidas de centralidad (generalmente normalizadas o estandarizadas) que pueden encontrarse en la literatura sobre la cuestión para determinar y comparar, de forma cuantitativa, la importancia relativa de un nodo en el conjunto de la red.
La centralidad no es un atributo intrínseco de los nodos, sino un atributo estructural: un valor que depende de las relaciones de dicho nodo con los demás de la red. Generalmente, el nodo central suele tener la mayor centralidad, mientras que la mínima suele corresponder a los nodos periféricos.
En los grafos dirigidos, cuantas más aristas reciba un nodo (persona en el caso de una RR. SS.) más nodos (personas) están intentando interactuar con él y más importancia tendrá en el grafo (más “prestigio” tendrá la persona en la red). Pero si la conexión de un nodo con otro (la influencia de una persona sobre otra en una RR. SS.) no es directa, sino que tiene lugar a través de un camino más largo (pasando por más nodos), entonces quiere decir que su influencia es mayor, ya que, en el caso de la RR. SS. más personas han recibido esa influencia, unas directamente y otras de forma indirecta.
En la literatura existen diversas medidas de la centralidad (Wasserman & Faust, 1995). Entre ellas, a continuación se definen la centralidad de grado, la centralidad de intermediación (o simplemente centralidad) y la centralidad de vector propio.
- La centralidad de grado (en inglés, degree centrality) fue la primera de las medidas de centralidad y también es la más sencilla. Más concretamente, la centralidad del \(i\)-ésimo nodo es su grado, es decir, el número de aristas que posee un nodo con los demás:
\[ C_{D}^{(i)}=\sum _{j \neq i} a_{ij},\] donde \(a_{ij}\) es el \(j\)-ésimo elemento de la \(i\)-ésima fila de la matriz de adyacencia.
Para normalizar la centralidad de grado, lo normal es dividirla por el número total de nodos de la red. En el caso de grafos simples (sin bucles), basta con dividir por el número total de nodos menos 1. Cuando el grado máximo de un grafo sea demasiado bajo, también se podría dividir por dicho grado máximo. Por tanto, se consideran aceptables las siguientes medidas de centralidad de grado normalizadas: \[C_{Dnorm}^{(i)}=\frac {C_{D}^{(i)}}{n}, \hspace{0,5cm} C_{Dnorm}^{(i)}=\frac {C_{D}^{(i)}}{n-1}, \hspace{0,5cm} C_{Dnorm}^{(i)}=\frac {C_{D}^{(i)}} {\max_{i \in V} C_{D}^{(i)}}.\]
- La centralidad de intermediación (betweenness) del \(i\)-ésimo nodo es la proporción que el número de caminos entre cualesquiera otros dos nodos (\(j\) y \(k\)) cuya conexión pasa por el nodo \(i\) supone respecto del número de caminos mínimos (más cortos) desde el nodo \(j\) hasta el nodo \(k\). Por lo tanto, en el caso de una RR. SS., una persona tendrá mayor influencia cuanto mayor centralidad de información tenga porque comunicará mucha información a través de los nodos de la red (supuesto que la transferencia de la información a través de la red siempre se lleve a cabo a través de los caminos más cortos).
Formalmente, la centralidad de intermediación del \(i\)-ésimo nodo se define como sigue:
\[C_B^{(i)}=\sum_{i \neq k \in V} \frac {B_{jk}^{(i)}}{B_{jk}},\]
donde \(B_{jk}^{(i)}\) es el número de caminos más cortos desde \(j\) hasta \(k\) que pasan por el nodo \(i\) y \(B_{jk}\) el número de caminos más cortos desde el nodo \(j\) hasta el nodo \(k\).
La centralidad de intermediación normalizada (para que sus valores estén siempre entre 0 y 1) se obtiene dividiendo la centralidad de intermediación por el mayor número posible de pares de nodos, excluyendo el nodo en el que se está midiendo el grado de centralidad.257
En caso de grafos no dirigidos: \[C_{B(norm)}^{(i)}=\frac {C_B^{(i)}}{ \frac {(n-1)(n-2)}{2}}. \]
En caso de grafos dirigidos:
\[C_{B(norm)}^{(i)}=\frac {C_B^{(i)}}{ (n-1)(n-2)}. \]
El siguiente código muestra la centralidad de intermediación normalizada correspondiente a los nodos del ejemplo de Facebook (para obtenerla sin normalizar, hay que especificar normalized = FALSE
al llamar a la función betweenness()
):
betweenness_centrality <- betweenness(grafo_facebook)
betweenness_centrality
#> Gema Patricia Ramon José Maria Ángeles Gabriel Javier
#> 0.000000 33.500000 30.200000 10.500000 53.300000 7.000000 37.800000 27.200000
#> Victor Leonor Ana Isabel Cristóbal Rosa Aurora
#> 42.700000 0.000000 9.333333 0.000000 6.000000 4.666667 23.800000
Como puede apreciarse, María es el nodo con mayor centralidad, es decir, es la persona con mayor influencia en la transferencia de información entre los nodos a través de la red, bajo el supuesto de que cada nodo transfiere la información siguiendo los caminos más cortos.
- La centralidad de vector propio (eigencentrality), \(C_E^{(i)}\). Es una medida de centralidad recursiva que se basa en la centralidad de los nodos con los que se relaciona. En concreto, la centralidad de valor propio es proporcional a la suma de las centralidades de sus nodos vecinos. A modo de ejemplo, un relaciones públicas de una discoteca puede conocer a mucha gente mientras que un político puede conocer a menos gente; sin embargo, la gente que conoce el relaciones públicas de la discoteca es gente normal, sin grandes influencias en otra gente, mientas que las personas que conoce el político son personas altamente influyentes. Por ello, el político está mejor posicionado en términos de influencia que el relaciones públicas de la discoteca, aunque conozca a menos gente.
En términos matemáticos, la expresión de la centralidad de vector propio del \(i\)-ésimo nodo es: \(C_E^{(i)} = \lambda \sum_{j} a_{ij}C_E^{(j)}\), donde \(\lambda\) es la constante de proporcionalidad, \(a_{ij}\) es el valor de la fila \(i\) y la columna \(j\) de la matriz de adyacencia A y \(C_E^{(j)}\) es la centralidad de valor propio de los nodos con los que se relaciona el nodo \(i\). La denominación de “centralidad de vector propio” se debe a que el vector de dicha centralidad corresponde al vector propio de \(\bf A\) asociado al mayor valor propio de la matriz, \(\lambda\).
El siguiente código muestra la centralidad mediante vector propio correspondiente a los nodos del ejemplo actual:
eigencentrality <- eigen_centrality(grafo_facebook)$vector
eigencentrality
#> Gema Patricia Ramon José Maria Ángeles Gabriel Javier
#> 0.1618269 0.6090993 0.7768737 0.3148866 0.9257523 0.6787138 0.7180398 0.4714266
#> Victor Leonor Ana Isabel Cristóbal Rosa Aurora
#> 1.0000000 0.4725741 0.7663131 0.2086397 0.7620632 0.3831565 0.7000984
Como puede apreciarse, la mayor centralidad de autovalor corresponde al nodo Víctor. Este nodo es, por tanto, central en términos de conexión con otros nodos importantes de la red, identificándose como el nodo líder o de mayor influencia de la red.
39.5.2 Detección de comunidades
En el análisis de una RR. SS. es importante detectar las distintas comunidades que la componen, entendiendo por comunidad un grupo de personas afines. La detección de dichas comunidades permite estudiar los distintos grupos que conforman la RR. SS. en función de sus relaciones y afinidad (Missaoui & Sarr, 2015).
En principio, podría identificarse la detección de comunidades con la agrupación o clusterización. Aunque esta última podría aplicarse a grafos/RR. SS., en la agrupación en clústeres los elementos con atributos similares se agrupan en el mismo clúster, mientras que la detección de comunidades está especialmente diseñada para analizar redes en función de un único tipo de atributo: los bordes. Además, los algoritmos de agrupación tienden a separar nodos periféricos individuales de las comunidades a las que deberían pertenecer, cosa que no ocurre con las técnicas de detección de comunidades.
Existen diversos algoritmos en R para detectar comunidades (Borgatti et al., 2022), pero ninguno ha demostrado que pueda actuar a la perfección con todos los grafos debido a la gran tipología existente. En el ejemplo anterior se detectan las siguientes comunidades:
- Detección de comunidades mediante centralidad de intermediación:
communities <- edge.betweenness.community(grafo_facebook)
#head(communities, 10) # descomentar para ver las comunidades
- Detección de comunidades mediante centralidad de vector propio (eigencentality):
communities <- leading.eigenvector.community(grafo_facebook)
#head(communities, 10) # descomentar para ver las comunidades
- Detección de comunidades mediante Walktrap: este procedimiento no está basado en una medida de centralidad, sino en la idea de que la distancia entre vértices se mide a través de caminatas aleatorias en la red. Específicamente, la idea básica del algoritmo
walktrap
es que los recorridos aleatorios en un gráfico/red tienden a quedar atrapados en partes densamente conectadas que se identifican con comunidades.Walktrap
utiliza el resultado de caminatas aleatorias para fusionar comunidades de forma aglomerativa. La calidad de las agrupaciones se puede evaluar utilizando cualquier criterio de calidad disponible, si bien el de modularidad es el más popular.
A continuación se presenta el código del algoritmo walktrap
para detectar dos comunidades:
communities <- walktrap.community(grafo_facebook)
#head(communities, 10) # descomentar para ver las comunidades
Otro método de detección de comunidades que merece la pena mencionar es el método de detección de comunidades de Louvain, que es el algoritmo más popular por su fácil interpretación, flexibilidad, alta calidad de las comunidades y eficiencia en tiempo de ejecución. Se basa en la modularidad, que trata de maximizar la diferencia entre el número real y el número esperado de aristas en una comunidad. Sin embargo, la optimización de la modularidad en una red es NP-difícil (acrónimo del término de teoría de la computación nondeterministic polynomial time-hard) y, por lo tanto, tiene que usar heurística.
El algoritmo de Louvain se divide en dos fases que se repiten iterativamente: \((i)\) movimiento local de nodos y \((ii)\) agregación de la red. El algoritmo comienza con una red ponderada de n nodos. En la primera fase, el algoritmo asigna una comunidad diferente a cada nodo de la red. Luego, para cada nodo, considera los vecinos y evalúa la ganancia de modularidad eliminando dicho nodo particular de la comunidad a la que pertenece y colocándolo en la comunidad vecina donde la ganancia (positiva) se hace máxima. El nodo permanecerá en la misma comunidad si no hay ganancia positiva. Este proceso se aplica repetidamente y para todos los nodos hasta que no haya más ganancias. Esta primera fase del algoritmo se detiene cuando se obtiene un máximo local de modularidad. En la segunda fase, el algoritmo construye una nueva red considerando como nodos las comunidades encontradas en la primera fase. Una vez completada la segunda fase, el algoritmo vuelve a aplicar la primera fase a la red resultante. Estos pasos se repiten hasta que no haya cambios en la red y se obtenga la máxima modularidad.
Una ventaja del algoritmo es que descubre comunidades de comunidades durante el proceso. Una limitación importante es que tiene tendencia a descubrir comunidades que están internamente desconectadas (mal conectadas) ya que al mover un nodo que ha actuado como un puente entre dos componentes en una comunidad a una nueva comunidad puede desconectar la comunidad anterior. Otra limitación, esta vez desde la perspectiva computacional, es el uso del almacenamiento de la red en la memoria principal.
39.5.3 Representación con grafos
Una vez detectadas las comunidades, se procede a su representación final mediante grafos.
- El grafo de tipo centralidad de vector propio (eigencentrality) muestra la existencia de tres comunidades. Según este método, no es tan importante tener muchos amigos, sino que lo importante es que tus amigos sean muy influyentes (véase Fig. 39.9).
eigen_grafo <- leading.eigenvector.community(grafo_facebook)
- El grafo de tipo centralidad de intermediación (betweenness), respresentado en la Fig. 39.9 muestra siete comunidades.
betweenness_grafo <- edge.betweenness.community(grafo_facebook)
- En el grafo de tipo walktrap se pueden observar dos comunidades (véase Fig. 39.9).
Walktrap_grafo <- walktrap.community(grafo_facebook, steps = 5, modularity = TRUE)
# Debe indicarse el largo de la caminata aleatoria, se recomienda usar 5 caminatas.
par(mfrow = c(1, 3), mai = c(0, 0, 0, 0))
plot(eigen_grafo, grafo_facebook, edge.arrow.size = 0.25, vertex.size = 50, main = " " )
plot(betweenness_grafo, grafo_facebook, edge.arrow.size = 0.25, vertex.size = 50, main = " ")
plot(Walktrap_grafo, grafo_facebook, edge.arrow.size = 0.25, vertex.label = (grafo_facebook)$name, vertex.size = 50, main = " "
)