Python
Matplotlib: graficar con pyplot
Cómo dibujar líneas, barras, dispersión e histogramas con pyplot, cómo poner títulos y leyendas, y en qué se diferencia la interfaz plt de trabajar con figure y axes.
El problema: una lista de 500 números no dice nada
Con NumPy ya puedes calcular promedios y máximos de miles de valores. Pero imprimirlos no ayuda a entenderlos: nadie mira 500 números en la consola y ve una tendencia. Un gráfico sí la muestra de un vistazo.
Matplotlib es la librería estándar de Python para eso, y pyplot es su interfaz simple: un conjunto de funciones que van armando el gráfico paso a paso.
pip install matplotlibimport matplotlib.pyplot as pltas plt es, igual que np para NumPy, la convención universal.
El gráfico mínimo
import matplotlib.pyplot as plt
meses = [1, 2, 3, 4, 5]
ventas = [120, 145, 130, 180, 210]
plt.plot(meses, ventas)
plt.show()plot(x, y) dibuja una línea uniendo los puntos, y show() abre la ventana con el resultado.
plt.show() va al final, una sola vez, después de todo lo que quieras dibujar. Es un error muy común llamarlo después de cada plot y terminar con varias ventanas de un elemento cada una en vez de un gráfico completo. En un notebook de Jupyter se puede omitir, porque el gráfico se muestra solo.
Un gráfico que se entiende solo
Una línea sin etiquetas no comunica nada. Estas cuatro funciones convierten un dibujo en un gráfico:
import matplotlib.pyplot as plt
meses = [1, 2, 3, 4, 5]
ventas = [120, 145, 130, 180, 210]
plt.plot(meses, ventas, label="Sucursal centro")
plt.title("Ventas del primer semestre")
plt.xlabel("Mes")
plt.ylabel("Unidades vendidas")
plt.legend()
plt.grid(True)
plt.show()| Función | Qué agrega |
|---|---|
plt.title("...") | El título arriba del gráfico |
plt.xlabel(...) / plt.ylabel(...) | El nombre de cada eje |
plt.legend() | El recuadro que explica cada serie |
plt.grid(True) | La cuadrícula de fondo |
plt.legend() solo muestra las series que tengan label=. Si llamas a legend() y no aparece nada (con una advertencia sobre “No artists with labels found”), es que te faltó el label en el plot.
Varias series en un mismo gráfico
Llamar a plot varias veces antes del show superpone las líneas:
meses = [1, 2, 3, 4, 5]
centro = [120, 145, 130, 180, 210]
norte = [90, 100, 160, 150, 170]
plt.plot(meses, centro, label="Centro")
plt.plot(meses, norte, label="Norte")
plt.title("Ventas por sucursal")
plt.xlabel("Mes")
plt.ylabel("Unidades")
plt.legend()
plt.show()Matplotlib les asigna colores distintos automáticamente. Si quieres controlarlos, plot acepta parámetros de estilo:
plt.plot(meses, centro, color="crimson", linestyle="--", marker="o", linewidth=2)| Parámetro | Para qué | Valores típicos |
|---|---|---|
color | Color de la línea | "red", "crimson", "#2ecc71" |
linestyle | Tipo de trazo | "-" sólida, "--" guiones, ":" puntos |
marker | Marca en cada punto | "o" círculo, "s" cuadrado, "^" triángulo |
linewidth | Grosor | Un número |
alpha | Transparencia | De 0 a 1 |
label | Nombre en la leyenda | Un texto |
Los cuatro tipos de gráfico que más vas a usar
Cambia la función y cambia el gráfico; el resto (títulos, etiquetas, show) es idéntico.
Línea: cómo evoluciona algo en el tiempo
plt.plot(meses, ventas)Es el gráfico por defecto y el correcto cuando el eje X tiene un orden natural — meses, días, distancias. Unir los puntos con una línea afirma que hay continuidad entre ellos.
Barras: comparar categorías
productos = ["Café", "Té", "Mate", "Jugo"]
vendidos = [340, 180, 420, 90]
plt.bar(productos, vendidos, color="teal")
plt.title("Unidades vendidas por producto")
plt.ylabel("Unidades")
plt.show()bar es lo correcto cuando el eje X son categorías sin orden. Usar una línea aquí sería un error de lectura: sugeriría que hay una progresión del café al té que no existe. Para barras horizontales, plt.barh(...).
Dispersión: la relación entre dos variables
import numpy as np
horas = np.array([1, 2, 2, 3, 4, 4, 5, 6, 7, 8])
notas = np.array([3, 4, 5, 5, 6, 7, 7, 8, 9, 9])
plt.scatter(horas, notas, color="darkorange", alpha=0.7)
plt.title("Horas de estudio vs. nota")
plt.xlabel("Horas")
plt.ylabel("Nota")
plt.show()scatter dibuja puntos sueltos, sin unirlos. Es lo indicado cuando cada punto es una observación independiente y lo que buscas es ver si hay una tendencia entre las dos variables.
Histograma: cómo se reparten los valores
import numpy as np
datos = np.random.normal(170, 10, 1000)
plt.hist(datos, bins=30, color="mediumpurple", edgecolor="white")
plt.title("Distribución de estaturas")
plt.xlabel("cm")
plt.ylabel("Frecuencia")
plt.show()hist recibe una sola lista de valores, los agrupa en intervalos (bins) y dibuja cuántos cayeron en cada uno. No confundir con bar: en bar tú das la altura de cada barra; en hist la altura la cuenta Matplotlib.
bins cambia mucho la lectura del histograma: pocos intervalos esconden detalle, demasiados lo llenan de ruido. Entre 20 y 50 suele ser un buen punto de partida para unos cuantos cientos de datos.
Varios gráficos en una figura: subplots
Para poner gráficos lado a lado, subplot(filas, columnas, posición):
plt.subplot(1, 2, 1)
plt.plot(meses, centro)
plt.title("Centro")
plt.subplot(1, 2, 2)
plt.bar(productos, vendidos)
plt.title("Productos")
plt.tight_layout()
plt.show()subplot(1, 2, 1) significa “una fila, dos columnas, estoy trabajando en la primera”. Todo lo que dibujes después va ahí, hasta que llames al siguiente subplot. tight_layout() reacomoda los márgenes para que los títulos no se pisen.
La posición se cuenta desde 1, no desde 0 — es de los pocos lugares de Python donde eso pasa, y confunde a todo el mundo la primera vez.
plt.* y el enfoque orientado a objetos
En documentación y respuestas de internet vas a ver dos estilos distintos, y conviene reconocerlos:
plt.plot(x, y)
plt.title("Título")
plt.show()fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_title("Título")
plt.show()Los dos producen el mismo gráfico. La diferencia es dónde va el resultado:
plt.algo(...)actúa sobre “el gráfico actual”, un estado global que Matplotlib mantiene por detrás. Cómodo para algo rápido.fig, ax = plt.subplots()te devuelve los objetos:figes la ventana entera yaxlos ejes de un gráfico concreto. Cada llamada dice explícitamente sobre cuál actúa.
Fíjate en el detalle que más marea al pasar de uno a otro: los nombres cambian. plt.title(...) se convierte en ax.set_title(...), plt.xlabel(...) en ax.set_xlabel(...). La regla es que casi todo gana el prefijo set_.
Para un gráfico suelto, el estilo plt alcanza y es más corto. Cuando hay varios subplots o el gráfico se arma dentro de una función, el estilo con ax evita el error clásico de dibujar sobre el gráfico equivocado porque el “actual” no era el que creías.
Guardar en vez de mostrar
plt.savefig("ventas.png", dpi=150, bbox_inches="tight")savefig guarda el gráfico en un archivo — la extensión decide el formato (.png, .pdf, .svg). dpi controla la resolución y bbox_inches="tight" recorta el margen blanco sobrante.
savefig tiene que ir antes de show(). show() vacía la figura al cerrarse, así que si guardas después obtienes un PNG en blanco. Es uno de los errores más frustrantes de Matplotlib justamente porque no da ningún mensaje de error.
NumPy y pyplot juntos
Es la combinación para la que las dos librerías fueron pensadas: NumPy genera y transforma los datos, pyplot los dibuja.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 200)
y = np.sin(x)
plt.plot(x, y, label="sen(x)")
plt.plot(x, np.cos(x), label="cos(x)", linestyle="--")
plt.title("Seno y coseno")
plt.xlabel("x (radianes)")
plt.ylabel("valor")
plt.axhline(0, color="gray", linewidth=0.8)
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()Aquí se ve por qué linspace era la función correcta para esto: 200 puntos repartidos parejo entre 0 y 2π dan una curva suave. Con arange habrías tenido que calcular a mano el paso para que entraran 200.
Pon a prueba
Tienes la cantidad de películas vistas por género: comedia 12, drama 8, terror 15, documental 4. Quieres comparar cuál se ve más. ¿Qué función de pyplot corresponde?
Este código se ejecuta sin fallar, pero produce dos ventanas en vez de una, sin leyenda, y guarda un PNG completamente en blanco. ¿Qué está mal?
import matplotlib.pyplot as plt
meses = [1, 2, 3, 4]
centro = [120, 145, 130, 180]
norte = [90, 100, 160, 150]
plt.plot(meses, centro, label="Centro")
plt.show()
plt.plot(meses, norte)
plt.legend()
plt.show()
plt.savefig("ventas.png")1. Hay dos plt.show(). El primero cierra la figura, así que la segunda línea empieza un gráfico nuevo en vez de superponerse. show() va una sola vez, al final de todo.
2. Al segundo plot le falta label=. legend() solo lista las series etiquetadas; sin label="Norte" no tiene nada que mostrar de esa línea (y Matplotlib avisa con “No artists with labels found”).
3. savefig está después del último show(). Para entonces la figura ya se vació, así que el archivo sale en blanco. Y como no es un error, no hay ningún mensaje que lo delate.
La versión correcta:
plt.plot(meses, centro, label="Centro")
plt.plot(meses, norte, label="Norte")
plt.legend()
plt.savefig("ventas.png")
plt.show()El orden que conviene memorizar: dibujar todo → etiquetar → guardar → mostrar.
Un gráfico de barras con título, nombre del eje vertical y guardado en archivo antes de mostrarse.
import matplotlib.pyplot as
productos = ["Café", "Té", "Mate"]
vendidos = [340, 180, 420]
plt.(productos, vendidos)
plt.("Unidades vendidas")
plt.ylabel("Unidades")
plt.savefig("ventas.png")
plt.()Ordena las líneas para dibujar la curva del seno con título y leyenda, guardarla y mostrarla.
- plt.title("Función seno")
- x = np.linspace(0, 2 * np.pi, 200)
- import numpy as np
- plt.show()
- plt.plot(x, np.sin(x), label="sen(x)")
- plt.legend()
- plt.savefig("seno.png")
- import matplotlib.pyplot as plt
Hasta aquí llega esta sección
Con NumPy para calcular y pyplot para visualizar tienes la base del stack científico de Python. El paso siguiente natural es pandas, que agrega tablas con nombres de columna (los DataFrame) sobre los arrays de NumPy y se integra con Matplotlib directamente — es la herramienta habitual para leer un CSV, limpiarlo y graficarlo en pocas líneas.