22.1 Receta
Imagine un algoritmo que describa cómo se prepara una receta gastronómica en una cocina institucional. Se compone de ingredientes y pasos de preparación del alimento. El grafo relaciona ingredientes con pasos hasta llegar a servir los platos. De lo posterior no se pasa a lo anterior, por eso son acíclicos. Nunca se llega de un paso posterior a uno anterior. Además, es importante la direccionalidad de las relaciones.
import matplotlib.pyplot as plt
import networkx as nx
G = nx.DiGraph()
G.add_edges_from([
("ingredientes", "8 piezas de ossobuco de 180 gr cada una"),
("ingredientes", "1/2 taza aceite de oliva"),
("ingredientes", "sal al gusto"),
("ingredientes", "pimienta al gusto"),
("ingredientes", "4 cdas. harina de trigo"),
("ingredientes", "4 cucharadas de mantequilla"),
("ingredientes", "1 zanahoria"),
("ingredientes", "1 cebolla cabezona grande"),
("ingredientes", "2 tomates"),
("ingredientes", "2 tallos de apio"),
("ingredientes", "2 dientes de ajo"),
("ingredientes", "4 cdas. pasta de tomate"),
("ingredientes", "1 taza de caldo de carne"),
("ingredientes", "perejil"),
("ingredientes", "2 hojas de laurel"),
("ingredientes", "1 ramita de tomillo"),
("ingredientes", "1 ramita de romero"),
("ingredientes", "1 lb. Espaguetti"),
("ingredientes", "1 taza vino blanco"),
("ingredientes", "queso parmesano rallado"),
("1 lb. Espaguetti", "olla de agua"),
("olla de agua", "hervir"),
("hervir", "escurrir"),
("escurrir", "saltear pasta"),
("perejil", "picar fino"),
("picar fino", "sartén"),
("sartén", "saltear pasta"),
("1 zanahoria", "picar finamente"),
("2 tallos de apio", "picar finamente"),
("2 tomates", "pelar y retirar semillas"),
("pelar y retirar semillas", "picar finamente"),
("1 cebolla cabezona grande", "picar finamente"),
("2 dientes de ajo", "picar finamente"),
("sal al gusto", "olla express con agua"),
("pimienta al gusto", "olla express con agua"),
("8 piezas de ossobuco de 180 gr cada una", "olla express con agua"),
("olla express con agua", "dorar"),
("dorar", "retirar ossobuco"),
("picar finamente", "sofreir"),
("agregar ossobuco", "integrar"),
("dorar", "sofreir"),
("1/2 taza aceite de oliva", "olla express con agua"),
("4 cucharadas de mantequilla", "sartén"),
("1 taza de caldo de carne", "agregar consomé"),
("sofreir", "integrar"),
("integrar", "remover"),
("remover", "reducir"),
("2 hojas de laurel", "integrar"),
("1 ramita de tomillo", "integrar"),
("1 ramita de romero", "olla express con agua"),
("4 cdas. pasta de tomate", "sofreir"),
("4 cdas. harina de trigo", "integrar"),
("1 taza vino blanco", "integrar"),
("reducir", "agregar consomé"),
("agregar consomé", "cocinar 40 min"),
("cocinar 40 min", "emplatar"),
("emplatar", "bañar en su propia salsa"),
("bañar en su propia salsa", "servir"),
("queso parmesano rallado", "servir"),
("saltear pasta", "servir")])
nx.draw(G, with_labels = True, font_size = 10, alpha = 0.5)
Debe haber manera de controlar la posición en que se ubican los nodos. Un pequeño ejemplo:
import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph()
G.add_edge('0','1')
G.add_edge('1','2')
G.add_edge('2','0')
G.add_edge('0','3')
G.add_edge('1','4')
G.add_edge('5','0')
pos={'0':(1, 0),'1':(1, 1),'2':(2, 3),'3':(3, 2),'4':(1, 1.80),'5':(0, 2)} # node:(x,y)
nx.set_node_attributes(G, pos, 'coord')
nx.draw(G, pos=pos, with_labels=True)
Especifiquemos la receta de manera legible:
G = nx.DiGraph()
pos = {"ingredientes":(1,-4),
"8 piezas de ossobuco de 180 gr cada una":(2, -13),
"2 dientes de ajo":(2, -1), "1 cebolla cabezona grande":(2, 7),
"2 tallos de apio":(2,1),
"1 zanahoria":(2, 5),
"2 tomates":(2,3),
"1 taza vino blanco":(2, -15),
"perejil":(2, 15), "picar fino":(4, 15),
"saltear pasta":(6, 13),
"1 taza de caldo de carne":(2, 9),
"4 cdas. pasta de tomate":(2, -3),
"2 hojas de laurel":(2,-19),
"1 ramita de tomillo":(2, -21),
"1 ramita de romero": (2,-5),
"4 cdas. harina de trigo":(2, -17),
"4 cucharadas de mantequilla":(2, 18),
"1 lb. Espaguetti":(2, 13),
"queso parmesano rallado": (2, 11),
"1/2 taza aceite de oliva":(2,-7),
"sal al gusto":(2,-11),
"pimienta al gusto":(2, -9),
"olla de agua":(3, 13),
"hervir":(4, 13),
"olla express con agua":(3, -5),
"dorar":(4, -5), "escurrir":(5, 13),
"sartén":(5, 15),
"picar finamente":(5, -3),
"pelar y retirar semillas": (3, 1),
"remover":(6, -3),
"reducir":(6, -1),
"integrar":(6, -5),
"retirar ossobuco":(4, -7),
"sofreir":(5, -5),
"agregar ossobuco":(6, -9),
"agregar consomé":(6, 1),
"cocinar 40 min":(6, 3),
"emplatar": (6, 5),
"bañar en su propia salsa":(6, 7),
"servir":(6, 9)
}
G.add_edges_from([
("ingredientes", "8 piezas de ossobuco de 180 gr cada una"),
("ingredientes", "1/2 taza aceite de oliva"),
("ingredientes", "sal al gusto"),
("ingredientes", "pimienta al gusto"),
("ingredientes", "4 cdas. harina de trigo"),
("ingredientes", "4 cucharadas de mantequilla"),
("ingredientes", "1 zanahoria"),
("ingredientes", "1 cebolla cabezona grande"),
("ingredientes", "2 tomates"),
("ingredientes", "2 tallos de apio"),
("ingredientes", "2 dientes de ajo"),
("ingredientes", "4 cdas. pasta de tomate"),
("ingredientes", "1 taza de caldo de carne"),
("ingredientes", "perejil"),
("ingredientes", "2 hojas de laurel"),
("ingredientes", "1 ramita de tomillo"),
("ingredientes", "1 ramita de romero"),
("ingredientes", "1 lb. Espaguetti"),
("ingredientes", "1 taza vino blanco"),
("ingredientes", "queso parmesano rallado"),
("1 lb. Espaguetti", "olla de agua"),
("olla de agua", "hervir"),
("hervir", "escurrir"),
("escurrir", "saltear pasta"),
("perejil", "picar fino"),
("picar fino", "sartén"),
("sartén", "saltear pasta"),
("1 zanahoria", "picar finamente"),
("2 tallos de apio", "picar finamente"),
("2 tomates", "pelar y retirar semillas"),
("pelar y retirar semillas", "picar finamente"),
("1 cebolla cabezona grande", "picar finamente"),
("2 dientes de ajo", "picar finamente"),
("sal al gusto", "olla express con agua"),
("pimienta al gusto", "olla express con agua"),
("8 piezas de ossobuco de 180 gr cada una", "olla express con agua"),
("olla express con agua", "dorar"),
("dorar", "retirar ossobuco"),
("picar finamente", "sofreir"),
("agregar ossobuco", "integrar"),
("dorar", "sofreir"),
("1/2 taza aceite de oliva", "olla express con agua"),
("4 cucharadas de mantequilla", "sartén"),
("1 taza de caldo de carne", "agregar consomé"),
("sofreir", "integrar"),
("integrar", "remover"),
("remover", "reducir"),
("2 hojas de laurel", "integrar"),
("1 ramita de tomillo", "integrar"),
("1 ramita de romero", "olla express con agua"),
("4 cdas. pasta de tomate", "sofreir"),
("4 cdas. harina de trigo", "integrar"),
("1 taza vino blanco", "integrar"),
("reducir", "agregar consomé"),
("agregar consomé", "cocinar 40 min"),
("cocinar 40 min", "emplatar"),
("emplatar", "bañar en su propia salsa"),
("bañar en su propia salsa", "servir"),
("queso parmesano rallado", "servir"),
("saltear pasta", "servir")
])
plt.figure(figsize=(10, 8))
plt.subplots_adjust(left=0.1, right=1, top=1, bottom=0.1)
nx.draw(G, with_labels=False, font_size=50, pos=pos, alpha=0.7)
for node, (x, y) in pos.items():
plt.text(x, y - 0.6, node, fontsize=8, ha="center")
plt.title("Espaguetti con Ossobuco", fontname="Sans", fontsize=20, fontweight="bold",y=0.96)
No es usual usar redes para representar procesos, si bien los procesos son RAG. Hay programas especializados para representarlos, donde es más fácil ubicar la posición de cada nodo.
También debe ser posible controlar el color de los nodos. Un pequeño ejemplo:
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
G = nx.barabasi_albert_graph(20, 1)
color_state_map = {-1: 'red', 0: 'black', 1: 'blue'}
states = np.random.choice([-1, 0, 1], size = 20)
nx.set_node_attributes(G, dict(zip(G.nodes(), states)), 'state')
plt.figure(figsize = (12, 8))
nx.draw(G, with_labels = True,
node_color = [color_state_map[node[1]['state']]
for node in G.nodes(data = True)],
node_size = 600, font_color = 'white')
Ahora aplicado sobre la receta. Se especifica qué nodos tienen cada color.
G = nx.DiGraph()
pos = {"ingredientes":(1,-4),
"8 piezas de ossobuco de 180 gr cada una":(2, -13),
"2 dientes de ajo":(2, -1),
"1 cebolla cabezona grande":(2, 7),
"2 tallos de apio":(2,1),
"1 zanahoria":(2, 5), "2 tomates":(2,3),
"1 taza vino blanco":(2, -15),
"perejil":(2, 15),
"picar fino":(4, 15),
"saltear pasta":(6, 13),
"1 taza de caldo de carne":(2, 9),
"4 cdas. pasta de tomate":(2, -3),
"2 hojas de laurel":(2,-19), "1 ramita de tomillo":(2, -21),
"1 ramita de romero": (2,-5),
"4 cdas. harina de trigo":(2, -17),
"4 cucharadas de mantequilla":(2, 18),
"1 lb. Espaguetti":(2, 13),
"queso parmesano rallado": (2, 11),
"1/2 taza aceite de oliva":(2,-7),
"sal al gusto":(2,-11),
"pimienta al gusto":(2, -9),
"olla de agua":(3, 13),
"hervir":(4, 13),
"olla express con agua":(3, -5),
"dorar":(4, -5),
"escurrir":(5, 13),
"sartén":(5, 15),
"picar finamente":(5, -3),
"pelar y retirar semillas": (3, 1),
"remover":(6, -3),
"reducir":(6, -1),
"integrar":(6, -5),
"retirar ossobuco":(4, -7),
"sofreir":(5, -5),
"agregar ossobuco":(6, -9),
"agregar consomé":(6, 1),
"cocinar 40 min":(6, 3),
"emplatar": (6, 5),
"bañar en su propia salsa":(6, 7),
"servir":(6, 9)
}
G.add_edges_from([
("ingredientes", "8 piezas de ossobuco de 180 gr cada una"),
("ingredientes", "1/2 taza aceite de oliva"),
("ingredientes", "sal al gusto"),
("ingredientes", "pimienta al gusto"),
("ingredientes", "4 cdas. harina de trigo"),
("ingredientes", "4 cucharadas de mantequilla"),
("ingredientes", "1 zanahoria"),
("ingredientes", "1 cebolla cabezona grande"),
("ingredientes", "2 tomates"),
("ingredientes", "2 tallos de apio"),
("ingredientes", "2 dientes de ajo"),
("ingredientes", "4 cdas. pasta de tomate"),
("ingredientes", "1 taza de caldo de carne"),
("ingredientes", "perejil"),
("ingredientes", "2 hojas de laurel"),
("ingredientes", "1 ramita de tomillo"),
("ingredientes", "1 ramita de romero"),
("ingredientes", "1 lb. Espaguetti"),
("ingredientes", "1 taza vino blanco"),
("ingredientes", "queso parmesano rallado"),
("1 lb. Espaguetti", "olla de agua"),
("olla de agua", "hervir"),
("hervir", "escurrir"),
("escurrir", "saltear pasta"),
("perejil", "picar fino"),
("picar fino", "sartén"),
("sartén", "saltear pasta"),
("1 zanahoria", "picar finamente"),
("2 tallos de apio", "picar finamente"),
("2 tomates", "pelar y retirar semillas"),
("pelar y retirar semillas", "picar finamente"),
("1 cebolla cabezona grande", "picar finamente"),
("2 dientes de ajo", "picar finamente"),
("sal al gusto", "olla express con agua"),
("pimienta al gusto", "olla express con agua"),
("8 piezas de ossobuco de 180 gr cada una", "olla express con agua"),
("olla express con agua", "dorar"),
("dorar", "retirar ossobuco"),
("picar finamente", "sofreir"),
("agregar ossobuco", "integrar"),
("dorar", "sofreir"),
("1/2 taza aceite de oliva", "olla express con agua"),
("4 cucharadas de mantequilla", "sartén"),
("1 taza de caldo de carne", "agregar consomé"),
("sofreir", "integrar"),
("integrar", "remover"),
("remover", "reducir"),
("2 hojas de laurel", "integrar"),
("1 ramita de tomillo", "integrar"),
("1 ramita de romero", "olla express con agua"),
("4 cdas. pasta de tomate", "sofreir"),
("4 cdas. harina de trigo", "integrar"),
("1 taza vino blanco", "integrar"),
("reducir", "agregar consomé"),
("agregar consomé", "cocinar 40 min"),
("cocinar 40 min", "emplatar"),
("emplatar", "bañar en su propia salsa"),
("bañar en su propia salsa", "servir"),
("queso parmesano rallado", "servir"),
("saltear pasta", "servir")
])
proceso1 = {"4 cucharadas de mantequilla",
"perejil",
"1 lb. Espaguetti",
"olla de agua",
"hervir",
"picar fino",
"sartén",
"escurrir",
"saltear pasta"}
proceso2 = {"1 taza vino blanco",
"2 hojas de laurel",
"4 cdas. harina de trigo",
"1 ramita de tomillo", "agregar ossobuco"}
proceso3 = {"1 ramita de romero",
"1/2 taza aceite de oliva",
"pimienta al gusto",
"sal al gusto",
"8 piezas de ossobuco de 180 gr cada una",
"olla express con agua",
"dorar",
"retirar ossobuco",
"agregar ossobuco"}
inicio = {"ingredientes"}
restantes = {n for n in G.nodes if n not in proceso1|proceso2|proceso3|inicio}
c_pr1, c_pr2, c_pr3, c_restantes, c_inicio = "#0077bb","#33bbee","#44aa99", "#ddcc77", "#999933"
plt.figure(figsize=(10, 8))
plt.subplots_adjust(left=0.1, right=1, top=1, bottom=0.1)
nx.draw_networkx_edges(G, pos=pos, alpha=0.5)
nx.draw_networkx_nodes(G, pos=pos, nodelist=list(proceso1), node_color=c_pr1)
nx.draw_networkx_nodes(G, pos=pos, nodelist=list(proceso2), node_color=c_pr2)
nx.draw_networkx_nodes(G, pos=pos, nodelist=list(proceso3), node_color=c_pr3)
nx.draw_networkx_nodes(G, pos=pos, nodelist=list(inicio), node_color=c_inicio)
nx.draw_networkx_nodes(G, pos=pos, nodelist=list(restantes), node_color=c_restantes)
# nx.draw(G, with_labels=False, pos=pos, alpha=0.7)
for node, (x, y) in pos.items():
plt.text(x, y - 0.6, node, fontsize=8, ha="center")
plt.title("Espaguetti con Ossobuco", fontname="Sans", fontsize=20, fontweight="bold", y=0.96)
Cualquier variable categórica se podrá usar como clasificador de color.