Lección 14 de 16 · Nivel 4: Experto
Gráficos con matplotlib: barras, líneas y un panel para tus informes
- ⏱ 8 min de lectura
- ❓ 3 preguntas de repaso
- ✍️ Ejercicio con solución
- 📥 Plantilla descargable
En esta lección
- Crear gráficos de barras, líneas y barras apiladas con matplotlib.
- Graficar directamente desde pandas con
.plot(). - Armar un panel de varios gráficos y guardarlo como imagen para un informe.
- Elegir el gráfico correcto y evitar los errores que engañan al lector.
Una tabla con números responde preguntas a quien tiene tiempo de leerla. Un buen gráfico las responde en dos segundos. En esta lección convertirás el análisis de ventas de las lecciones 11 y 13 en un panel visual con matplotlib, la biblioteca de gráficos más usada de Python, sobre la que se construyen muchas otras.
Instalación y primer gráfico
pip install matplotlib pandas
(Recuerda activar tu entorno virtual primero, como en la lección 10.) El patrón básico de matplotlib siempre es el mismo: crear una figura con uno o varios "ejes" (cada eje es un gráfico), dibujar y guardar o mostrar.
import matplotlib.pyplot as plt
meses = ["Ene", "Feb", "Mar"]
ventas = [3407.5, 2340.0, 3447.5]
fig, ax = plt.subplots(figsize=(7, 4)) # tamaño en pulgadas
ax.plot(meses, ventas, marker="o")
ax.set_title("Ventas por mes")
ax.set_ylabel("Ventas")
fig.savefig("ventas_mes.png", dpi=150) # guarda la imagen
plt.show() # abre una ventana (en Jupyter se muestra solo)
Siempre trabaja con fig y ax (la "interfaz orientada a objetos"). Verás en internet código con plt.plot() directamente: funciona para gráficos rápidos, pero se complica en cuanto quieres más de un gráfico.
Preparar los datos con pandas
Partimos del archivo de ventas. Como ya sabes de la lección 11, hay una fila sin región, que corresponde a Eva (Centro):
import pandas as pd
df = pd.read_csv("ventas.csv", parse_dates=["fecha"])
df["region"] = df["region"].fillna("Centro")
df["importe"] = df["unidades"] * df["precio"]
por_region = df.groupby("region")["importe"].sum().sort_values()
print(por_region)
region
Sur 2520.0
Centro 2562.5
Norte 4112.5
Name: importe, dtype: float64
Barras: comparar categorías
fig, ax = plt.subplots(figsize=(7, 4))
ax.barh(por_region.index, por_region.values, color="#2f6fed")
ax.set_title("Ventas por región, 1.er trimestre 2026")
ax.set_xlabel("Ventas")
for i, v in enumerate(por_region.values): # etiqueta con el valor
ax.text(v, i, f" {v:,.0f}", va="center")
ax.spines[["top", "right"]].set_visible(False) # quita bordes innecesarios
fig.tight_layout()
fig.savefig("ventas_region.png", dpi=150)
Tres decisiones que hacen que este gráfico se lea mejor: barras horizontales (los nombres largos caben sin girar), ordenadas de menor a mayor (con sort_values(), la más grande queda arriba) y el valor escrito junto a cada barra, para no obligar a estimarlo con el eje.
1. Quieres mostrar cómo evolucionaron las ventas mes a mes. ¿Qué gráfico usas?
Las líneas muestran tendencias en el tiempo. Las barras sirven para comparar categorías, y los gráficos circulares solo para pocas partes de un total.
Graficar directamente desde pandas
Los DataFrame y las Series tienen un método .plot() que usa matplotlib por debajo. Es ideal para tablas dinámicas:
meses = {1: "Ene", 2: "Feb", 3: "Mar"}
por_producto = df.pivot_table(index=df["fecha"].dt.month, columns="producto",
values="importe", aggfunc="sum", fill_value=0)
por_producto.index = por_producto.index.map(meses)
print(por_producto)
ax = por_producto.plot(kind="bar", stacked=True, rot=0, figsize=(7, 4))
ax.set_title("Ventas por producto y mes")
producto Café Chocolate Té
fecha
Ene 2687.5 0.0 720.0
Feb 750.0 600.0 990.0
Mar 1812.5 825.0 810.0
kind acepta "line", "bar", "barh", "hist", "box", "scatter" y más. Usamos un diccionario para poner los meses en español, porque los nombres automáticos de las fechas dependen del idioma del sistema.
Un panel con varios gráficos
Con plt.subplots(1, 3) obtienes tres ejes en una fila y los llenas uno por uno:
meses = {1: "Ene", 2: "Feb", 3: "Mar"}
por_mes = df.groupby(df["fecha"].dt.month)["importe"].sum()
por_mes.index = por_mes.index.map(meses)
fig, axs = plt.subplots(1, 3, figsize=(15, 4.5))
axs[0].plot(por_mes.index, por_mes.values, marker="o", linewidth=2)
axs[0].set_title("Ventas por mes")
axs[0].set_ylim(bottom=0)
por_producto.plot(kind="bar", stacked=True, ax=axs[1], rot=0)
axs[1].set_title("Ventas por producto")
axs[1].set_xlabel("")
axs[1].legend(title="", frameon=False)
axs[2].barh(por_region.index, por_region.values, color="#2f6fed")
axs[2].set_title("Ventas por región")
for ax in axs:
ax.spines[["top", "right"]].set_visible(False)
fig.suptitle("Panel de ventas · 1.er trimestre 2026", fontsize=14, fontweight="bold")
fig.tight_layout()
fig.savefig("panel_ventas.png", dpi=150)
El resultado:

Lectura en dos segundos: febrero fue el mes flojo (sobre todo por el café), el chocolate se suma desde febrero y la región Norte vende casi tanto como las otras dos juntas. Eso es lo que debe lograr un buen panel.
Usa dpi=150 o más para documentos y presentaciones, format="svg" si quieres un gráfico vectorial que se vea nítido a cualquier tamaño, y bbox_inches="tight" si se corta algún título. Combinado con la lección 12, puedes generar este panel cada lunes y enviarlo por correo sin tocar nada.
Gráficos que no engañan
- Las barras empiezan en cero. Si el eje empieza en 2.000, una diferencia del 5% parece del 200%.
- Un mensaje por gráfico, dicho en el título si es posible: "Febrero cayó un 31%" es mejor que "Ventas".
- Evita los gráficos circulares con más de 3 o 4 partes: las barras se comparan mucho mejor.
- Pocos colores y con intención: un color neutro para todo y uno fuerte para lo que quieres destacar.
- Nada de 3D: deforma las proporciones y no aporta información.
2. En un gráfico de barras, el eje Y empieza en 2.300 y las barras de febrero (2.340) y marzo (3.447) parecen una 25 veces más alta que la otra. ¿Qué corriges?
En las barras, la longitud representa el valor. Si el eje no empieza en cero, las proporciones mienten.
3. ¿Para qué sirve fig, axs = plt.subplots(2, 2)?
subplots(filas, columnas) devuelve la figura y una matriz de ejes. Con una sola fila (1, 3) se accede con un índice: axs[0], axs[1]…
Ejercicio práctico: tu panel de gastos
Con el archivo gastos.csv (columnas fecha, categoria, descripcion, monto) del proyecto de la lección 8:
- Calcula el gasto total por categoría y muéstralo en barras horizontales ordenadas, con el valor en cada barra.
- Calcula el gasto por mes y muéstralo en una línea con el eje desde cero.
- Junta ambos gráficos en una figura de 1 × 2 y guárdala como
panel_gastos.png.
Ver solución
import matplotlib.pyplot as plt
import pandas as pd
g = pd.read_csv("gastos.csv", parse_dates=["fecha"])
por_cat = g.groupby("categoria")["monto"].sum().sort_values()
por_mes = g.groupby(g["fecha"].dt.to_period("M"))["monto"].sum()
por_mes.index = por_mes.index.astype(str) # "2026-09", "2026-10"…
fig, axs = plt.subplots(1, 2, figsize=(12, 4))
axs[0].barh(por_cat.index, por_cat.values, color="#2f6fed")
for i, v in enumerate(por_cat.values):
axs[0].text(v, i, f" {v:,.0f}", va="center")
axs[0].set_title("Gasto por categoría")
axs[1].plot(por_mes.index, por_mes.values, marker="o")
axs[1].set_ylim(bottom=0)
axs[1].set_title("Gasto por mes")
for ax in axs:
ax.spines[["top", "right"]].set_visible(False)
fig.tight_layout()
fig.savefig("panel_gastos.png", dpi=150)
Si tus fechas abarcan muchos meses y las etiquetas se superponen, agrega axs[1].tick_params(axis="x", rotation=45).
Resumen
- Patrón básico:
fig, ax = plt.subplots(), dibujar enax,fig.savefig(). - Líneas para el tiempo, barras (ordenadas) para comparar categorías, barras apiladas para la composición.
- pandas grafica directamente con
.plot(kind=...), y los dos estilos se combinan pasandoax=. - Un panel es una figura con varios ejes:
plt.subplots(filas, columnas). - Barras desde cero, un mensaje por gráfico, pocos colores y nada de 3D.
En la próxima lección obtendrás datos que no vienen en ningún archivo: aprenderás web scraping para extraer información de páginas web con requests y BeautifulSoup, de forma responsable.
Crea tu cuenta en 30 segundos, marca tus lecciones y descarga el certificado al terminar.