python - pyplot - matplotlib secondary y axis
Eje secundario con twinx(): ¿cómo agregar a la leyenda? (6)
A partir de la versión 2.1 de matplotlib, puede utilizar una leyenda de figura . En lugar de ax.legend()
, que produce una leyenda con los controladores de los ejes de los ejes, se puede crear una figura de leyenda.
fig.legend(loc=1)
que reunirá todos los manejadores de todas las subparcelas en la figura. Dado que es una leyenda de la figura, se colocará en la esquina de la figura y el argumento loc
será relativo a la figura.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,10)
y = np.linspace(0,10)
z = np.sin(x/3)**2*98
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x,y, ''-'', label = ''Quantity 1'')
ax2 = ax.twinx()
ax2.plot(x,z, ''-r'', label = ''Quantity 2'')
fig.legend(loc=1)
ax.set_xlabel("x [units]")
ax.set_ylabel(r"Quantity 1")
ax2.set_ylabel(r"Quantity 2")
plt.show()
Para volver a colocar la leyenda en los ejes, se debería proporcionar un bbox_to_anchor
y un bbox_transform
. El último sería la transformación de los ejes de los ejes en los que debería residir la leyenda. El primero puede ser las coordenadas del borde definido por el lugar dado en las coordenadas de los ejes.
fig.legend(loc=1, bbox_to_anchor=(1,1), bbox_transform=ax.transAxes)
Tengo una trama con dos ejes y, utilizando twinx()
. También asigno etiquetas a las líneas y quiero mostrarlas con legend()
, pero solo logro obtener las etiquetas de un eje en la leyenda:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc(''mathtext'', default=''regular'')
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(time, Swdown, ''-'', label = ''Swdown'')
ax.plot(time, Rn, ''-'', label = ''Rn'')
ax2 = ax.twinx()
ax2.plot(time, temp, ''-r'', label = ''temp'')
ax.legend(loc=0)
ax.grid()
ax.set_xlabel("Time (h)")
ax.set_ylabel(r"Radiation ($MJ/,m^{-2}/,d^{-1}$)")
ax2.set_ylabel(r"Temperature ($^/circ$C)")
ax2.set_ylim(0, 35)
ax.set_ylim(-20,100)
plt.show()
Así que solo obtengo las etiquetas del primer eje en la leyenda, y no la etiqueta ''temp'' del segundo eje. ¿Cómo podría añadir esta tercera etiqueta a la leyenda?
Encontré un siguiente ejemplo oficial de matplotlib que usa host_subplot para mostrar varios ejes y y todas las diferentes etiquetas en una leyenda. No hay solución necesaria. La mejor solución que encontré hasta ahora. http://matplotlib.org/examples/axes_grid/demo_parasite_axes2.html
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
import matplotlib.pyplot as plt
host = host_subplot(111, axes_class=AA.Axes)
plt.subplots_adjust(right=0.75)
par1 = host.twinx()
par2 = host.twinx()
offset = 60
new_fixed_axis = par2.get_grid_helper().new_fixed_axis
par2.axis["right"] = new_fixed_axis(loc="right",
axes=par2,
offset=(offset, 0))
par2.axis["right"].toggle(all=True)
host.set_xlim(0, 2)
host.set_ylim(0, 2)
host.set_xlabel("Distance")
host.set_ylabel("Density")
par1.set_ylabel("Temperature")
par2.set_ylabel("Velocity")
p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature")
p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")
par1.set_ylim(0, 4)
par2.set_ylim(1, 65)
host.legend()
plt.draw()
plt.show()
No estoy seguro de si esta funcionalidad es nueva, pero también puede usar el método get_legend_handles_labels () en lugar de mantener un seguimiento de las líneas y las etiquetas usted mismo:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc(''mathtext'', default=''regular'')
pi = np.pi
# fake data
time = np.linspace (0, 25, 50)
temp = 50 / np.sqrt (2 * pi * 3**2) /
* np.exp (-((time - 13)**2 / (3**2))**2) + 15
Swdown = 400 / np.sqrt (2 * pi * 3**2) * np.exp (-((time - 13)**2 / (3**2))**2)
Rn = Swdown - 10
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(time, Swdown, ''-'', label = ''Swdown'')
ax.plot(time, Rn, ''-'', label = ''Rn'')
ax2 = ax.twinx()
ax2.plot(time, temp, ''-r'', label = ''temp'')
# ask matplotlib for the plotted objects and their labels
lines, labels = ax.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax2.legend(lines + lines2, labels + labels2, loc=0)
ax.grid()
ax.set_xlabel("Time (h)")
ax.set_ylabel(r"Radiation ($MJ/,m^{-2}/,d^{-1}$)")
ax2.set_ylabel(r"Temperature ($^/circ$C)")
ax2.set_ylim(0, 35)
ax.set_ylim(-20,100)
plt.show()
Puede agregar fácilmente una segunda leyenda agregando la línea:
ax2.legend(loc=0)
Obtendrás esto:
Pero si quieres todas las etiquetas en una leyenda, debes hacer algo como esto:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc(''mathtext'', default=''regular'')
time = np.arange(10)
temp = np.random.random(10)*30
Swdown = np.random.random(10)*100-10
Rn = np.random.random(10)*100-10
fig = plt.figure()
ax = fig.add_subplot(111)
lns1 = ax.plot(time, Swdown, ''-'', label = ''Swdown'')
lns2 = ax.plot(time, Rn, ''-'', label = ''Rn'')
ax2 = ax.twinx()
lns3 = ax2.plot(time, temp, ''-r'', label = ''temp'')
# added these three lines
lns = lns1+lns2+lns3
labs = [l.get_label() for l in lns]
ax.legend(lns, labs, loc=0)
ax.grid()
ax.set_xlabel("Time (h)")
ax.set_ylabel(r"Radiation ($MJ/,m^{-2}/,d^{-1}$)")
ax2.set_ylabel(r"Temperature ($^/circ$C)")
ax2.set_ylim(0, 35)
ax.set_ylim(-20,100)
plt.show()
Lo que te dará esto:
Puede obtener fácilmente lo que desea al agregar la línea en el hacha:
ax.plot(0, 0, ''-r'', label = ''temp'')
o
ax.plot(np.nan, ''-r'', label = ''temp'')
Esto no trazaría nada más que añadir una etiqueta a la leyenda del hacha.
Creo que esta es una manera mucho más fácil. No es necesario rastrear las líneas automáticamente cuando solo tiene unas pocas líneas en los segundos ejes, ya que la fijación a mano como la anterior sería bastante fácil. De todos modos, depende de lo que necesites.
El código completo es el siguiente:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc(''mathtext'', default=''regular'')
time = np.arange(22.)
temp = 20*np.random.rand(22)
Swdown = 10*np.random.randn(22)+40
Rn = 40*np.random.rand(22)
fig = plt.figure()
ax = fig.add_subplot(111)
ax2 = ax.twinx()
#---------- look at below -----------
ax.plot(time, Swdown, ''-'', label = ''Swdown'')
ax.plot(time, Rn, ''-'', label = ''Rn'')
ax2.plot(time, temp, ''-r'') # The true line in ax2
ax.plot(np.nan, ''-r'', label = ''temp'') # Make an agent in ax
ax.legend(loc=0)
#---------------done-----------------
ax.grid()
ax.set_xlabel("Time (h)")
ax.set_ylabel(r"Radiation ($MJ/,m^{-2}/,d^{-1}$)")
ax2.set_ylabel(r"Temperature ($^/circ$C)")
ax2.set_ylim(0, 35)
ax.set_ylim(-20,100)
plt.show()
La trama es la siguiente:
Actualización: agregar una mejor versión:
ax.plot(np.nan, ''-r'', label = ''temp'')
Esto no hará nada mientras que la plot(0, 0)
puede cambiar el rango del eje.
Un truco rápido que puede satisfacer sus necesidades.
Quite el marco de la caja y coloque manualmente las dos leyendas una al lado de la otra. Algo como esto..
ax1.legend(loc = (.75,.1), frameon = False)
ax2.legend( loc = (.75, .05), frameon = False)
Donde la tupla de locación es de izquierda a derecha y los porcentajes de abajo a arriba que representan la ubicación en el gráfico.