# graficos-en-matplotlib
Gráficos en Matplotlib
Volver a la portada del cursoManipulación de Ejes y Límites
Sección 3 · Personalización de Gráficos
Manipulación de Ejes y Límites
La línea del ingreso mensual de PyShop con los límites automáticos tiene el eje y entre 0.562 y 1.545 millones. Con ese rango, febrero (0.61 M) queda al 5 % de la altura del panel y diciembre (1.50 M) al 95 %: a la vista, diciembre parece vender veinte veces más, cuando en realidad vende 2.5 veces más. Matplotlib elige los límites para que los datos llenen el panel, no para que la comparación sea justa. Esta lectura muestra cómo tomar el control de los ejes: límites, marcas, formato de las etiquetas, escalas, un segundo eje y anotaciones.
1. Límites y márgenes
Por defecto, cada eje se ajusta al mínimo y al máximo de los datos y agrega un margen del 5 % a cada lado. Por eso el eje x de los meses 1 a 12 va de 0.45 a 12.55. set_xlim y set_ylim fijan los límites; si pasas None en un extremo, ese extremo sigue siendo automático:
import matplotlib.pyplot as plt
ventas = generar_ventas()
mes = ventas["fecha"].dt.month
mensual = ventas.groupby(mes)["total"].sum() / 1e6
fig, ax = plt.subplots()
ax.plot(mensual.index, mensual.values, marker="o")
def limites(ax):
return tuple(round(float(v), 3) for v in ax.get_ylim())
print(limites(ax)) # (0.562, 1.545)
ax.set_ylim(0, None)
print(limites(ax)) # (0.0, 1.545)
print(ax.margins()) # (0.05, 0.05)
En barras el eje y siempre debe empezar en cero, porque la longitud de la barra es el dato; Matplotlib ya lo hace así con ax.bar. En líneas no es obligatorio, pero si el rango no empieza en cero conviene que el lector lo note. ax.margins(x=0) elimina el margen horizontal cuando la serie debe tocar los bordes.
2. Marcas y sus etiquetas
Las marcas automáticas del eje x son 0, 2, 4… 14: Matplotlib no sabe que son meses. set_xticks recibe las posiciones y, con labels=, el texto de cada una. Para etiquetas largas, ax.tick_params(axis="x", rotation=45) las inclina.
MESES = ["Ene", "Feb", "Mar", "Abr", "May", "Jun",
"Jul", "Ago", "Sep", "Oct", "Nov", "Dic"]
ax.set_xticks(range(1, 13), labels=MESES)
print(len(ax.get_xticks())) # 12
3. Formateadores
Cada Axis usa un formatter para convertir el número de la marca en texto. El módulo matplotlib.ticker trae varios listos:
StrMethodFormatter("{x:,.0f}"): usa la sintaxis destr.format; con ingresos en pesos produce «1,000,000».FuncFormatter(f): llama a una función propiaf(valor, posicion); sirve para escribir «1.2 M».PercentFormatter(): agrega el símbolo de porcentaje; condecimals=0muestra «10%» en lugar de «10.0%».
from matplotlib.ticker import FuncFormatter
def millones(valor, pos):
return f"{valor:.1f} M"
ax.yaxis.set_major_formatter(FuncFormatter(millones))
# etiquetas: 0.0 M, 0.2 M, 0.4 M ...
El formateador cambia solo el texto: los datos y los límites siguen iguales. Es mejor que dividir entre un millón a mano cuando la misma figura necesita la cifra exacta en otro lugar.
4. Escala logarítmica
Los precios de PyShop van de 110.39 a 6,677.50 MXN: el más caro vale 60.5 veces el más barato. En un histograma con escala lineal, Libros y Ropa quedan apretados en la orilla izquierda y Electrónica ocupa casi todo el ancho. Con ax.set_xscale("log") cada marca multiplica por 10 en vez de sumar una cantidad fija, y los grupos de precios se separan. La escala log es útil cuando los datos abarcan varios órdenes de magnitud; no admite ceros ni negativos.
5. Dos ejes verticales en el mismo panel: twinx
Pedidos (de 349 a 800 por mes) y ticket medio (de 1,738 a 1,958 MXN) tienen unidades distintas. ax.twinx() crea un segundo Axes que comparte el eje x y pone su eje y a la derecha:
pedidos = ventas.groupby(mes).size()
ticket = ventas.groupby(mes)["total"].sum() / pedidos
fig, ax = plt.subplots()
ax.bar(pedidos.index, pedidos.values, color="C0")
ax.set_ylabel("Pedidos")
ax2 = ax.twinx()
ax2.plot(ticket.index, ticket.values, color="C1", marker="o")
ax2.set_ylabel("Ticket medio (MXN)")
print(len(fig.axes)) # 2
Úsalo con cuidado: el lector puede comparar alturas que no son comparables. Colorea cada eje y su etiqueta igual que su serie.
6. Cuadrícula, bordes y anotaciones
ax.grid(axis="y", alpha=0.3) dibuja líneas guía tenues solo en y. ax.spines[["top", "right"]].set_visible(False) quita los bordes que no aportan. ax.annotate escribe un texto con flecha sobre un punto concreto, por ejemplo el máximo de diciembre:
pico = mensual.idxmax() # 12
ax.annotate(f"{mensual[pico]:.2f} M", xy=(pico, mensual[pico]),
xytext=(8, 1.4), arrowprops={"arrowstyle": "->"})
Trampas comunes
- Eje y recortado sin avisar. Exagera las diferencias; en barras es un error.
- Fijar límites antes de dibujar. Si después agregas otra serie con autoescala, los límites pueden cambiar; ajusta al final.
- Poner etiquetas sin posiciones. Pasa siempre las posiciones a
set_xticks; si no, las etiquetas no coinciden con los datos. - Log con ceros. Los valores en cero o negativos desaparecen de un eje logarítmico.
twinxsin colores. Sin distinción visual, no se sabe qué eje lee cada serie.
Cierre
El notebook ejes-colores-estilos aplica todo esto a la línea mensual y a los precios, y sigue con colores, colormaps y hojas de estilo. Antes de entregar un gráfico, revisa tres cosas: dónde empieza el eje y, si las marcas dicen algo legible y si las unidades están en la etiqueta.