Saltar al contenido

# analisis-de-grafos-en-python-y-networkx

Análisis de Grafos en Python y NetworkX

Volver a la portada del curso
Notebook

Manipulación Avanzada de Grafos

Sección 3 · Manipulación de Grafos en Python

Notebook: manipulación avanzada de grafos

En el video anterior construiste la red de productos de PyShop a partir de los pedidos. En este notebook la manipulas: lees y escribes atributos, recortas subgrafos, filtras aristas por peso, renombras nodos, combinas grafos, guardas la red en disco y trabajas con un grafo dirigido. Esta guía acompaña al notebook manipulacion-avanzada-de-grafos y te dice qué buscar en cada sección y qué resultados debes obtener.

Objetivos

  • Consultar y usar atributos de nodos y aristas.
  • Extraer subgrafos con subgraph, ego_graph y k_core.
  • Filtrar aristas por peso y entender cómo cambia la red con el umbral.
  • Distinguir una vista de una copia.
  • Renombrar nodos, combinar grafos y guardarlos en GraphML o como lista de aristas.
  • Recorrer un DiGraph con sucesores, predecesores y componentes fuertes.

1. Datos y atributos

La primera celda copia datos.py; el notebook es autocontenido. Cada nodo tiene el atributo categoria y cada arista el atributo weight:

G = red_productos()
print(G.nodes["laptop"])            # {'categoria': 'computo'}
print(G.edges["laptop", "mouse"])   # {'weight': 134}
cat = nx.get_node_attributes(G, "categoria")
print(Counter(cat.values()))

El conteo da celulares 7, computo 7, cocina 7, hogar 6, deportes 6, audio 5 y accesorios 3 (los tres productos puente). G.nodes(data=True) y G.edges(data=True) devuelven todos los atributos a la vez.

2. Subgrafos

Un subgrafo conserva un conjunto de nodos y las aristas entre ellos. Con los 7 productos de cómputo:

computo = [n for n, c in cat.items() if c == "computo"]
S = G.subgraph(computo)
print(S.number_of_nodes(), S.number_of_edges())   # 7 16
print(round(nx.density(S), 3))                    # 0.762

La densidad del grupo es 0.762, contra 0.166 de la red completa: dentro de una categoría casi todo se compra con todo. Dos recortes más:

  • nx.ego_graph(G, "cafetera") devuelve la cafetera y sus vecinos: 9 nodos y 26 aristas, entre ellos foco inteligente, lámpara de escritorio y termo. Con radius=2 crece a 21 nodos y 64 aristas.
  • nx.k_core(G, 4) conserva el subgrafo donde todo nodo tiene al menos 4 vecinos dentro de él: 34 nodos y 124 aristas. Quedan fuera cuchillos, mochila para laptop, organizador, power bank, reloj deportivo, tabla para picar y tocadiscos. El núcleo más alto, core_number 7, lo forman 8 productos: 5 de celulares, 2 de audio y el cable usb-c.

3. Filtrar por peso

Para quedarte solo con los pares que se compran muy seguido, filtra las aristas:

fuertes = [(a, b) for a, b, w in G.edges(data="weight") if w >= 100]
F = G.edge_subgraph(fuertes)
print(F.number_of_nodes(), F.number_of_edges())   # 20 19
print(nx.number_connected_components(F))          # 6

Quedan 19 aristas entre 20 productos, repartidas en 6 grupos sueltos. La más fuerte une audífonos inalámbricos con audífonos de diadema (186 pedidos). edge_subgraph descarta los nodos que se quedan sin aristas; si en lugar de eso copias la red y borras las aristas débiles con remove_edges_from, los 41 nodos se conservan y aparecen 27 componentes, la mayoría nodos sueltos. Elige según la pregunta.

4. Componentes y el efecto del umbral

Con el umbral de 25 la red tiene 2 componentes: una de 40 productos y cuchillos, aislado. El notebook reconstruye la red con varios mínimos:

Mínimo de pedidosAristasDensidadComponentes
15940.7241
102430.2961
251360.1662
40910.1116
60480.05916

Con mínimo 1 casi todo se conecta con todo y no se distingue nada; con 60 la red se rompe en 16 pedazos. El 25 deja una red con estructura y casi toda conectada.

5. Vistas y copias

G.subgraph(...) no copia datos: es una vista de solo lectura. Si intentas S.add_node("x") obtienes NetworkXError: Frozen graph can't be modified. Para modificar un subgrafo, primero S.copy(). La ventaja de la vista es que no duplica memoria y refleja los cambios del grafo original.

6. Renombrar y combinar

  • nx.relabel_nodes(G, {n: n.upper() for n in G}) devuelve una red con los nombres en mayúsculas; "laptop" ya no existe en ella.
  • nx.convert_node_labels_to_integers(G, label_attribute="nombre") numera los nodos de 0 a 40 y guarda el nombre original como atributo: el nodo 0 es {'categoria': 'celulares', 'nombre': 'celular gama media'}.
  • nx.compose(A, B) une dos grafos y fusiona los nodos con el mismo nombre. nx.union(A, B) exige que no compartan nodos: si los comparten lanza NetworkXError y sugiere rename o disjoint_union.

7. Guardar y leer

nx.write_graphml(G, "red.graphml")
H = nx.read_graphml("red.graphml")
print(H.number_of_nodes(), H.number_of_edges())   # 41 136
nx.write_weighted_edgelist(G, "red.txt", delimiter=";")
E = nx.read_weighted_edgelist("red.txt", delimiter=";")
print(E.number_of_nodes(), E.number_of_edges())   # 40 136

GraphML conserva los atributos y el nodo aislado. La lista de aristas pierde a cuchillos, porque un nodo sin aristas no aparece en ninguna línea, y pierde la categoría. Usa GraphML para guardar la red completa y la lista de aristas para intercambiar datos simples.

8. Grafo dirigido

El notebook arma el DiGraph de ejemplo Ana→Beto, Ana→Caro, Beto→Caro, Caro→Dani, Dani→Ana y Eva→Caro. D.successors("Ana") da Beto y Caro; D.predecessors("Caro") da Ana, Beto y Eva, así que el grado de entrada de Caro es 3 y el de Eva, 0. El grafo es débilmente conexo, pero no fuertemente conexo: sus componentes fuertes son {Ana, Beto, Caro, Dani} y {Eva}, porque nadie llega a Eva. D.to_undirected() lo convierte en un grafo de 6 aristas.

Qué debes obtener

Subgrafo de cómputo con 7 nodos, 16 aristas y densidad 0.762; ego de la cafetera con 9 nodos y 26 aristas; 4-core de 34 nodos; 19 aristas con peso de al menos 100; GraphML con 41 nodos y la lista de aristas con 40. Si tus números difieren, revisa la semilla (semilla=42) y el umbral (minimo=25). Como práctica, calcula el ego de radio 1 del cable usb-c y compara su densidad con la del subgrafo de cómputo.

Recursos