Cómo establecer etiquetas de ejes comunes para subparcelas

8 minutos de lectura

avatar de usuario de farqwag25
farqwag25

tengo la siguiente trama:

import matplotlib.pyplot as plt

fig2 = plt.figure()
ax3 = fig2.add_subplot(2,1,1)
ax4 = fig2.add_subplot(2,1,2)
ax4.loglog(x1, y1)
ax3.loglog(x2, y2)
ax3.set_ylabel('hello')

Quiero poder crear etiquetas y títulos de ejes no solo para cada una de las dos subtramas, sino también etiquetas comunes que abarquen ambas subtramas. Por ejemplo, dado que ambos gráficos tienen ejes idénticos, solo necesito un conjunto de etiquetas de ejes x e y. Sin embargo, quiero títulos diferentes para cada trama secundaria.

Intenté algunas cosas pero ninguna funcionó bien.

avatar de usuario de wwliao
wwliao

Puede crear una subparcela grande que cubra las dos subparcelas y luego establecer las etiquetas comunes.

import random
import matplotlib.pyplot as plt

x = range(1, 101)
y1 = [random.randint(1, 100) for _ in range(len(x))]
y2 = [random.randint(1, 100) for _ in range(len(x))]

fig = plt.figure()
ax = fig.add_subplot(111)    # The big subplot
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)

# Turn off axis lines and ticks of the big subplot
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
ax.spines['left'].set_color('none')
ax.spines['right'].set_color('none')
ax.tick_params(labelcolor="w", top=False, bottom=False, left=False, right=False)

ax1.loglog(x, y1)
ax2.loglog(x, y2)

# Set common labels
ax.set_xlabel('common xlabel')
ax.set_ylabel('common ylabel')

ax1.set_title('ax1 title')
ax2.set_title('ax2 title')

plt.savefig('common_labels.png', dpi=300)

etiquetas_comunes.png

Otra forma es usar fig.text() para establecer las ubicaciones de las etiquetas comunes directamente.

import random
import matplotlib.pyplot as plt

x = range(1, 101)
y1 = [random.randint(1, 100) for _ in range(len(x))]
y2 = [random.randint(1, 100) for _ in range(len(x))]

fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)

ax1.loglog(x, y1)
ax2.loglog(x, y2)

# Set common labels
fig.text(0.5, 0.04, 'common xlabel', ha="center", va="center")
fig.text(0.06, 0.5, 'common ylabel', ha="center", va="center", rotation='vertical')

ax1.set_title('ax1 title')
ax2.set_title('ax2 title')

plt.savefig('common_labels_text.png', dpi=300)

etiquetas_comunes_texto.png

  • La función de subtítulos usa la versión fig.text(). Entonces, ¿esta podría ser la forma “oficial” de hacerlo?

    – PhML

    26 de marzo de 2013 a las 10:12

  • Vale la pena enfatizar que ax tiene que ser creado antes ax1 y ax2de lo contrario, la parcela grande cubrirá las parcelas pequeñas.

    – 1”

    29 de noviembre de 2014 a las 21:06

  • También se necesita ax.grid(False) o plt.grid(False) si los parámetros de trazado globales incluyen una cuadrícula (visible).

    – Næreen

    17/10/2017 a las 17:31

  • Parece que el primer enfoque ya no funciona con las versiones recientes de matplotplib (yo uso 2.0.2): las etiquetas agregadas al eje adjunto no son visibles.

    – M.Toya

    27 oct 2017 a las 9:50

  • ¿Cómo agregar y_labels a cada subparcela individual?

    – Fardín Abdi

    24 de mayo de 2018 a las 1:43

Avatar de usuario de Julian Chen
Julián Chen

Una manera simple usando subplots:

import matplotlib.pyplot as plt

fig, axes = plt.subplots(3, 4, sharex=True, sharey=True)
# add a big axes, hide frame
fig.add_subplot(111, frameon=False)
# hide tick and tick label of the big axes
plt.tick_params(labelcolor="none", top=False, bottom=False, left=False, right=False)
plt.grid(False)
plt.xlabel("common X")
plt.ylabel("common Y")

  • También se necesita ax.grid(False) o plt.grid(False) si los parámetros de trazado globales incluyen una cuadrícula (visible).

    – Næreen

    17/10/2017 a las 17:31

  • Estoy haciendo esto para una subparcela (5, 1) y mi ylabel está muy lejos en el borde izquierdo de la ventana en lugar de cerca de las subparcelas.

    – Evidlo

    10 de abril de 2019 a las 4:22

  • Tienes un voto a favor. pero siempre explique qué está haciendo el código, adjunte una imagen o muestre un ejemplo, porque definitivamente tomó un poco de tiempo obtenerlo.

    – Kareem Jeiroudi

    17 abr 2019 a las 22:08


  • Cambio 'off' a False con versiones más nuevas de Matplotlib (tengo 2.2.2)

    –Ted

    7 de agosto de 2019 a las 11:17

  • Y luego, ¿cómo se agregan las parcelas? for ax in axes: ax.plot(x, y) no parece hacer ningún bien.

    – número de usuario

    16 oct 2019 a las 14:44

plt.setp() hará el trabajo:

# plot something
fig, axs = plt.subplots(3,3, figsize=(15, 8), sharex=True, sharey=True)
for i, ax in enumerate(axs.flat):
    ax.scatter(*np.random.normal(size=(2,200)))
    ax.set_title(f'Title {i}')

# set labels
plt.setp(axs[-1, :], xlabel="x axis label")
plt.setp(axs[:, 0], ylabel="y axis label")

ingrese la descripción de la imagen aquí

  • ¿Hay alguna manera de establecer también el tamaño/peso de la fuente con este método?

    – pfabri

    10 de julio de 2020 a las 14:26

  • @pfabri… plt.setp(axs[-1, :], xlabel='x axis label') solo modifica el texto de la etiqueta (aparentemente). si desea personalizar sus parámetros de texto, por ejemplo, ax.set_xlabel(None, size=12, weight='demibold', color='xkcd:lime green', labelpad=0.33) dentro de por círculo. controlar matplotlib.texto.Texto para más **kwargs.

    – Manuel F.

    23 de enero de 2021 a las 0:37


avatar de usuario de tdy
tdy

Nuevo en matplotlib 3.4.0

Ahora hay métodos integrados para establecer etiquetas de ejes comunes:


Para reproducir OP loglog tramas (etiquetas comunes pero títulos separados):

x = np.arange(0.01, 10.01, 0.01)
y = 2 ** x

fig, (ax1, ax2) = plt.subplots(2, 1, constrained_layout=True)
ax1.loglog(y, x)
ax2.loglog(x, y)

# separate subplot titles
ax1.set_title('ax1.title')
ax2.set_title('ax2.title')

# common axis labels
fig.supxlabel('fig.supxlabel')
fig.supylabel('fig.supylabel')

demostración de subetiqueta

Avatar de usuario de KYC
KYC

La respuesta de Wen-wei Liao es buena si no está tratando de exportar gráficos vectoriales o si ha configurado sus backends de matplotlib para ignorar los ejes incoloros; de lo contrario, los ejes ocultos se mostrarían en el gráfico exportado.

Mi respuesta suplabel aquí es similar a la fig.suptitle que utiliza el fig.text función. Por lo tanto, no hay hachas de artista que se creen y se vuelvan incoloras. Sin embargo, si intenta llamarlo varias veces, se agregará texto uno encima del otro (como fig.suptitle también lo hace). La respuesta de Wen-wei Liao no, porque fig.add_subplot(111) devolverá el mismo objeto Axes si ya está creado.

Mi función también se puede llamar después de que se hayan creado las parcelas.

def suplabel(axis,label,label_prop=None,
             labelpad=5,
             ha="center",va="center"):
    ''' Add super ylabel or xlabel to the figure
    Similar to matplotlib.suptitle
    axis       - string: "x" or "y"
    label      - string
    label_prop - keyword dictionary for Text
    labelpad   - padding from the axis (default: 5)
    ha         - horizontal alignment (default: "center")
    va         - vertical alignment (default: "center")
    '''
    fig = pylab.gcf()
    xmin = []
    ymin = []
    for ax in fig.axes:
        xmin.append(ax.get_position().xmin)
        ymin.append(ax.get_position().ymin)
    xmin,ymin = min(xmin),min(ymin)
    dpi = fig.dpi
    if axis.lower() == "y":
        rotation=90.
        x = xmin-float(labelpad)/dpi
        y = 0.5
    elif axis.lower() == 'x':
        rotation = 0.
        x = 0.5
        y = ymin - float(labelpad)/dpi
    else:
        raise Exception("Unexpected axis: x or y")
    if label_prop is None: 
        label_prop = dict()
    pylab.text(x,y,label,rotation=rotation,
               transform=fig.transFigure,
               ha=ha,va=va,
               **label_prop)

  • Esta es la mejor respuesta imo. Es fácil de implementar y las etiquetas no se superponen gracias a la opción de rotulador.

    – Arthur Dent

    25 de junio de 2018 a las 20:23

Aquí hay una solución en la que configura la etiqueta y de una de las parcelas y ajusta la posición de la misma para que esté centrada verticalmente. De esta forma evitas los problemas mencionados por KYC.

import numpy as np
import matplotlib.pyplot as plt

def set_shared_ylabel(a, ylabel, labelpad = 0.01):
    """Set a y label shared by multiple axes
    Parameters
    ----------
    a: list of axes
    ylabel: string
    labelpad: float
        Sets the padding between ticklabels and axis label"""

    f = a[0].get_figure()
    f.canvas.draw() #sets f.canvas.renderer needed below

    # get the center position for all plots
    top = a[0].get_position().y1
    bottom = a[-1].get_position().y0

    # get the coordinates of the left side of the tick labels 
    x0 = 1
    for at in a:
        at.set_ylabel('') # just to make sure we don't and up with multiple labels
        bboxes, _ = at.yaxis.get_ticklabel_extents(f.canvas.renderer)
        bboxes = bboxes.inverse_transformed(f.transFigure)
        xt = bboxes.x0
        if xt < x0:
            x0 = xt
    tick_label_left = x0

    # set position of label
    a[-1].set_ylabel(ylabel)
    a[-1].yaxis.set_label_coords(tick_label_left - labelpad,(bottom + top)/2, transform=f.transFigure)

length = 100
x = np.linspace(0,100, length)
y1 = np.random.random(length) * 1000
y2 = np.random.random(length)

f,a = plt.subplots(2, sharex=True, gridspec_kw={'hspace':0})
a[0].plot(x, y1)
a[1].plot(x, y2)
set_shared_ylabel(a, 'shared y label (a. u.)')

ingrese la descripción de la imagen aquí

  • Esta es la mejor respuesta imo. Es fácil de implementar y las etiquetas no se superponen gracias a la opción de rotulador.

    – Arthur Dent

    25 de junio de 2018 a las 20:23

Avatar de usuario de J.Zhao
J. Zhao

# list loss and acc are your data
fig = plt.figure()
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)

ax1.plot(iteration1, loss)
ax2.plot(iteration2, acc)

ax1.set_title('Training Loss')
ax2.set_title('Training Accuracy')

ax1.set_xlabel('Iteration')
ax1.set_ylabel('Loss')

ax2.set_xlabel('Iteration')
ax2.set_ylabel('Accuracy')

¿Ha sido útil esta solución?