python - libreria - matplotlib legend example
Cómo sacar a la leyenda de la trama. (16)
Colocando la leyenda ( bbox_to_anchor
)
Una leyenda se coloca dentro del cuadro delimitador de los ejes utilizando el argumento loc
para plt.legend
.
Por ejemplo, loc="upper right"
coloca la leyenda en la esquina superior derecha del cuadro delimitador, que por defecto se extiende de (0,0)
a (1,1)
en las coordenadas de los ejes (o en la notación del cuadro delimitador (x0,y0, width, height)=(0,0,1,1)
).
Para colocar la leyenda fuera del cuadro delimitador de ejes, se puede especificar una tupla (x0,y0)
de coordenadas de ejes de la esquina inferior izquierda de la leyenda.
plt.legend(loc=(1.04,0))
Sin embargo, un enfoque más versátil sería especificar manualmente el cuadro delimitador en el que se debe colocar la leyenda, utilizando el argumento bbox_to_anchor
. Uno puede restringirse para suministrar solo la parte (x0,y0)
de bbox. Esto crea un cuadro de intervalo cero, desde el cual la leyenda se expandirá en la dirección dada por el argumento loc
. P.ej
plt.legend(bbox_to_anchor=(1.04,1), loc="upper left")
coloca la leyenda fuera de los ejes, de modo que la esquina superior izquierda de la leyenda esté en la posición (1.04,1)
en las coordenadas de los ejes.
Más abajo se dan ejemplos adicionales, donde además se muestra la interacción entre diferentes argumentos como el mode
y ncols
.
l1 = plt.legend(bbox_to_anchor=(1.04,1), borderaxespad=0)
l2 = plt.legend(bbox_to_anchor=(1.04,0), loc="lower left", borderaxespad=0)
l3 = plt.legend(bbox_to_anchor=(1.04,0.5), loc="center left", borderaxespad=0)
l4 = plt.legend(bbox_to_anchor=(0,1.02,1,0.2), loc="lower left",
mode="expand", borderaxespad=0, ncol=3)
l5 = plt.legend(bbox_to_anchor=(1,0), loc="lower right",
bbox_transform=fig.transFigure, ncol=3)
l6 = plt.legend(bbox_to_anchor=(0.4,0.8), loc="upper right")
En esta pregunta se pueden encontrar detalles sobre cómo interpretar el argumento 4-tuple para bbox_to_anchor
, como en l4
. El mode="expand"
expande la leyenda horizontalmente dentro del cuadro delimitador dado por la 4-tupla. Para una leyenda expandida verticalmente, vea esta pregunta .
A veces puede ser útil especificar el cuadro delimitador en coordenadas de figura en lugar de coordenadas de ejes. Esto se muestra en el ejemplo l5
de arriba, donde se bbox_transform
argumento bbox_transform
para colocar la leyenda en la esquina inferior izquierda de la figura.
Postprocesamiento
La colocación de la leyenda fuera de los ejes a menudo conduce a la situación no deseada de que está total o parcialmente fuera del lienzo de la figura.
Las soluciones a este problema son:
Ajustar los parámetros de subplot
Se pueden ajustar los parámetros de subplot de forma tal que los ejes ocupen menos espacio dentro de la figura (y, por lo tanto, dejen más espacio para la leyenda) utilizandoplt.subplots_adjust
. P.ejplt.subplots_adjust(right=0.7)
deja un 30% de espacio en el lado derecho de la figura, donde se puede colocar la leyenda.
Diseño ajustado
Uso deplt.tight_layout
Permite ajustar automáticamente los parámetros deplt.tight_layout
para que los elementos de la figura queden ajustados contra los bordes de la figura. Desafortunadamente, la leyenda no se tiene en cuenta en este automatismo, pero podemos proporcionar una caja rectangular en la que cabrá toda el área de subparcelas (incluidas las etiquetas).plt.tight_layout(rect=[0,0,0.75,1])
Guardando la figura con
bbox_inches = "tight"
El argumentobbox_inches = "tight"
aplt.savefig
se puede usar para guardar la figura de modo que todo el artista en el lienzo (incluida la leyenda) se ajuste al área guardada. Si es necesario, el tamaño de la figura se ajusta automáticamente.plt.savefig("output.png", bbox_inches="tight")
- Ajustando automáticamente los parámetros de subplot
En esta respuesta se puede encontrar una forma de ajustar automáticamente la posición de la subparcela para que la leyenda se ajuste dentro del lienzo sin cambiar el tamaño de la figura : Crear una figura con el tamaño exacto y sin relleno (y la leyenda fuera de los ejes)
Comparación entre los casos discutidos anteriormente:
Alternativas
Una figura leyenda
Uno puede usar una leyenda para la figura en lugar de los ejes, matplotlib.figure.Figure.legend
. Esto se ha vuelto especialmente útil para la versión matplotlib> = 2.1, donde no se necesitan argumentos especiales
fig.legend(loc=7)
Para crear una leyenda para todos los artistas en los diferentes ejes de la figura. La leyenda se coloca utilizando el argumento loc
, similar a cómo se coloca dentro de los ejes, pero en referencia a toda la figura, por lo tanto, estará fuera de los ejes de forma algo automática. Lo que queda es ajustar las subparcelas de modo que no haya superposición entre la leyenda y los ejes. Aquí el punto "Ajustar los parámetros de subplot" desde arriba será útil. Un ejemplo:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,2*np.pi)
colors=["#7aa0c4","#ca82e1" ,"#8bcd50","#e18882"]
fig, axes = plt.subplots(ncols=2)
for i in range(4):
axes[i//2].plot(x,np.sin(x+i), color=colors[i],label="y=sin(x+{})".format(i))
fig.legend(loc=7)
fig.tight_layout()
fig.subplots_adjust(right=0.75)
plt.show()
Leyenda dentro de ejes de subtrama dedicados
Una alternativa al uso de bbox_to_anchor
sería colocar la leyenda en sus ejes de subtrama dedicados ( lax
). Dado que la subparcela de leyenda debe ser más pequeña que la gráfica, podemos usar gridspec_kw={"width_ratios":[4,1]}
en la creación de los ejes. Podemos ocultar los ejes lax.axis("off")
pero aún así colocar una leyenda. Los identificadores y las etiquetas de la leyenda deben obtenerse de la trama real mediante h,l = ax.get_legend_handles_labels()
, y luego se pueden suministrar al leyenda en la subparcela lax
, lax.legend(h,l)
. Un ejemplo completo está abajo.
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = 6,2
fig, (ax,lax) = plt.subplots(ncols=2, gridspec_kw={"width_ratios":[4,1]})
ax.plot(x,y, label="y=sin(x)")
....
h,l = ax.get_legend_handles_labels()
lax.legend(h,l, borderaxespad=0)
lax.axis("off")
plt.tight_layout()
plt.show()
Esto produce una trama que es visualmente muy similar a la trama de arriba:
También podríamos usar los primeros ejes para colocar la leyenda, pero usar la bbox_transform
de los ejes de leyenda,
ax.legend(bbox_to_anchor=(0,0,1,1), bbox_transform=lax.transAxes)
lax.axis("off")
En este enfoque, no necesitamos obtener los bbox_to_anchor
leyenda externamente, pero necesitamos especificar el argumento bbox_to_anchor
.
Lecturas y notas adicionales:
- Considere la guía de leyendas de matplotlib con algunos ejemplos de otras cosas que desea hacer con leyendas.
- Se puede encontrar directamente algún código de ejemplo para colocar leyendas para gráficos circulares en respuesta a esta pregunta: Python - La leyenda se superpone con el gráfico circular
- El argumento
loc
puede tomar números en lugar de cadenas, lo que acorta las llamadas, sin embargo, no se asignan de forma muy intuitiva entre sí. Aquí está el mapeo para referencia:
Tengo una serie de 20 parcelas (no subparcelas) que se realizarán en una sola figura. Quiero que la leyenda esté fuera de la caja. Al mismo tiempo, no quiero cambiar los ejes, ya que el tamaño de la figura se reduce. Por favor ayúdame para las siguientes consultas:
- Quiero mantener el cuadro de leyenda fuera del área de la trama. (Quiero que la leyenda esté afuera en el lado derecho del área de la parcela).
- ¿Existe de todos modos que reduzco el tamaño de fuente del texto dentro del cuadro de leyenda, de modo que el tamaño del cuadro de leyenda sea pequeño?
Además de todas las excelentes respuestas aquí, las versiones más recientes de matplotlib
y pylab
pueden determinar automáticamente dónde colocar la leyenda sin interferir con las parcelas .
pylab.legend(loc=''best'')
¡Esto colocará automáticamente la leyenda fuera de la trama!
Algo en estas líneas funcionó para mí. Comenzando con un poco de código tomado de Joe, este método modifica el ancho de la ventana para que se ajuste automáticamente a una leyenda a la derecha de la figura.
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
for i in xrange(5):
ax.plot(x, i * x, label=''$y = %ix$''%i)
# Put a legend to the right of the current axis
leg = ax.legend(loc=''center left'', bbox_to_anchor=(1, 0.5))
plt.draw()
# Get the ax dimensions.
box = ax.get_position()
xlocs = (box.x0,box.x1)
ylocs = (box.y0,box.y1)
# Get the figure size in inches and the dpi.
w, h = fig.get_size_inches()
dpi = fig.get_dpi()
# Get the legend size, calculate new window width and change the figure size.
legWidth = leg.get_window_extent().width
winWidthNew = w*dpi+legWidth
fig.set_size_inches(winWidthNew/dpi,h)
# Adjust the window size to fit the figure.
mgr = plt.get_current_fig_manager()
mgr.window.wm_geometry("%ix%i"%(winWidthNew,mgr.window.winfo_height()))
# Rescale the ax to keep its original size.
factor = w*dpi/winWidthNew
x0 = xlocs[0]*factor
x1 = xlocs[1]*factor
width = box.width*factor
ax.set_position([x0,ylocs[0],x1-x0,ylocs[1]-ylocs[0]])
plt.draw()
Aquí hay otra solución, similar a la adición de bbox_extra_artists
y bbox_inches
, donde no tiene que tener sus artistas adicionales en el alcance de su llamada savefig
. Se me ocurrió esto ya que genero la mayor parte de mi trama dentro de las funciones.
En lugar de agregar todas sus adiciones al cuadro delimitador cuando desee escribirlas, puede agregarlas con anticipación a los artistas de la Figure
. Usando algo similar a la respuesta de Franck Dernoncourt arriba :
import matplotlib.pyplot as plt
# data
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]
# plotting function
def gen_plot(x, y):
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(all_x, all_y)
lgd = ax.legend( [ "Lag " + str(lag) for lag in all_x], loc="center right", bbox_to_anchor=(1.3, 0.5))
fig.artists.append(lgd) # Here''s the change
ax.set_title("Title")
ax.set_xlabel("x label")
ax.set_ylabel("y label")
return fig
# plotting
fig = gen_plot(all_x, all_y)
# No need for `bbox_extra_artists`
fig.savefig("image_output.png", dpi=300, format="png", bbox_inches="tight")
Aquí hay un ejemplo del tutorial matplotlib que se encuentra aquí . Este es uno de los ejemplos más simples, pero agregué transparencia a la leyenda y agregué plt.show () para que pueda pegar esto en el shell interactivo y obtener un resultado:
import matplotlib.pyplot as plt
p1, = plt.plot([1, 2, 3])
p2, = plt.plot([3, 2, 1])
p3, = plt.plot([2, 3, 1])
plt.legend([p2, p1, p3], ["line 1", "line 2", "line 3"]).get_frame().set_alpha(0.5)
plt.show()
Como se ha señalado, también puede colocar la leyenda en la trama, o ligeramente desde el borde también. Aquí hay un ejemplo utilizando la API de Plotly Python , hecha con un cuaderno de IPython . Estoy en el equipo
Para empezar, querrás instalar los paquetes necesarios:
import plotly
import math
import random
import numpy as np
Luego, instale Plotly:
un=''IPython.Demo''
k=''1fw3zw2o13''
py = plotly.plotly(username=un, key=k)
def sin(x,n):
sine = 0
for i in range(n):
sign = (-1)**i
sine = sine + ((x**(2.0*i+1))/math.factorial(2*i+1))*sign
return sine
x = np.arange(-12,12,0.1)
anno = {
''text'': ''$//sum_{k=0}^{//infty} //frac {(-1)^k x^{1+2k}}{(1 + 2k)!}$'',
''x'': 0.3, ''y'': 0.6,''xref'': "paper", ''yref'': "paper",''showarrow'': False,
''font'':{''size'':24}
}
l = {
''annotations'': [anno],
''title'': ''Taylor series of sine'',
''xaxis'':{''ticks'':'''',''linecolor'':''white'',''showgrid'':False,''zeroline'':False},
''yaxis'':{''ticks'':'''',''linecolor'':''white'',''showgrid'':False,''zeroline'':False},
''legend'':{''font'':{''size'':16},''bordercolor'':''white'',''bgcolor'':''#fcfcfc''}
}
py.iplot([{''x'':x, ''y'':sin(x,1), ''line'':{''color'':''#e377c2''}, ''name'':''$x////$''},/
{''x'':x, ''y'':sin(x,2), ''line'':{''color'':''#7f7f7f''},''name'':''$ x-//frac{x^3}{6}$''},/
{''x'':x, ''y'':sin(x,3), ''line'':{''color'':''#bcbd22''},''name'':''$ x-//frac{x^3}{6}+//frac{x^5}{120}$''},/
{''x'':x, ''y'':sin(x,4), ''line'':{''color'':''#17becf''},''name'':''$ x-//frac{x^5}{120}$''}], layout=l)
Esto crea su gráfico y le brinda la oportunidad de mantener la leyenda dentro de la trama. El valor predeterminado para la leyenda, si no está configurado, es colocarlo en el gráfico, como se muestra aquí.
Para una ubicación alternativa, puede alinear estrechamente el borde del gráfico y el borde de la leyenda, y eliminar las líneas del borde para un ajuste más estrecho.
Puede mover y cambiar el estilo de la leyenda y el gráfico con código, o con la GUI. Para cambiar la leyenda, tiene las siguientes opciones para ubicar la leyenda dentro del gráfico asignando valores de x e y de <= 1. Por ejemplo:
-
{"x" : 0,"y" : 0}
- Abajo a la izquierda -
{"x" : 1, "y" : 0}
- Abajo a la derecha -
{"x" : 1, "y" : 1}
- Arriba a la derecha -
{"x" : 0, "y" : 1}
- Arriba a la izquierda -
{"x" :.5, "y" : 0}
- Centro inferior -
{"x": .5, "y" : 1}
- Centro superior
En este caso, elegimos la parte superior derecha, estilo de legendstyle = {"x" : 1, "y" : 1}
, también descrita en la documentación :
Crear propiedades de fuente
from matplotlib.font_manager import FontProperties
fontP = FontProperties()
fontP.set_size(''small'')
legend([plot1], "title", prop=fontP)
Hay varias formas de hacer lo que quieres. Para agregar a lo que @inalis y @Navi ya dijeron, puede usar el argumento de la palabra clave bbox_to_anchor
para colocar la leyenda parcialmente fuera de los ejes y / o reducir el tamaño de la fuente.
Antes de considerar reducir el tamaño de la fuente (lo que puede hacer que las cosas sean muy difíciles de leer), intente jugar y colocar la leyenda en diferentes lugares:
Entonces, comencemos con un ejemplo genérico:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
for i in xrange(5):
ax.plot(x, i * x, label=''$y = %ix$'' % i)
ax.legend()
plt.show()
Si hacemos lo mismo, pero si usamos el argumento de la palabra clave bbox_to_anchor
, podemos cambiar la leyenda ligeramente fuera de los límites de los ejes:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
for i in xrange(5):
ax.plot(x, i * x, label=''$y = %ix$'' % i)
ax.legend(bbox_to_anchor=(1.1, 1.05))
plt.show()
Del mismo modo, puede hacer que la leyenda sea más horizontal y / o ponerla en la parte superior de la figura (también estoy activando esquinas redondeadas y una sombra paralela simple):
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
for i in xrange(5):
line, = ax.plot(x, i * x, label=''$y = %ix$''%i)
ax.legend(loc=''upper center'', bbox_to_anchor=(0.5, 1.05),
ncol=3, fancybox=True, shadow=True)
plt.show()
Alternativamente, puede reducir el ancho de la trama actual y colocar la leyenda completamente fuera del eje de la figura:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
for i in xrange(5):
ax.plot(x, i * x, label=''$y = %ix$''%i)
# Shrink current axis by 20%
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
# Put a legend to the right of the current axis
ax.legend(loc=''center left'', bbox_to_anchor=(1, 0.5))
plt.show()
Y de una manera similar, puede reducir la trama verticalmente y colocar una leyenda horizontal en la parte inferior:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
for i in xrange(5):
line, = ax.plot(x, i * x, label=''$y = %ix$''%i)
# Shrink current axis''s height by 10% on the bottom
box = ax.get_position()
ax.set_position([box.x0, box.y0 + box.height * 0.1,
box.width, box.height * 0.9])
# Put a legend below current axis
ax.legend(loc=''upper center'', bbox_to_anchor=(0.5, -0.05),
fancybox=True, shadow=True, ncol=5)
plt.show()
Echa un vistazo a la guía de leyendas de matplotlib . También puede echar un vistazo a plt.figlegend()
. Espero que ayude un poco, de todos modos!
La solución que me funcionó cuando tuve una gran leyenda fue usar un diseño de imagen extra vacío. En el siguiente ejemplo, hice 4 filas y en la parte inferior trazo la imagen con el desplazamiento de la leyenda (bbox_to_anchor) en la parte superior, no se corta.
f = plt.figure()
ax = f.add_subplot(414)
lgd = ax.legend(loc=''upper left'', bbox_to_anchor=(0, 4), mode="expand", borderaxespad=0.3)
ax.autoscale_view()
plt.savefig(fig_name, format=''svg'', dpi=1200, bbox_extra_artists=(lgd,), bbox_inches=''tight'')
No es exactamente lo que pediste, pero encontré que es una alternativa para el mismo problema. Haz la leyenda semi-transparente, así:
Haga esto con:
fig = pylab.figure()
ax = fig.add_subplot(111)
ax.plot(x,y,label=label,color=color)
# Make the legend transparent:
ax.legend(loc=2,fontsize=10,fancybox=True).get_frame().set_alpha(0.5)
# Make a transparent text box
ax.text(0.02,0.02,yourstring, verticalalignment=''bottom'',
horizontalalignment=''left'',
fontsize=10,
bbox={''facecolor'':''white'', ''alpha'':0.6, ''pad'':10},
transform=self.ax.transAxes)
No sé si ya resolvió su problema ... probablemente sí, pero ... Simplemente usé la cadena ''afuera'' para la ubicación, como en Matlab. He importado pylab desde matplotlib. ver el código de la siguiente manera:
from matplotlib as plt
from matplotlib.font_manager import FontProperties
...
...
t = A[:,0]
sensors = A[:,index_lst]
for i in range(sensors.shape[1]):
plt.plot(t,sensors[:,i])
plt.xlabel(''s'')
plt.ylabel(''°C'')
lgd = plt.legend(b,loc=''center left'', bbox_to_anchor=(1, 0.5),fancybox = True, shadow = True)
Para colocar la leyenda fuera del área de trazado, use las palabras clave loc y bbox_to_anchor de la leyenda (). Por ejemplo, el siguiente código colocará la leyenda a la derecha del área de trazado:
legend(loc="upper left", bbox_to_anchor=(1,1))
Para más información, consulte la guía de leyenda.
Respuesta corta: puedes usar bbox_to_anchor
+ bbox_extra_artists
+ bbox_inches=''tight''
.
Respuesta más larga: puede usar bbox_to_anchor
para especificar manualmente la ubicación del cuadro de leyenda, como han señalado otras personas en las respuestas.
Sin embargo, el problema habitual es que el cuadro de leyenda está recortado, por ejemplo:
import matplotlib.pyplot as plt
# data
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]
# Plot
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(all_x, all_y)
# Add legend, title and axis labels
lgd = ax.legend( [ ''Lag '' + str(lag) for lag in all_x], loc=''center right'', bbox_to_anchor=(1.3, 0.5))
ax.set_title(''Title'')
ax.set_xlabel(''x label'')
ax.set_ylabel(''y label'')
fig.savefig(''image_output.png'', dpi=300, format=''png'')
Para evitar que el cuadro de leyenda se recorte, cuando guarde la figura, puede usar los parámetros bbox_extra_artists
y bbox_inches
para pedir a savefig
que incluya elementos recortados en la imagen guardada:
fig.savefig(''image_output.png'', bbox_extra_artists=(lgd,), bbox_inches=''tight'')
Ejemplo (solo cambié la última línea para agregar 2 parámetros a fig.savefig()
):
import matplotlib.pyplot as plt
# data
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]
# Plot
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(all_x, all_y)
# Add legend, title and axis labels
lgd = ax.legend( [ ''Lag '' + str(lag) for lag in all_x], loc=''center right'', bbox_to_anchor=(1.3, 0.5))
ax.set_title(''Title'')
ax.set_xlabel(''x label'')
ax.set_ylabel(''y label'')
fig.savefig(''image_output.png'', dpi=300, format=''png'', bbox_extra_artists=(lgd,), bbox_inches=''tight'')
Deseo que matplotlib permita de forma nativa la ubicación externa para el cuadro de leyenda como lo hace Matlab :
figure
x = 0:.2:12;
plot(x,besselj(1,x),x,besselj(2,x),x,besselj(3,x));
hleg = legend(''First'',''Second'',''Third'',...
''Location'',''NorthEastOutside'')
% Make the text of the legend italic and color it brown
set(hleg,''FontAngle'',''italic'',''TextColor'',[.3,.2,.1])
Simplemente llame a legend()
llame después de la plot()
llame así:
# matplotlib
plt.plot(...)
plt.legend(loc=''center left'', bbox_to_anchor=(1, 0.5))
# Pandas
df.myCol.plot().legend(loc=''center left'', bbox_to_anchor=(1, 0.5))
Los resultados se verían algo como esto:
También puedes probar figlegend
. Es posible crear una leyenda independiente de cualquier objeto Axes. Sin embargo, es posible que deba crear algunas rutas "ficticias" para asegurarse de que el formato de los objetos se transmita correctamente.
Respuesta corta : invoca la opción de arrastrar en la leyenda y muévela interactivamente donde quieras:
ax.legend().draggable()
Respuesta larga : si prefiere colocar la leyenda de forma interactiva / manual en lugar de programada, puede alternar el modo de la leyenda que se puede arrastrar para que pueda arrastrarla a donde desee. Revisa el siguiente ejemplo:
import matplotlib.pylab as plt
import numpy as np
#define the figure and get an axes instance
fig = plt.figure()
ax = fig.add_subplot(111)
#plot the data
x = np.arange(-5, 6)
ax.plot(x, x*x, label=''y = x^2'')
ax.plot(x, x*x*x, label=''y = x^3'')
ax.legend().draggable()
plt.show()