2011-08-05 19 views
102

tengo el siguiente diagrama:pyplot etiquetas de los ejes para las parcelas

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 ser capaz de crear etiquetas de los ejes y los títulos no sólo para cada una de las dos tramas secundarias, sino también las etiquetas comunes que abarcan ambas tramas secundarias. Por ejemplo, dado que ambos gráficos tienen ejes idénticos, solo necesito un conjunto de etiquetas de ejes xey. Sin embargo, quiero diferentes títulos para cada subtrama.

he intentado un par de cosas, pero ninguno de ellos trabajaron derecho

Respuesta

150

Puede crear una gran trama secundaria que cubre las dos subtramas y establezca las etiquetas comunes.

import random 
import matplotlib.pyplot as plt 

x = range(1, 101) 
y1 = [random.randint(1, 100) for _ in xrange(len(x))] 
y2 = [random.randint(1, 100) for _ in xrange(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='off', bottom='off', left='off', right='off') 

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) 

common_labels.png

Otra forma es usar fig.text() para establecer la ubicación de las etiquetas comunes directamente.

import random 
import matplotlib.pyplot as plt 

x = range(1, 101) 
y1 = [random.randint(1, 100) for _ in xrange(len(x))] 
y2 = [random.randint(1, 100) for _ in xrange(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) 

common_labels_text.png

+1

La función suptitle utilizar la versión fig.text(). ¿Entonces esta podría ser la forma "oficial" de hacerlo? – PhML

+2

Vale la pena enfatizar que 'ax 'debe crearse antes de' ax1' y 'ax2'; de lo contrario, la gran trama cubrirá las parcelas pequeñas. –

+0

ax.grid (False) o plt.grid (False) también es necesario si los parámetros globales de trazado incluyen una cuadrícula (visible). –

10

Wen-wei respuesta de Liao es bueno si usted no está tratando de exportar los gráficos vectoriales o que haya configurado sus backends matplotlib para ignorar ejes incoloros; de lo contrario, los ejes ocultos aparecerían en el gráfico exportado.

Mi respuesta suplabel aquí es similar al fig.suptitle que usa la función fig.text. Por lo tanto, no hay ningún artista de ejes creado y descolorido. Sin embargo, si intenta llamarlo varias veces, obtendrá texto agregado uno encima del otro (como también lo hace fig.suptitle). 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 invocar después de que se hayan creado los diagramas.

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

Una forma sencilla utilizando 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='off', bottom='off', left='off', right='off') 
plt.grid(False) 
plt.xlabel("common X") 
plt.ylabel("common Y") 
+0

perfectamente corto! – maggie

+1

Esta debería ser la respuesta aceptada – kungfujam

+0

Acepto que esta es una gran respuesta –

7

Aquí está una solución en la que se establece el ylabel de una de las parcelas y ajustar la posición de la misma forma que esté centrada verticalmente. De esta forma evitará 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.)') 

enter image description here

Cuestiones relacionadas