Anotar gráficos de Matplotlib de manera efectiva

MatplotlibMatplotlibBeginner
Practicar Ahora

This tutorial is from open-source community. Access the source code

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

Esta práctica te guiará a través de la anotación de gráficos en Matplotlib. Aprenderás cómo resaltar puntos de interés específicos y utilizar diversas herramientas visuales para llamar la atención hacia estos puntos. Las herramientas de anotación y texto son esenciales para transmitir información y hacer que los gráficos sean más atractivos visualmente.

Consejos sobre la VM

Una vez finalizada la inicialización de la VM, haz clic en la esquina superior izquierda para cambiar a la pestaña Cuaderno y acceder a Jupyter Notebook para practicar.

A veces, es posible que tengas que esperar unos segundos a que Jupyter Notebook termine de cargarse. La validación de operaciones no puede automatizarse debido a las limitaciones de Jupyter Notebook.

Si tienes problemas durante el aprendizaje, no dudes en preguntar a Labby. Proporciona retroalimentación después de la sesión y resolveremos rápidamente el problema para ti.

Especificar puntos de texto y puntos de anotación

Debes especificar un punto de anotación xy=(x, y) para anotar ese punto. Además, puedes especificar un punto de texto xytext=(x, y) para la ubicación del texto de esta anotación. Opcionalmente, puedes especificar el sistema de coordenadas de xy y xytext con una de las siguientes cadenas para xycoords y textcoords (el valor predeterminado es 'data'):

  • 'puntos de figura' : puntos a partir de la esquina inferior izquierda de la figura
  • 'píxeles de figura' : píxeles a partir de la esquina inferior izquierda de la figura
  • 'fracción de figura' : (0, 0) es la esquina inferior izquierda de la figura y (1, 1) es la esquina superior derecha
  • 'puntos de eje' : puntos a partir de la esquina inferior izquierda del eje
  • 'píxeles de eje' : píxeles a partir de la esquina inferior izquierda del eje
  • 'fracción de eje' : (0, 0) es la esquina inferior izquierda del eje y (1, 1) es la esquina superior derecha
  • 'puntos de desplazamiento' : Especifica un desplazamiento (en puntos) a partir del valor xy
  • 'píxeles de desplazamiento' : Especifica un desplazamiento (en píxeles) a partir del valor xy
  • 'data' : utilizar el sistema de coordenadas de datos del eje

Nota: para los sistemas de coordenadas físicos (puntos o píxeles) el origen es el (inferior, izquierdo) de la figura o del eje.

Opcionalmente, puedes especificar propiedades de flecha que dibujan una flecha desde el texto hasta el punto anotado proporcionando un diccionario de propiedades de flecha. Las claves válidas son:

  • width: el ancho de la flecha en puntos
  • frac: la fracción de la longitud de la flecha ocupada por la punta
  • headwidth: el ancho de la base de la punta de la flecha en puntos
  • shrink: mueve la punta y la base un cierto porcentaje alejada del punto anotado y del texto
  • cualquier clave para matplotlib.patches.polygon (por ejemplo, facecolor)
import matplotlib.pyplot as plt
import numpy as np

from matplotlib.patches import Ellipse
from matplotlib.text import OffsetFrom

## Crea nuestra figura y los datos que utilizaremos para la representación
fig, ax = plt.subplots(figsize=(4, 4))

t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)

## Dibuja una línea y agrega algunas anotaciones simples
line, = ax.plot(t, s)
ax.annotate('píxeles de figura',
            xy=(10, 10), xycoords='píxeles de figura')
ax.annotate('puntos de figura',
            xy=(107, 110), xycoords='puntos de figura',
            fontsize=12)
ax.annotate('fracción de figura',
            xy=(.025,.975), xycoords='fracción de figura',
            horizontalalignment='left', verticalalignment='top',
            fontsize=20)

## Los siguientes ejemplos muestran cómo se dibujan estas flechas.

ax.annotate('desplazamiento de punto a partir de datos',
            xy=(3, 1), xycoords='data',
            xytext=(-10, 90), textcoords='puntos de desplazamiento',
            arrowprops=dict(facecolor='black', shrink=0.05),
            horizontalalignment='center', verticalalignment='bottom')

ax.annotate('fracción de eje',
            xy=(2, 1), xycoords='data',
            xytext=(0.36, 0.68), textcoords='fracción de eje',
            arrowprops=dict(facecolor='black', shrink=0.05),
            horizontalalignment='right', verticalalignment='top')

## También puedes utilizar puntos o píxeles negativos para especificar a partir de (derecha, arriba).
## Por ejemplo, (-10, 10) es 10 puntos a la izquierda del lado derecho del eje y 10
## puntos arriba del fondo

ax.annotate('desplazamiento de píxel a partir de fracción de eje',
            xy=(1, 0), xycoords='fracción de eje',
            xytext=(-20, 20), textcoords='píxeles de desplazamiento',
            horizontalalignment='right',
            verticalalignment='bottom')

ax.set(xlim=(-1, 5), ylim=(-3, 5))

Usando múltiples sistemas de coordenadas y tipos de eje

Puedes especificar el xypoint y el xytext en diferentes posiciones y sistemas de coordenadas, y, opcionalmente, activar una línea de conexión y marcar el punto con un marcador. Las anotaciones también funcionan en ejes polares.

fig, ax = plt.subplots(subplot_kw=dict(projection='polar'), figsize=(3, 3))
r = np.arange(0, 1, 0.001)
theta = 2*2*np.pi*r
line, = ax.plot(theta, r)

ind = 800
thisr, thistheta = r[ind], theta[ind]
ax.plot([thistheta], [thisr], 'o')
ax.annotate('una anotación polar',
            xy=(thistheta, thisr),  ## theta, radio
            xytext=(0.05, 0.05),    ## fracción, fracción
            textcoords='fracción de figura',
            arrowprops=dict(facecolor='black', shrink=0.05),
            horizontalalignment='left',
            verticalalignment='bottom')

Personalizando estilos de flechas y burbujas

La flecha entre xytext y el punto de anotación, así como la burbuja que cubre el texto de la anotación, son altamente personalizables. A continuación se presentan algunas opciones de parámetros y su resultado.

fig, ax = plt.subplots(figsize=(8, 5))

t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
line, = ax.plot(t, s, lw=3)

ax.annotate(
    'directa',
    xy=(0, 1), xycoords='data',
    xytext=(-50, 30), textcoords='puntos de desplazamiento',
    arrowprops=dict(arrowstyle="->"))
ax.annotate(
    'arc3,\nrad 0.2',
    xy=(0.5, -1), xycoords='data',
    xytext=(-80, -60), textcoords='puntos de desplazamiento',
    arrowprops=dict(arrowstyle="->",
                    connectionstyle="arc3,rad=.2"))
ax.annotate(
    'arco,\nángulo 50',
    xy=(1., 1), xycoords='data',
    xytext=(-90, 50), textcoords='puntos de desplazamiento',
    arrowprops=dict(arrowstyle="->",
                    connectionstyle="arc,angleA=0,armA=50,rad=10"))
ax.annotate(
    'arco,\n brazos',
    xy=(1.5, -1), xycoords='data',
    xytext=(-80, -60), textcoords='puntos de desplazamiento',
    arrowprops=dict(
        arrowstyle="->",
        connectionstyle="arc,angleA=0,armA=40,angleB=-90,armB=30,rad=7"))
ax.annotate(
    'ángulo,\nángulo 90',
    xy=(2., 1), xycoords='data',
    xytext=(-70, 30), textcoords='puntos de desplazamiento',
    arrowprops=dict(arrowstyle="->",
                    connectionstyle="angle,angleA=0,angleB=90,rad=10"))
ax.annotate(
    'angle3,\nángulo -90',
    xy=(2.5, -1), xycoords='data',
    xytext=(-80, -60), textcoords='puntos de desplazamiento',
    arrowprops=dict(arrowstyle="->",
                    connectionstyle="angle3,angleA=0,angleB=-90"))
ax.annotate(
    'ángulo,\nredondeado',
    xy=(3., 1), xycoords='data',
    xytext=(-60, 30), textcoords='puntos de desplazamiento',
    bbox=dict(boxstyle="round", fc="0.8"),
    arrowprops=dict(arrowstyle="->",
                    connectionstyle="angle,angleA=0,angleB=90,rad=10"))
ax.annotate(
    'ángulo,\nround4',
    xy=(3.5, -1), xycoords='data',
    xytext=(-70, -80), textcoords='puntos de desplazamiento',
    size=20,
    bbox=dict(boxstyle="round4,pad=.5", fc="0.8"),
    arrowprops=dict(arrowstyle="->",
                    connectionstyle="angle,angleA=0,angleB=-90,rad=10"))
ax.annotate(
    'ángulo,\nencogimiento',
    xy=(4., 1), xycoords='data',
    xytext=(-60, 30), textcoords='puntos de desplazamiento',
    bbox=dict(boxstyle="round", fc="0.8"),
    arrowprops=dict(arrowstyle="->",
                    shrinkA=0, shrinkB=10,
                    connectionstyle="angle,angleA=0,angleB=90,rad=10"))
## Puedes pasar una cadena vacía para obtener solo las flechas de anotación renderizadas
ax.annotate('', xy=(4., 1.), xycoords='data',
            xytext=(4.5, -1), textcoords='data',
            arrowprops=dict(arrowstyle="<->",
                            connectionstyle="bar",
                            ec="k",
                            shrinkA=5, shrinkB=5))

ax.set(xlim=(-1, 5), ylim=(-4, 3))

Más ejemplos de sistemas de coordenadas

A continuación mostraremos algunos más ejemplos de sistemas de coordenadas y cómo se puede especificar la ubicación de las anotaciones.

fig, (ax1, ax2) = plt.subplots(1, 2)

bbox_args = dict(boxstyle="round", fc="0.8")
arrow_args = dict(arrowstyle="->")

## Aquí demostraremos los límites del sistema de coordenadas y cómo
## colocamos el texto de las anotaciones.

ax1.annotate('fracción de figura : 0, 0', xy=(0, 0), xycoords='fracción de figura',
             xytext=(20, 20), textcoords='puntos de desplazamiento',
             ha="left", va="bottom",
             bbox=bbox_args,
             arrowprops=arrow_args)

ax1.annotate('fracción de figura : 1, 1', xy=(1, 1), xycoords='fracción de figura',
             xytext=(-20, -20), textcoords='puntos de desplazamiento',
             ha="right", va="top",
             bbox=bbox_args,
             arrowprops=arrow_args)

ax1.annotate('fracción de eje : 0, 0', xy=(0, 0), xycoords='fracción de eje',
             xytext=(20, 20), textcoords='puntos de desplazamiento',
             ha="left", va="bottom",
             bbox=bbox_args,
             arrowprops=arrow_args)

ax1.annotate('fracción de eje : 1, 1', xy=(1, 1), xycoords='fracción de eje',
             xytext=(-20, -20), textcoords='puntos de desplazamiento',
             ha="right", va="top",
             bbox=bbox_args,
             arrowprops=arrow_args)

## También es posible generar anotaciones arrastrables

an1 = ax1.annotate('Arrastrame 1', xy=(.5,.7), xycoords='data',
                   ha="center", va="center",
                   bbox=bbox_args)

an2 = ax1.annotate('Arrastrame 2', xy=(.5,.5), xycoords=an1,
                   xytext=(.5,.3), textcoords='fracción de eje',
                   ha="center", va="center",
                   bbox=bbox_args,
                   arrowprops=dict(patchB=an1.get_bbox_patch(),
                                   connectionstyle="arc3,rad=0.2",
                                   **arrow_args))
an1.draggable()
an2.draggable()

an3 = ax1.annotate('', xy=(.5,.5), xycoords=an2,
                   xytext=(.5,.5), textcoords=an1,
                   ha="center", va="center",
                   bbox=bbox_args,
                   arrowprops=dict(patchA=an1.get_bbox_patch(),
                                   patchB=an2.get_bbox_patch(),
                                   connectionstyle="arc3,rad=0.2",
                                   **arrow_args))

## Finalmente mostraremos algunas anotaciones y colocaciones más complejas

text = ax2.annotate('xy=(0, 1)\nxycoords=("data", "fracción de eje")',
                    xy=(0, 1), xycoords=("data", 'fracción de eje'),
                    xytext=(0, -20), textcoords='puntos de desplazamiento',
                    ha="center", va="top",
                    bbox=bbox_args,
                    arrowprops=arrow_args)

ax2.annotate('xy=(0.5, 0)\nxycoords=artista',
             xy=(0.5, 0.), xycoords=text,
             xytext=(0, -20), textcoords='puntos de desplazamiento',
             ha="center", va="top",
             bbox=bbox_args,
             arrowprops=arrow_args)

ax2.annotate('xy=(0.8, 0.5)\nxycoords=ax1.transData',
             xy=(0.8, 0.5), xycoords=ax1.transData,
             xytext=(10, 10),
             textcoords=OffsetFrom(ax2.bbox, (0, 0), "points"),
             ha="left", va="bottom",
             bbox=bbox_args,
             arrowprops=arrow_args)

ax2.set(xlim=[-2, 2], ylim=[-2, 2])

Resumen

Esta práctica ha cubierto los conceptos básicos de cómo hacer anotaciones en los gráficos de Matplotlib. Has aprendido cómo especificar puntos de anotación y de texto, cómo utilizar múltiples sistemas de coordenadas y tipos de eje, cómo personalizar los estilos de flechas y burbujas, y más ejemplos de sistemas de coordenadas. Estas herramientas son esenciales para hacer que los gráficos sean más atractivos visualmente y para transmitir información de manera efectiva.