---
id: draw_join_graph_figure_py_datascience
name: draw_join_graph_figure
kind: function
lang: py
domain: datascience
version: "1.0.0"
purity: impure
signature: "def draw_join_graph_figure(join_graph: dict, title: str = None) -> \"matplotlib.figure.Figure\""
description: "Rasteriza el join graph de una base (relaciones FK inter-tabla, salida de build_join_graph) a un matplotlib.figure.Figure: nodos circulares con el nombre de cada tabla (hubs en color de acento cálido, el resto neutro) y aristas dirigidas etiquetadas from_col→to_col (más la cardinalidad si viene). Es la contrapartida dibujada del string Mermaid para que el capítulo de relaciones del informe AutomaticEDA muestre un diagrama real. Layout networkx spring_layout determinista (seed=42), backend Agg sin abrir ventanas; defensivo: nunca lanza y nunca hace I/O."
tags: [eda, plot, relations, graph, matplotlib, figure, networkx, datascience, impure]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [matplotlib, networkx]
example: |
  from draw_join_graph_figure import draw_join_graph_figure
  join_graph = {
      "nodes": [
          {"table": "customers", "out_degree": 0, "in_degree": 1, "role": "dimension"},
          {"table": "orders", "out_degree": 1, "in_degree": 0, "role": "fact"},
      ],
      "edges": [
          {"from_table": "orders", "from_col": "customer_id",
           "to_table": "customers", "to_col": "id", "cardinality": "N:1"},
      ],
      "hubs": ["orders"],
  }
  fig = draw_join_graph_figure(join_graph, title="Relaciones FK")
  fig.savefig("/tmp/join_graph.png")
tested: true
tests:
  - "test_returns_figure_with_axis"
  - "test_savefig_produces_nonempty_png"
  - "test_empty_dict_does_not_raise_and_savefig_png"
  - "test_none_does_not_raise_and_savefig_png"
test_file_path: "python/functions/datascience/draw_join_graph_figure_test.py"
file_path: "python/functions/datascience/draw_join_graph_figure.py"
params:
  - name: join_graph
    desc: "Dict producido por build_join_graph. Claves: `nodes` (list[dict] con table, out_degree, in_degree, role), `edges` (list[dict] con from_table, from_col, to_table, to_col y opcional cardinality/inclusion) y `hubs` (list[str] de tablas hub a destacar en color cálido). Claves ausentes, items no-dict, None o {} se toleran (devuelve Figure con texto, sin lanzar). Los nombres de nodo se derivan también de las aristas, así que un grafo con edges pero sin nodes explícitos igual se dibuja."
  - name: title
    desc: "Título dibujado sobre el diagrama. Si se omite (None) se usa \"Join graph\". Default None."
output: "Un matplotlib.figure.Figure (figsize 7x5) con un único Axes que contiene el diagrama node-link dirigido: tablas como nodos circulares etiquetados (hubs en acento cálido #DD8452, resto en azul neutro #4C72B0) y FKs como flechas dirigidas con etiqueta from_col→to_col (+ cardinalidad). Si join_graph no tiene nodos ni aristas (o es None/{}), devuelve igualmente una Figure con el texto centrado \"Sin relaciones FK detectadas.\"; ante cualquier fallo interno devuelve una Figure con un mensaje genérico (nunca lanza). El caller rasteriza/cierra la figura; la función no la muestra ni la guarda."
---

## Ejemplo

```python
from draw_join_graph_figure import draw_join_graph_figure

# `join_graph` es la salida de build_join_graph (nodes + edges + hubs).
join_graph = {
    "nodes": [
        {"table": "customers", "out_degree": 0, "in_degree": 1, "role": "dimension"},
        {"table": "orders", "out_degree": 2, "in_degree": 0, "role": "fact"},
        {"table": "products", "out_degree": 0, "in_degree": 1, "role": "dimension"},
    ],
    "edges": [
        {"from_table": "orders", "from_col": "customer_id",
         "to_table": "customers", "to_col": "id", "cardinality": "N:1"},
        {"from_table": "orders", "from_col": "product_id",
         "to_table": "products", "to_col": "id", "cardinality": "N:1"},
    ],
    "hubs": ["orders"],  # `orders` se pinta en color de acento (tabla de hechos)
}

fig = draw_join_graph_figure(join_graph, title="Relaciones FK")

# El renderer del informe lo rasteriza; aquí solo persistimos para inspección.
fig.savefig("/tmp/join_graph.png")
```

## Cuando usarla

Úsala en el capítulo de relaciones de un informe AutomaticEDA cuando quieras un
diagrama **dibujado** del esquema relacional, no solo el bloque Mermaid pegable.
Pásale directamente la salida de `build_join_graph` (`nodes` + `edges` + `hubs`)
y obtienes una `matplotlib.figure.Figure` lista para que el renderer perezoso la
rasterice. Es la pareja visual del string Mermaid: Mermaid sirve para pegar en
Markdown/docs que lo soporten; esta función produce la imagen real (PNG/PDF) que
va embebida en informes que no renderizan Mermaid.

## Gotchas

- **Impura por matplotlib.** Fija el backend `Agg` al importar — no abre
  ventanas ni depende de un display. Segura de llamar en lotes desde el
  renderer.
- **Layout determinista (`seed=42`).** Usa `nx.spring_layout(G, seed=42)`, así
  que la misma entrada produce el mismo diagrama (test reproducible). Para
  grafos de 0/1 nodos usa una posición fija centrada en vez del spring layout.
- **No hace I/O.** No llama `plt.show()` ni guarda a disco — solo devuelve la
  `Figure`. Quien la consume la rasteriza y la libera (`plt.close(fig)`) para no
  acumular memoria en informes con muchas tablas.
- **Devuelve una Figure, NO un dict.** A diferencia de `build_join_graph` (que
  devuelve el dict del grafo), esta función devuelve el objeto de figura ya
  dibujado.
- **Defensiva, nunca lanza.** `None`, `{}`, claves ausentes o items malformados
  se manejan sin error: en el peor caso devuelve una `Figure` con
  "Sin relaciones FK detectadas." (vacío) o un mensaje genérico (fallo interno).
  No la envuelvas en try/except por miedo a un raise — no lo hay.
