Análisis de redes mediante gráficos



El análisis de redes sociales es el proceso de explorar varios sistemas utilizando la teoría de redes. Comenzó a ser ampliamente utilizado precisamente cuando quedó claro que una gran cantidad de redes existentes (sociales, económicas, biológicas) tienen propiedades universales: después de haber estudiado un tipo, puede comprender la estructura de cualquier otra red y aprender a hacer predicciones a partir de ellas.



Cualquier red consta de participantes individuales (personas o cosas en la red) y las relaciones entre ellos. Las redes se visualizan muy a menudo mediante gráficos, estructuras formadas por muchos puntos y líneas que representan las conexiones entre estos puntos. Los participantes se representan como nodos de la red y sus relaciones se representan como líneas que los conectan. Esta visualización ayuda a obtener una evaluación cualitativa y cuantitativa de las redes:





Figura: 1. Gráfico direccional que muestra la rotación de dinero entre los bancos que forman el mercado de divisas (1). Los bancos de la UE están marcados en rojo, América del Norte, en azul, y otros países, en verde.





Figura: 2. Gráfico que muestra la cooperación de los socios auditores en Dinamarca en 2010-2014 (2)



Usando el análisis de redes sociales, es posible analizar una variedad de interacciones y procesos de intercambio de recursos, tanto materiales como de información. Por ejemplo, al analizar la red de transacciones entre clientes bancarios (donde los nodos son los clientes del banco y los bordes son transferencias entre ellos), es posible determinar el círculo de personas involucradas en transacciones fraudulentas o identificar violaciones a las regulaciones internas por parte de los empleados del banco.



También puede construir una red de relaciones laborales (utilizando el ejemplo de varios tipos de comunicación entre empleados), lo que puede ayudar a comprender la estructura social de la organización y la posición de cada empleado en esta estructura. A partir de los datos sobre el tipo de comunicación de cada colaborador, se puede incluso analizar cómo afectan a su carrera características como el liderazgo, la tutoría y la cooperación y, en base a los conocimientos adquiridos, determinar las metas profesionales y proponer programas de formación para alcanzarlas.



Además, los eventos se pueden predecir utilizando el ejemplo de redes. Por ejemplo, hay modelos que estiman la probabilidad de una falla de software, algunos de ellos consideran a las personas como una fuente de predicciones; después de todo, son las personas las que desarrollan y prueban los productos antes de su lanzamiento. Sus interacciones forman una red: puede pensar en los desarrolladores como nodos, y si trabajaron juntos en el mismo archivo en la misma versión, como bordes de una red. Comprender las interacciones y la información sobre fallas anteriores le dirá mucho sobre la confiabilidad del producto final y señalará los archivos que tienen más probabilidades de fallar.



Ahora intentemos aplicar el análisis de redes sociales en la práctica. Para hacer esto, usaremos el lenguaje de programación Python, o más bien la biblioteca networkx para trabajar con gráficos, la biblioteca matplotlib para visualización y la biblioteca comunitaria para resaltar comunidades dentro de la red. Vamos a importarlos:



1.	import community
2.	import networkx as nx
3.	import matplotlib.cm as cm
4.	import matplotlib.pyplot as plt


1. Importar datos y transformarlos en un gráfico



Como conjunto de datos, tomemos una correspondencia por correo electrónico de una gran universidad europea, que contiene información anónima sobre todos los mensajes de correo electrónico entrantes y salientes entre miembros de una institución de investigación (enlace). El conjunto de datos contiene un archivo txt donde cada línea enumera pares de nodos que están relacionados entre sí.



1.	G = nx.read_edgelist('email-Eu-core.txt', create_using=nx.DiGraph())
2.	print(' : {}'.format(G.number_of_nodes()))
3.	print(' : {}'.format(G. number_of_edges()))
4.	print('       : {}'.format(round(G.number_of_edges() / float(G.number_of_nodes()), 4)))

 : 1005
 : 25571
      : 25.4438


En el código anterior, el conjunto de datos se ha importado y convertido en un gráfico. Luego, dedujimos secuencialmente los principales parámetros del gráfico: el número de nodos, bordes y el número promedio de vecinos de los nodos en el gráfico. El último parámetro refleja qué tan cerca están conectados los nodos entre sí.



2. Principales características de los gráficos



Para comprender cómo puede trabajar con cada gráfico específico, primero debe comprender cómo funciona. Echemos un vistazo rápido a las características mediante las cuales puede comprender la estructura de un gráfico.



En primer lugar, considere los conceptos de conectividad y direccionalidad. Un gráfico se llama conectado si cada par de nodos del gráfico está interconectado, es decir, desde cualquier nodo puede llegar a cualquier otro nodo. Si el gráfico está desconectado, entonces se puede dividir en subgráficos conectados al máximo (llamados componentes). Además, los gráficos pueden estar dirigidos y no dirigidos. Esto está determinado por la presencia de la direccionalidad de las conexiones entre los dos participantes. Un ejemplo de una red dirigida son las transacciones entre clientes bancarios, donde cada cliente puede recibir pagos tanto entrantes como salientes.



En el caso general, en grafos dirigidos, las conexiones no son mutuas, por lo tanto, para grafos dirigidos, en lugar del concepto de conectividad, se distinguen los conceptos de componentes de conectividad débil y fuerte. Un componente se considera débilmente conectado si ignorar la dirección da como resultado un gráfico conectado. Un componente de fuerte conectividad puede serlo si todos sus vértices son mutuamente alcanzables. Echemos un vistazo a la estructura del gráfico de nuestro conjunto de datos de correo electrónico:



1.	if nx.is_directed(G):
2.	        if nx.is_weakly_connected(G):
3.	                print('         .')
4.	        else:
5.	                print('       .')
6.	else:
7.	        if nx.is_connected(G):
8.	                print('    .')
9.	        else:
10.	                print('       .')


El gráfico es direccional y consta de varios componentes.



Aquí verificamos la direccionalidad y la conectividad del gráfico y descubrimos que el gráfico del conjunto de datos está dirigido y contiene varios componentes desconectados. Echemos un vistazo más de cerca a los componentes más grandes de la conectividad fuerte y débil:



1.	G_weak = G.subgraph(max(nx.weakly_connected_components(G), key=len))
2.	print(' : {}'.format(G_weak.number_of_nodes()))
3.	print(' : {}'.format(G_weak.number_of_edges()))
4.	print('      : {}'.format(round(G_weak.number_of_edges() / float(G_weak.number_of_nodes()), 4)))
5.	
6.	G_strong = G.subgraph(max(nx.strongly_connected_components(G), key=len))
7.	print(' : {}'.format(G_strong.number_of_nodes()))
8.	print(' : {}'.format(G_strong.number_of_edges()))
9.	print('      : {}'.format(round(G_strong.number_of_edges() / float(G_strong.number_of_nodes()), 4)))


 : 986
 : 25552
      : 25.9148

 : 803
 : 24729
      : 30.7958


Entonces, hemos obtenido las características principales para el componente débilmente conectado y para el componente fuerte conectado incluido en él. Veamos qué conclusiones podemos sacar en esta etapa. Vemos que de 1005 participantes, 986 personas se comunican entre sí, mientras que 183 personas de ellos enviaron correos electrónicos a otras personas de forma unilateral, y solo 803 personas mantuvieron una comunicación bidireccional. En 823 casos, falló el intento de establecer la comunicación por correo electrónico. También vemos que los participantes más activos (incluidos en el componente de conexión fuerte) se comunican con un promedio de 30 personas.



Veamos otras características clave de los gráficos que definen su estructura. Los gráficos se consideran ponderados si las relaciones entre los nodos reflejan no solo la existencia misma de la conexión, sino también un cierto peso, que refleja la fuerza de esta conexión. Nuestro conjunto de datos con correos electrónicos no está ponderado, ya que solo tiene en cuenta el hecho de la correspondencia, pero no el número de correos electrónicos enviados.



Además, los nodos y enlaces pueden crear diferentes tipos de redes: monocotiledóneas, dicotiledóneas o multinivel. Las redes monocotiledóneas consisten en un tipo de participantes y las conexiones entre ellos. Las redes bipartitas constan de dos tipos diferentes de participantes, donde un tipo de nodo está asociado solo con el otro tipo. Las redes multinivel también incluyen dos tipos de participantes, pero los vínculos pueden conectar tanto diferentes tipos de participantes como el mismo tipo de participantes (por ejemplo, relaciones entre gerentes y relaciones entre proyectos). El conjunto de datos que hemos tomado para la investigación es una red monocotiledónea, ya que consta de un solo tipo de participantes y las conexiones entre ellos.



3. Visualización de gráficos



Ahora intentemos visualizar el conjunto de datos que hemos tomado. Para esto necesitamos la biblioteca matplotlib, que ya importamos arriba:



1.	plt.figure(figsize=(15, 15))
2.	plt.title('E-mails')
3.	nx.draw(G, node_size=5)
4.	plt.show()


La primera línea establece el tamaño de la imagen futura, que luego se le asigna un nombre específico. La tercera línea de la función de dibujo pasa un gráfico y especifica el tamaño de los nodos, después de lo cual el gráfico se muestra en la pantalla. Echémosle un vistazo:





Fig. 3. Gráfico de interacciones de los usuarios con información sobre todos los correos electrónicos entrantes y salientes entre miembros de la institución de investigación.



En el gráfico resultante vemos que hay una serie de puntos que no están conectados con el resto de participantes de la comunicación. Estas personas, no relacionadas con el resto de participantes, se subieron al gráfico, ya que se enviaban cartas exclusivamente a sí mismos. También puede notar que en la periferia hay una serie de puntos que están conectados con el resto del gráfico por algunas conexiones entrantes; estos son participantes que cayeron en el componente débilmente conectado para nuestro gráfico, pero no cayeron en el componente conectado fuerte.



Veamos también un gráfico que ilustra el fuerte componente de conectividad: personas que tienen una comunicación bidireccional con otros miembros de la institución de investigación:





Fig. 4. Componente de conectividad sólida con información sobre todos los correos electrónicos entrantes y salientes entre miembros de la institución de investigación...



4. Grado de nudo y distribución de grados de nudos



Ahora que conocemos la estructura de nuestro gráfico y podemos visualizarlo, pasemos a un análisis más detallado. Cada nodo del gráfico tiene un grado: el número de vecinos más cercanos de ese nodo. En las redes dirigidas, se puede distinguir tanto el grado de entrada (el número de conexiones entrantes al nodo) como el grado de salida (el número de conexiones salientes del nodo). Si calculamos el grado para cada nodo del gráfico, podemos determinar la distribución de los grados de los nodos. Echemos un vistazo para el gráfico de correo electrónico:



1.	degree = dict(G.degree())
2.	degree_values = sorted(set(degree.values()))
3.	hist = [list(degree.values()).count(x) for x in degree_values]
4.	plt.figure(figsize=(10, 10))
5.	plt.plot(degree_values, hist, 'ro-')
6.	plt.legend(['Degree'])
7.	plt.xlabel('Degree')
8.	plt.ylabel('Number of nodes')
9.	plt.show()




Figura: 5. Distribución de grados en un gráfico con información sobre todos los correos electrónicos entrantes y salientes entre miembros de la institución de investigación



En el gráfico resultante vemos la distribución de grados de nodos: una gran cantidad de nodos tienen pocas conexiones con vecinos, pero hay una pequeña cantidad de nodos grandes que tienen varias conexiones con otros los participantes es enorme. Esta tendencia se denomina ley de distribución de potencia y es muy típica de redes grandes. Esta ley puede describir la distribución de la población de diferentes ciudades, el ranking de sitios en Internet e incluso la distribución de la riqueza entre las personas.



5. Trayectoria, diámetro y distancia media en el gráfico



Ahora determinemos qué tan conectados están los miembros de nuestro gráfico. Primero, hablemos de los diferentes tipos de espaciado de nodos.



Cualquier secuencia de bordes que conecte nodos se llama camino. La mayoría de las veces, la investigación considera una ruta simple, es decir, una ruta sin ciclos y nodos repetidos. El camino más corto entre dos nodos se llama distancia geodésica. El camino más largo y corto en un gráfico se llama diámetro, pero es muy sensible a las desviaciones (una cadena en un gráfico multimillonario puede cambiar su diámetro). En grafos dirigidos, el concepto de diámetro se puede utilizar solo para el componente de conectividad fuerte, porque para calcular el diámetro es necesario que para cada par de nodos exista un camino entre ellos. En gráficos no dirigidos, es suficiente que el componente en consideración esté conectado.



Otra característica muy informativa es la distancia promedio entre nodos, que se puede obtener tomando el promedio de todos los caminos más cortos en el gráfico. La distancia promedio está determinada por la estructura del gráfico: si el gráfico está construido en forma de cadena, será grande, pero cuanto más cerca estén conectados los nodos, menor será la distancia promedio. La distancia media se puede calcular tanto para el componente conectado fuerte como para el componente conectado débilmente:



1.	print (': ', nx.diameter(G_strong))
2.	print ('     : ', nx.average_shortest_path_length(G_strong))
3.	print ('     : ', nx.average_shortest_path_length(G_weak))

: 6
     :  2.5474824768713336
     :  2.164486568301397


Echemos un vistazo a los resultados. El diámetro en este caso nos muestra la distancia máxima entre dos extraños, y aquí, como en la conocida teoría de los seis apretones de manos, esta distancia es 6. La distancia media en los componentes también es pequeña, en promedio dos “apretones de manos” son suficientes para dos desconocidos. Aquí también se puede ver un fenómeno interesante: la distancia promedio en el componente conectado fuerte es ligeramente menor que en el componente débilmente conectado. Esto se puede explicar por el hecho de que la dirección de los enlaces no se tiene en cuenta para el componente débilmente conectado (solo el hecho mismo de su presencia). Debido a esto, la conexión en el componente débil aparece donde estaba ausente para el componente fuerte.



6. Agrupación y asignación de comunidades



Habiendo descubierto las distancias entre los participantes, pasemos a otros fenómenos que reflejan cómo los participantes en el gráfico están conectados entre sí: agrupaciones y comunidades.



El coeficiente de agrupamiento muestra que dos elementos del gráfico, conectados a través del tercer elemento, con un alto grado de probabilidad, están conectados entre sí. Incluso si no están vinculados, la probabilidad de que se vinculen en el futuro es mucho mayor que los otros dos nodos tomados al azar. Este fenómeno, llamado agrupamiento o transitividad, es extremadamente común en los gráficos sociales.



Los gráficos con un alto grado de agrupación se caracterizan por la presencia de un número significativo de tripletes conectados (tres nodos conectados entre sí). Son el componente básico de muchas redes sociales, donde la cantidad de triángulos de este tipo es muy grande. A menudo, ni siquiera surgen triángulos, sino formaciones de conglomerados completos, llamadas comunidades, que están más estrechamente relacionadas entre sí que con el resto del gráfico.



Veamos la agrupación y el coeficiente de agrupación para el componente débilmente conectado:



1.	print (': ', nx.transitivity(G_strong))
2.	print (' : ', nx.average_clustering(G_strong))


:  0.2201493109315837
 :  0.37270757578876434


Para un componente con conectividad fuerte, podemos obtener las mismas características y también determinar el número de nodos centrales (nodos que están más estrechamente relacionados con el resto) y el número de nodos en la periferia del gráfico:



1.	print (': ', nx.transitivity(G_strong))
2.	print (' : ', nx.average_clustering(G_strong))
3.	print ('  : ', len(nx.center(G_strong)))
4.	print ('   : ', len(nx.periphery(G_strong)))


:  0.2328022090200813
 :  0.3905903756516427
  : 46
   : 3


En el segundo caso, la agrupación y el coeficiente de agrupación aumentaron, lo que refleja que el componente conectado fuerte contiene una mayor cantidad de tripletes conectados. Intentemos juntos para definir las comunidades principales en el componente débilmente acoplado:



1.	G_undirected = G_weak.to_undirected()
2.	partition = community.best_partition(G_undirected)
3.	communities = set(partition.values())
4.	communities_dict = {c: [k for k, v in partition.items() if v == c] for c in communities}
5.	highest_degree = {k: sorted(v, key=lambda x: G.degree(x))[-5:] for k, v in communities_dict.items()}
6.	print(' : ', len(highest_degree))
7.	print('    :', ', '.join([str(len(highest_degree[key])) for key in highest_degree]))


 : 8
    : 113, 114, 125, 131, 169, 188, 54, 92


Así, en la columna con correspondencia electrónica se pueden distinguir 8 comunidades, las cuales están más relacionadas entre sí que con el resto de la columna. La más pequeña de las comunidades tiene 54 miembros y la más grande tiene 188 miembros. Para las redes que contienen comunidades superpuestas o anidadas, puede ser más difícil determinar la partición óptima. Por lo tanto, cada vez que ejecuta el código, la composición de las comunidades puede diferir. Veamos la visualización de la división que obtuvimos:



1.	pos = nx.spring_layout(G)
2.	plt.figure(figsize=(15, 15))
3.	cmap = cm.get_cmap('gist_rainbow', max(partition.values()) + 1)
4.	nx.draw_networkx_nodes(G, pos, partition.keys(), node_size=20, cmap=cmap, node_color=list(partition.values()))
5.	nx.draw_networkx_edges(G, pos, alpha=0.5)
6.	plt.show()




Figura: 6. Visualización de varias comunidades en un componente débilmente acoplado con información sobre todos los correos electrónicos entrantes y salientes entre miembros de la institución de investigación



En el gráfico anterior, vemos la distribución de los participantes por comunidad, donde el color de los nodos describe la pertenencia a una comunidad en particular.



7. Reciprocidad de vínculos



Además de las propiedades ya descritas, también existe la reciprocidad en una red dirigida. Esta característica describe qué porcentaje de enlaces salientes tienen un enlace entrante de retroalimentación. Para averiguarlo, usamos la función especial Overall_reciprocity de la biblioteca networkx y observamos el nivel de reciprocidad en el gráfico y sus componentes:



1.	print ('  : ', nx.overall_reciprocity(G))
2.	print ('    : ', nx.overall_reciprocity(G_weak))
3.	print ('    : ', nx.overall_reciprocity(G_strong))

  :  0.6933635759258535
    :  0.6938791484032562
    :  0.7169719762222492


En el 71% de los casos, los usuarios recibieron respuestas a sus mensajes en el componente de conectividad fuerte. Para el componente débilmente conectado y el gráfico completo en su conjunto, el nivel es previsiblemente más bajo.



8. Propiedades universales de las redes



En resumen, las redes complejas, en general, tienen ciertas propiedades y algunas son características de muchas redes. Nuestro análisis del conjunto de datos de correo electrónico respalda bien estas tendencias:



  1. . , , . : web-, , , (Wikipedia, Microsoft). , .
  2. . , , « ». : , .
  3. , , : 80% , . – , , . , , .
  4. , . , .


Analizamos y confirmamos todas las tendencias enumeradas anteriormente para un conjunto de datos con correspondencia de correo electrónico: se encontró un gran componente conectado en el gráfico, que contiene más del 80% de todos los nodos. Dentro de este componente, la mayoría de los nodos se caracterizaron por una pequeña cantidad, sin embargo, hubo un pequeño porcentaje de participantes que tenían una gran cantidad de vecinos. También vimos que el diámetro y la distancia promedio entre los participantes en el gráfico es pequeño: la distancia promedio en el componente débilmente conectado que contiene 986 participantes fue solo 2.1, lo que significa que la mayoría de los participantes están conectados entre sí después de solo dos apretones de manos. Además, el gráfico se caracteriza por un alto grado de reciprocidad: el 69% de todos los participantes mantuvieron un contacto bidireccional entre sí.



Notas



El texto completo del estudio se puede encontrar en el libro de Nikolai Viktorovich Ursul "The Whole Truth About Forex" (2019).



La investigación se describe en el artículo "La aplicación del análisis de redes sociales a la contabilidad y la auditoría" Slobodan Kacanski, Dean Lusher (2017, enlace ).



All Articles