Capítulo 22 Introducción a la visualización de redes
Ya se ha visto que las redes son uno de los tipos de conjuntos de datos que puede haber. Tienen relación directa con la denominada red social “https://es.wikipedia.org/wiki/Red_social”.
Para representarla en python, una librería es networkx.
El inicio de la presente lección se tomó del canal de Youtube: Python Tutorial for Digital Humanities.
\(1^{ra}\) opción. Generar poco a poco nodos e irlos interconectando.
G = nx.Graph()
# Crear nodos individuales
G.add_node('Marta')
G.add_node('Mario')
# Unir los nodos
G.add_edge('Marta', 'Mario')
fig, ax = plt.subplots()
nx.draw(G, with_labels = True, font_size = 12, alpha = 0.5, ax = ax)
\(2^{da}\) opción. Crea directamente la interconexión. Se crean los nodos como consecuencia:
G = nx.Graph()
# Enlace entre nodos
G.add_edge('Marta', 'Mario')
nx.draw(G, with_labels = True, font_size = 12, alpha = 0.5)
\(3^{ra}\) opción. Leer una lista de parejas e interconectarlos.
G = nx.Graph()
# Enlace entre nodos
amigos = [('Marta', 'Mario'), ('Isabel','Ana'), ('Ana','Arturo'),
('Manuel','Connie'), ('Marta','Miguel'), ('Augusto','Ana'),
('Alberto','Alberto')]
G.add_edges_from(amigos)
nx.draw(G, with_labels = True, font_size = 12, alpha = 0.5)
Podemos controlar algunas características. Por ejemplo, eliminar los bucles:
G.remove_edges_from([('Alberto','Alberto')])
nx.draw(G, with_labels = True, font_size = 12, alpha = 0.5)
Alberto quedó aislado.
Simulemos una red.
import numpy as np
import pandas as pd
G = nx.DiGraph()
num_nodos = 10
G = nx.gn_graph(num_nodos, seed = 25)
nx.draw(G, with_labels = True, font_size = 12, alpha = 0.5)
Se usó nx.DiGraph para indicar dirección de las relaciones. Este tipo de relaciones, denominados grafos acíclicos dirigidos (o DAG, por sus siglas en inglés) implican añadir flecha de dirección a los enlaces.
Sea una simulación más sofisticada. Se carga faker, un generador de datos aleatorios.
import networkx as nx
import matplotlib.pyplot as plt
# pip install faker
from faker import Faker
fraude = Faker()
fraude.random.seed(4321)
num_nodos = 100
nombre1 = set()
nombre2 = set()
pais1 = set()
pais2 = set()
for i in range(0, num_nodos):
nombre1.add(fraude.first_name())
nombre2.add(fraude.first_name())
paises = ["Colombia", "Venezuela"]
import random
random.seed(1516)
dnombres = list(zip(nombre1, nombre2))
G = nx.Graph()
G.add_edges_from(dnombres)# Unir ambos conjuntos de nombres y asignar aleatoriamente un país a cada nombre.
todos_los_nombres = nombre1.union(nombre2)
nombres_pais = {name: fraude.random.choice(paises) for name in todos_los_nombres}
# Colores de los nodos por país
colores = {'Colombia': 'steelblue', 'Venezuela': 'firebrick'}
color_nodos = [colores[nombres_pais[node]] for node in G.nodes()]
# plt.figure(figsize=(10, 6))
nx.draw(G, with_labels=True, node_color = color_nodos, node_size = 80, alpha = 0.8, font_size = 8)
plt.title('Grafo con nodos segun país')
De nuevo, los nodos enlazados consigo mismo, se eliminan.
G.remove_edges_from(nx.selfloop_edges(G))
# plt.figure(figsize=(10, 6))
nx.draw(G, with_labels=True, node_color=color_nodos, node_size=80, alpha=0.8, font_size=10)
plt.title('Grafo con nodos segun país')
Las etiquetas quedan encima de los nodos y no siempre es la mejor opción. Una alternativa es introducir una perturbación aleatoria en la posición de cada etiqueta, para que se aparte un poco del nodo:
import numpy as np
# jitter function
def jitter_positions(positions, cantidad=0.1):
jittered = {}
for node, (x, y) in positions.items():
jittered[node] = (x + np.random.uniform(-cantidad, cantidad),
y + np.random.uniform(-cantidad, cantidad))
return jittered
pos = nx.spring_layout(G)
jittered_pos = jitter_positions(pos)
# plt.figure(figsize=(10, 6))
nx.draw_networkx_nodes(G, pos, node_color=color_nodos, node_size=80, alpha=0.8)
nx.draw_networkx_edges(G, pos)
nx.draw_networkx_labels(G, jittered_pos, font_size=10)
plt.title('Grafo con nodos segun país')
Pero dejarlo al azar no es la mejor idea. Es mejor aplicar un algoritmo que verifique que no haya superposición de etiquetas. Hay una librería que realiza esto: adjustText.
# pip install adjustText
from adjustText import adjust_text
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos, node_color = color_nodos, node_size = 80, alpha = 0.8)
nx.draw_networkx_edges(G, pos)
texts = [plt.text(pos[node][0], pos[node][1], node, fontsize = 6) for node in G.nodes()]
adjust_text(texts)
plt.title('Grafo con nodos segun país')