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
  1. Instalación y primer gráfico
  2. Preparar los datos con pandas
  3. Barras: comparar categorías
  4. Graficar directamente desde pandas
  5. Un panel con varios gráficos
  6. Gráficos que no engañan
  7. Resumen
Lo que lograrás 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?

  • Un gráfico circular (de torta)
  • Un gráfico de líneas
  • Un gráfico de barras horizontales ordenadas

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:

Panel con tres gráficos: ventas por mes en línea, ventas por producto en barras apiladas y ventas por región en barras horizontales

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.

Guardar para un informe

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?

  • Nada, así se ve mejor la diferencia.
  • Haces que el eje empiece en cero, por ejemplo con ax.set_ylim(bottom=0).
  • Cambias a un gráfico 3D.

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)?

  • Para crear dos figuras independientes.
  • Para crear una figura con 4 gráficos en una cuadrícula de 2 × 2, accesibles como axs[fila, columna].
  • Para duplicar el tamaño del gráfico.

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:

  1. Calcula el gasto total por categoría y muéstralo en barras horizontales ordenadas, con el valor en cada barra.
  2. Calcula el gasto por mes y muéstralo en una línea con el eje desde cero.
  3. 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 en ax, 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 pasando ax=.
  • 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.

🎓 Guarda tu avance y obtén tu certificado gratis

Crea tu cuenta en 30 segundos, marca tus lecciones y descarga el certificado al terminar.

Inscribirme gratis