font - subplot python
¿Cómo hago una sola leyenda para muchas subtramas con matplotlib? (6)
Esta respuesta es un complemento de @ Evert en la posición de leyenda.
Mi primer intento en la solución de @Evert fracasó debido a superposiciones de la leyenda y el título de la subtrama.
De hecho, las superposiciones son causadas por fig.tight_layout()
, que cambia el diseño de las subtramas sin tener en cuenta la leyenda de la figura. Sin embargo, fig.tight_layout()
es necesario.
Para evitar las superposiciones, podemos decir a fig.tight_layout()
que deje espacios para la leyenda de la figura mediante fig.tight_layout(rect=(0,0,1,0.9))
.
Estoy trazando el mismo tipo de información, pero para diferentes países, con múltiples subtramas con matplotlib. Es decir, tengo 9 tramas en una cuadrícula de 3x3, todas con lo mismo para las líneas (por supuesto, diferentes valores por línea).
Sin embargo, no he descubierto cómo poner una sola leyenda (ya que las 9 subparcelas tienen las mismas líneas) en la figura solo una vez.
¿Cómo puedo hacer eso?
Para el posicionamiento automático de una única leyenda en una figure
con muchos ejes, como los obtenidos con subplots()
, la siguiente solución funciona muy bien:
plt.legend( lines, labels, loc = ''lower center'', bbox_to_anchor = (0,-0.1,1,1),
bbox_transform = plt.gcf().transFigure )
Con bbox_to_anchor
y bbox_transform=plt.gcf().transFigure
está definiendo un nuevo cuadro delimitador del tamaño de su figure
para que sea una referencia para loc
. El uso de (0,-0.1,1,1)
mueve este buzón ligeramente hacia abajo para evitar que la leyenda se coloque sobre otros artistas.
OBS: use esta solución DESPUÉS de usar fig.set_size_inches()
y ANTES de usar fig.tight_layout()
Si bien es bastante tarde para el juego, daré otra solución aquí, ya que este sigue siendo uno de los primeros enlaces en aparecer en Google. Usando matplotlib 2.2.2, esto se puede lograr usando la función gridspec. En el siguiente ejemplo, el objetivo es tener cuatro subparcelas dispuestas en una forma 2x2 con la leyenda que se muestra en la parte inferior. Se crea un eje ''falso'' en la parte inferior para colocar la leyenda en un punto fijo. El eje "falso" se apaga, por lo que solo se muestra la leyenda. Resultado: https://i.stack.imgur.com/5LUWM.png .
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
#Gridspec demo
fig = plt.figure()
fig.set_size_inches(8,9)
fig.set_dpi(100)
rows = 17 #the larger the number here, the smaller the spacing around the legend
start1 = 0
end1 = int((rows-1)/2)
start2 = end1
end2 = int(rows-1)
gspec = gridspec.GridSpec(ncols=4, nrows=rows)
axes = []
axes.append(fig.add_subplot(gspec[start1:end1,0:2]))
axes.append(fig.add_subplot(gspec[start2:end2,0:2]))
axes.append(fig.add_subplot(gspec[start1:end1,2:4]))
axes.append(fig.add_subplot(gspec[start2:end2,2:4]))
axes.append(fig.add_subplot(gspec[end2,0:4]))
line, = axes[0].plot([0,1],[0,1],''b'') #add some data
axes[-1].legend((line,),(''Test'',),loc=''center'') #create legend on bottommost axis
axes[-1].set_axis_off() #don''t show bottommost axis
fig.tight_layout()
plt.show()
Solo tienes que pedir la leyenda una vez, fuera de tu loop.
Por ejemplo, en este caso tengo 4 subtramas, con las mismas líneas, y una sola leyenda.
from matplotlib.pyplot import *
ficheiros = [''120318.nc'', ''120319.nc'', ''120320.nc'', ''120321.nc'']
fig = figure()
fig.suptitle(''concentration profile analysis'')
for a in range(len(ficheiros)):
# dados is here defined
level = dados.variables[''level''][:]
ax = fig.add_subplot(2,2,a+1)
xticks(range(8), [''0h'',''3h'',''6h'',''9h'',''12h'',''15h'',''18h'',''21h''])
ax.set_xlabel(''time (hours)'')
ax.set_ylabel(''CONC ($/mu g. m^{-3}$)'')
for index in range(len(level)):
conc = dados.variables[''CONC''][4:12,index] * 1e9
ax.plot(conc,label=str(level[index])+''m'')
dados.close()
ax.legend(bbox_to_anchor=(1.05, 0), loc=''lower left'', borderaxespad=0.)
# it will place the legend on the outer right-hand side of the last axes
show()
También hay una buena función get_legend_handles_labels()
que puede invocar en el último eje (si itera sobre ellos) que recolectaría todo lo que necesita de label=
arguments:
handles, labels = ax.get_legend_handles_labels()
fig.legend(handles, labels, loc=''upper center'')
figlegend puede ser lo que estás buscando: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.figlegend
Ejemplo aquí: http://matplotlib.org/examples/pylab_examples/figlegend_demo.html
Otro ejemplo:
plt.figlegend( lines, labels, loc = ''lower center'', ncol=5, labelspacing=0. )
o:
fig.legend( lines, labels, loc = (0.5, 0), ncol=5 )