web-dev-qa-db-fra.com

Axe secondaire avec twinx (): comment ajouter une légende?

J'ai un tracé avec deux axes Y, en utilisant twinx(). Je donne aussi des étiquettes aux lignes et je veux les montrer avec legend(), mais je ne réussis qu'à obtenir les étiquettes d'un axe dans la légende:

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

Je n’obtiens donc que les libellés du premier axe de la légende, et non le libellé "temp" du deuxième axe. Comment pourrais-je ajouter ce troisième label à la légende?

enter image description here

243
joris

Vous pouvez facilement ajouter une deuxième légende en ajoutant la ligne:

ax2.legend(loc=0)

Vous aurez ceci:

enter image description here

Mais si vous voulez toutes les étiquettes d’une légende, vous devriez faire quelque chose comme ceci:

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

Ce qui vous donnera ceci:

enter image description here

298
Paul

Je ne sais pas si cette fonctionnalité est nouvelle, mais vous pouvez également utiliser la méthode get_legend_handles_labels () plutôt que de garder vous-même le suivi des lignes et des étiquettes:

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()
154
zgana

À partir de la version 2.1 de matplotlib, vous pouvez utiliser une légende de figure . Au lieu de ax.legend(), qui produit une légende avec les poignées des axes ax, on peut créer une légende de figure.

fig.legend (loc = "en haut à droite")

qui rassemblera toutes les poignées de toutes les sous-parcelles de la figure. Comme il s’agit d’une légende, elle sera placée au coin de la figure et l’argument loc sera relatif à la figure.

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="upper right")

ax.set_xlabel("x [units]")
ax.set_ylabel(r"Quantity 1")
ax2.set_ylabel(r"Quantity 2")

plt.show()

enter image description here

Pour replacer la légende dans les axes, vous devez fournir un bbox_to_anchor et un bbox_transform. Ces derniers seraient les axes transformés des axes dans lesquels la légende devrait résider. Les premiers pourraient être les coordonnées de l’arête définie par loc données en coordonnées d’axes.

fig.legend(loc="upper right", bbox_to_anchor=(1,1), bbox_transform=ax.transAxes)

enter image description here

45

Vous pouvez facilement obtenir ce que vous voulez en ajoutant la ligne dans ax:

ax.plot(0, 0, '-r', label = 'temp')

ou

ax.plot(np.nan, '-r', label = 'temp')

Cela ne représenterait rien d'autre que d'ajouter une étiquette à la légende de ax.

Je pense que c'est un moyen beaucoup plus facile. Il n’est pas nécessaire de suivre les lignes automatiquement lorsque vous n’avez que quelques lignes dans les deuxièmes axes, car il serait très facile de fixer à la main comme ci-dessus. Quoi qu'il en soit, cela dépend de ce dont vous avez besoin.

Le code entier est comme ci-dessous:

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

L'intrigue est comme ci-dessous:

enter image description here


Mise à jour: ajouter une meilleure version:

ax.plot(np.nan, '-r', label = 'temp')

Cela ne fera rien alors que plot(0, 0) peut changer la plage de l'axe.

35
Syrtis Major

Un hack rapide qui peut répondre à vos besoins.

Retirez le cadre de la boîte et positionnez manuellement les deux légendes l'une à côté de l'autre. Quelque chose comme ça..

ax1.legend(loc = (.75,.1), frameon = False)
ax2.legend( loc = (.75, .05), frameon = False)

Où le nuage de locus correspond aux pourcentages de gauche à droite et de bas en haut représentant l'emplacement dans le graphique.

6
user2105997

J'ai trouvé un exemple officiel matplotlib suivant qui utilise Host_subplot pour afficher plusieurs axes y et toutes les étiquettes différentes dans une légende. Aucune solution de contournement nécessaire. La meilleure solution que j'ai trouvée jusqu'à présent. 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()
5
gerrit