Saltar al contenido

lección 2

La gramatica de matplotlib: figure, axes y el flujo de trabajo

El modelo mental de matplotlib: figure es el lienzo, axes es el grafico. Aprende el flujo fig-ax que usaras en el 95% de tus visualizaciones.

55 min

Ya sabes que grafico elegir para cada pregunta. Ahora necesitas CONSTRUIRLO — y para eso necesitas una herramienta que te deje controlar cada pixel. Matplotlib es esa herramienta: la base sobre la que se construye practicamente toda la visualizacion en Python. Seaborn se monta encima, pandas.plot() la llama por detras, y la mayoria de librerias cientificas generan figuras de matplotlib. Aprenderla no es aprender UNA libreria: es aprender la gramatica comun de todas las demas. Y su modelo mental — figure, axes, elementos — es lo que te va a permitir pasar de "me sale algo" a "controlo exactamente lo que sale".

El problema de matplotlib es que tiene DOS interfaces, y si no entiendes cual es cual, acabas mezclando codigo que funciona a veces y se rompe en cuanto quieres algo complejo. La interfaz "pyplot" (plt.plot, plt.bar, plt.title) imita a MATLAB: rapida para prototipos, desastrosa para graficos con varios paneles o personalizacion seria. La interfaz "orientada a objetos" (fig, ax = plt.subplots()) es la que usan los profesionales. Es la que vas a aprender aqui.

### El modelo mental: lienzo, marco, grafico

Figure es la hoja de papel. Axes es el recuadro donde dibujas. Puedes tener varios recuadros en una sola hoja.

Piensa en un cuadro: la Figure es el bastidor completo con su marco. Los Axes son los recuadros donde realmente se pinta. Un bastidor puede tener un solo recuadro (lo mas habitual) o varios (cuando quieres comparar dos graficos lado a lado). El error mas comun de principiante es confundir Figure con Axes: llaman plt.title() pensando que va al grafico y en realidad va al axes actual, que con un solo grafico es el que esperas y con dos deja de serlo. Si creas dos paneles y llamas a plt.title(), el titulo se lo lleva el ultimo panel que creaste y el primero se queda mudo. Para el titulo de la figure entera existe plt.suptitle(). Y para no depender nunca de cual es "el actual", escribes ax.set_title() sobre el axes que quieres.

### El patron que usaras siempre: fig, ax = plt.subplots()

1import matplotlib.pyplot as plt
2
3# El patron profesional: SIEMPRE empieza asi
4fig, ax = plt.subplots(figsize=(8, 5))
5
6# Datos
7meses = ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun']
8ingresos = [120, 135, 128, 142, 155, 148]
9
10# Pintar en el axes (NO en plt directamente)
11ax.plot(meses, ingresos, marker='o', color='#a3e635', linewidth=2)
12
13# Configurar el axes
14ax.set_title('Ingresos mensuales S1 2024')
15ax.set_xlabel('Mes')
16ax.set_ylabel('Miles EUR')
17ax.spines['top'].set_visible(False)
18ax.spines['right'].set_visible(False)
19
20# Guardar (actua sobre la figure)
21plt.tight_layout()
22plt.savefig('ingresos_s1.png', dpi=150)
23plt.close()

El flujo completo: crear figure+axes, pintar, configurar, guardar, cerrar

Consejo de senior: nunca uses plt.show() en un script de produccion o en un notebook que se ejecuta automaticamente. plt.show() bloquea la ejecucion esperando que cierres la ventana. Usa plt.savefig() para guardar y plt.close() para liberar. Solo usa plt.show() cuando estas explorando interactivamente en un Jupyter Notebook.

### Los tres graficos basicos: plot, bar, scatter

Con tres metodos cubres el 80% de las necesidades: ax.plot() para lineas temporales, ax.bar() (o ax.barh()) para comparaciones, y ax.scatter() para relaciones. Cada uno acepta los datos como listas o arrays de numpy, y devuelve un objeto que puedes guardar si necesitas modificarlo despues (raro, pero posible).

1import matplotlib.pyplot as plt
2
3# --- Barras verticales ---
4fig, ax = plt.subplots(figsize=(7, 4))
5productos = ['Web', 'App', 'API', 'Widget']
6usuarios = [4500, 3200, 1800, 900]
7ax.bar(productos, usuarios, color='#22d3ee')
8ax.set_title('Usuarios activos por producto')
9ax.set_ylabel('Usuarios')
10plt.tight_layout()
11plt.savefig('barras.png', dpi=150)
12plt.close()
13
14# --- Scatter ---
15fig, ax = plt.subplots(figsize=(7, 5))
16precio = [10, 25, 15, 40, 30, 55, 20, 35]
17satisfaccion = [8.2, 7.5, 8.0, 6.1, 7.0, 5.5, 7.8, 6.8]
18ax.scatter(precio, satisfaccion, color='#f59e0b', s=80, alpha=0.7)
19ax.set_xlabel('Precio (EUR)')
20ax.set_ylabel('Satisfaccion (1-10)')
21ax.set_title('A mayor precio, menor satisfaccion?')
22plt.tight_layout()
23plt.savefig('scatter.png', dpi=150)
24plt.close()

bar() para comparar categorias, scatter() para explorar relaciones

### Personalizar: colores, tamanos y estilos

Matplotlib acepta colores como cadenas hex (#a3e635), como nombres CSS (tomato, steelblue) o como tuplas RGB normalizadas (0.64, 0.90, 0.21). Para un analista, lo practico es usar hex de la paleta de tu empresa o de una paleta perceptualmente uniforme como viridis.

1import matplotlib.pyplot as plt
2
3fig, ax = plt.subplots(figsize=(8, 5))
4
5meses = ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun']
6plan_basic = [200, 210, 215, 220, 225, 230]
7plan_pro = [150, 160, 175, 180, 195, 210]
8
9# Dos lineas con estilos distintos
10ax.plot(meses, plan_basic, marker='o', color='#a3e635',
11 linewidth=2, label='Basic')
12ax.plot(meses, plan_pro, marker='s', color='#22d3ee',
13 linewidth=2, linestyle='--', label='Pro')
14
15# Leyenda, titulos, limpieza
16ax.legend(loc='upper left', frameon=False)
17ax.set_title('Pro crece mas rapido que Basic (+40% vs +15%)')
18ax.set_ylabel('Clientes activos')
19ax.spines['top'].set_visible(False)
20ax.spines['right'].set_visible(False)
21ax.set_ylim(0, 300) # Forzar que el eje Y empiece en 0
22
23plt.tight_layout()
24plt.savefig('crecimiento_planes.png', dpi=150)
25plt.close()

Dos series en el mismo axes: color, estilo de linea y leyenda para distinguirlas

Cuidado con el eje Y que no empieza en cero. En un grafico de barras matplotlib empieza en cero el solo, porque la barra se dibuja desde ahi; aun asi, escribir ax.set_ylim(0, ...) no sobra, porque deja la intencion por escrito y te protege si alguien toca el rango mas adelante. Donde si recorta es en lineas y en scatter: si tus datos van de 98 a 102, matplotlib ajusta el eje de 97,8 a 102,2 y esa diferencia de cuatro puntos ocupa el grafico entero. Ahi la decision es tuya y depende del mensaje: si quieres ensenar la variacion pequena, recortar tiene sentido y hay que decirlo en el titulo; si quieres ensenar la magnitud, pon ax.set_ylim(0, ...). Lo que no vale es recortar sin darte cuenta.

### Subplots: varios graficos en un solo lienzo

Cuando quieres comparar dos graficos lado a lado (ventas vs costes, este trimestre vs el anterior), usas subplots con multiples axes. La llamada plt.subplots(1, 2) crea una fila con dos columnas; plt.subplots(2, 1) crea dos filas con una columna. Cada axes es independiente: tiene su propio titulo, sus propios ejes, sus propios datos.

1import matplotlib.pyplot as plt
2
3fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5), sharey=True)
4
5# Panel izquierdo: ingresos
6meses = ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun']
7ingresos = [120, 135, 128, 142, 155, 148]
8ax1.plot(meses, ingresos, marker='o', color='#a3e635', linewidth=2)
9ax1.set_title('Ingresos S1 2024')
10ax1.set_ylabel('Miles EUR')
11ax1.spines['top'].set_visible(False)
12ax1.spines['right'].set_visible(False)
13
14# Panel derecho: costes
15costes = [80, 82, 85, 88, 90, 92]
16ax2.plot(meses, costes, marker='s', color='#f59e0b', linewidth=2)
17ax2.set_title('Costes S1 2024')
18ax2.set_ylabel('Miles EUR')
19ax2.spines['top'].set_visible(False)
20ax2.spines['right'].set_visible(False)
21
22fig.suptitle('Ingresos crecen mas rapido que costes: margen mejora', fontsize=13)
23plt.tight_layout()
24plt.savefig('ingresos_vs_costes.png', dpi=150)
25plt.close()

Dos paneles lado a lado: la comparacion visual es inmediata

### Guardar como imagen: formatos y resolucion

plt.savefig() acepta cualquier formato que soporte tu sistema: PNG (rasterizado, el mas comun), SVG (vectorial, ideal para web), PDF (vectorial, para documentos). El parametro dpi (dots per inch) controla la resolucion del PNG: 72 dpi es pantalla basica, 150 es presentacion, 300 es impresion. Para un dashboard web, 150 es suficiente. Para un informe que se va a imprimir, usa 300.

1# Guardar en distintos formatos
2plt.savefig('grafico.png', dpi=150, bbox_inches='tight')
3plt.savefig('grafico.svg') # Vectorial, sin dpi
4plt.savefig('grafico.pdf') # Para LaTeX o informes
5
6# bbox_inches='tight' recorta el espacio en blanco sobrante
7# Es mas preciso que tight_layout() para el guardado

Tres formatos habituales y el truco de bbox_inches

Consejo de senior: si tu grafico va a un informe automatizado (un PDF que se genera cada lunes), usa siempre la misma resolucion y tamano. Asi el informe no "salta" visualmente de una semana a otra. Yo tengo un fichero config.py con ANCHO, ALTO, DPI y COLORES que importo en cada script de reporting. Es media hora de setup que te ahorra dolores de cabeza durante meses.

### La interfaz pyplot vs la orientada a objetos

La interfaz pyplot es un atajo que se convierte en trampa. Usa fig, ax desde el primer dia.

### Receta rapida: del dato al grafico en 6 lineas

Una vez que interiorizas el patron, cualquier grafico sigue la misma receta: (1) crear figure+axes, (2) pintar los datos, (3) configurar titulo y etiquetas, (4) limpiar lo que sobra, (5) guardar, (6) cerrar. Lo que cambia es el metodo de pintado (plot/bar/scatter/hist) y los parametros esteticos. La estructura es siempre la misma.

Memoriza este flujo. Cada grafico que hagas seguira estos 6 pasos.

Regístrate para guardar tu progreso.

## comentarios

Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.

Inicia sesión para comentar y responder.

cargando comentarios...