Sumbu sekunder dengan twinx (): bagaimana cara menambahkan legenda?

288

Saya punya plot dengan dua sumbu y, menggunakan twinx(). Saya juga memberi label pada garis, dan ingin menunjukkannya legend(), tetapi saya hanya berhasil mendapatkan label satu sumbu dalam legenda:

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

Jadi saya hanya mendapatkan label sumbu pertama dalam legenda, dan bukan label 'temp' dari sumbu kedua. Bagaimana saya bisa menambahkan label ketiga ini ke legenda?

masukkan deskripsi gambar di sini

Joris
sumber
4
[ Jangan lakukan ini di mana pun yang dekat dengan kode produksi mana pun ] Ketika satu-satunya tujuan saya adalah untuk menghasilkan plot yang indah dengan legenda yang sesuai ASAP, saya menggunakan hack jelek untuk merencanakan array kosong axdengan gaya yang saya gunakan di ax2: in kasus Anda ax.plot([], [], '-r', label = 'temp'),. Jauh lebih cepat dan sederhana daripada melakukannya dengan benar ...
Neinstein

Jawaban:

370

Anda dapat dengan mudah menambahkan legenda kedua dengan menambahkan baris:

ax2.legend(loc=0)

Anda akan mendapatkan ini:

masukkan deskripsi gambar di sini

Tetapi jika Anda ingin semua label pada satu legenda maka Anda harus melakukan sesuatu seperti ini:

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

Yang akan memberi Anda ini:

masukkan deskripsi gambar di sini

Paul
sumber
2
Ini gagal dengan errorbarplot. Untuk solusi yang menangani mereka dengan benar, lihat di bawah: stackoverflow.com/a/10129461/1319447
Davide
1
Untuk mencegah dua legenda yang tumpang tindih seperti dalam kasus saya di mana saya menetapkan dua .legend (loc = 0), Anda harus menentukan dua nilai yang berbeda untuk nilai lokasi legenda (keduanya selain 0). Lihat: matplotlib.org/api/legend_api.html
Roalt
Saya mengalami kesulitan menambahkan satu baris ke beberapa subplot dengan beberapa baris ax1. Dalam hal ini gunakan lns1=ax1.linesdan kemudian tambahkan lns2ke daftar ini.
Little Bobby Tables
Nilai-nilai berbeda yang digunakan locdijelaskan di sini
Dror
1
Lihat jawaban di bawah ini untuk cara yang lebih otomatis (dengan matplotlib> = 2.1): stackoverflow.com/a/47370214/653364
joris
183

Saya tidak yakin apakah fungsi ini baru, tetapi Anda juga dapat menggunakan metode get_legend_handles_labels () daripada melacak sendiri baris dan label:

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()
zgana
sumber
1
Ini adalah satu-satunya solusi yang dapat menangani kapak di mana plot tumpang tindih dengan legenda (kapak terakhir adalah kapak yang seharusnya memplot legenda)
Amelio Vazquez-Reina
5
Solusi ini juga berfungsi dengan errorbarplot, sedangkan yang diterima gagal (menunjukkan garis dan errorbars secara terpisah, dan tidak ada yang memiliki label yang tepat). Plus, lebih sederhana.
Davide
sedikit menangkap: tidak berfungsi jika Anda ingin menimpa label untuk ax2dan tidak memiliki satu set sejak awal
Ciprian Tomoiagă
Catatan: Untuk plot klasik, Anda tidak perlu menentukan argumen label. Tetapi untuk orang lain, mis. Anda perlu bar.
belka
Ini juga membuat segalanya lebih mudah jika Anda tidak tahu sebelumnya berapa banyak garis yang akan diplot.
Vegard Jervell
77

Dari matplotlib versi 2.1 dan seterusnya, Anda dapat menggunakan legenda gambar . Alih-alih ax.legend(), yang menghasilkan legenda dengan gagang dari kapak ax, orang dapat membuat tokoh legenda

fig.legend (loc = "kanan atas")

yang akan mengumpulkan semua pegangan dari semua subplot pada gambar. Karena itu adalah legenda tokoh, itu akan ditempatkan di sudut gambar, dan locargumen itu relatif terhadap sosok itu.

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

masukkan deskripsi gambar di sini

Untuk menempatkan legenda kembali ke kapak, orang akan memasok a bbox_to_anchordan a bbox_transform. Yang terakhir akan menjadi transformasi sumbu dari sumbu yang harus ditinggali legenda. Yang pertama mungkin adalah koordinat tepi yang ditentukan dengan locdiberikan dalam koordinat sumbu.

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

masukkan deskripsi gambar di sini

ImportanceOfBeingErnest
sumber
Jadi, versi 2.1 sudah dirilis? Tetapi di Anaconda 3, saya mencoba conda upgrade matplotlibtidak menemukan versi yang lebih baru, saya masih menggunakan v.2.0.2
StayFoolish
1
Ini adalah cara yang lebih bersih untuk mencapai hasil akhir.
Goutham
1
cantik dan pythonic
DanGoodrick
1
Ini tampaknya tidak berfungsi ketika Anda memiliki banyak subplot. Ini menambahkan legenda tunggal untuk semua subplot. Biasanya satu legenda membutuhkan satu legenda untuk setiap subplot, yang berisi seri di sumbu primer dan sekunder di setiap legenda.
sancho.s ReinstateMonicaCellio
@ sancho Benar, itulah yang ditulis dalam kalimat ketiga dari jawaban ini, "... yang akan mengumpulkan semua pegangan dari semua subplot pada gambar.".
ImportanceOfBeingErnest
38

Anda dapat dengan mudah mendapatkan apa yang Anda inginkan dengan menambahkan baris dalam kapak:

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

atau

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

Ini tidak akan merencanakan apa pun selain menambahkan label ke legenda kapak.

Saya pikir ini adalah cara yang jauh lebih mudah. Tidak perlu melacak garis secara otomatis ketika Anda hanya memiliki beberapa garis di sumbu kedua, karena memperbaiki dengan tangan seperti di atas akan sangat mudah. Bagaimanapun, itu tergantung pada apa yang Anda butuhkan.

Seluruh kode adalah sebagai berikut:

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

Plotnya seperti di bawah ini:

masukkan deskripsi gambar di sini


Perbarui: tambahkan versi yang lebih baik:

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

Ini tidak akan melakukan apa-apa sementara plot(0, 0)dapat mengubah kisaran sumbu.


Satu contoh tambahan untuk pencar

ax.scatter([], [], s=100, label = 'temp')  # Make an agent in ax
ax2.scatter(time, temp, s=10)  # The true scatter in ax2

ax.legend(loc=1, framealpha=1)
Syrtis Major
sumber
3
Saya suka ini. Agak jelek dalam hal "trik" sistem, tetapi sangat sederhana untuk diterapkan.
Daniel Power
Ini sangat sederhana untuk diterapkan. Tetapi ketika menggunakan ini dengan scatter, ukuran scatter yang dihasilkan dalam legenda hanyalah titik kecil.
greeeeeeen
@greeeeeeen Maka Anda hanya harus menentukan ukuran penanda saat membuat plot pencar :-)
Syrtis Major
@SyrtisMajor I, tentu saja, mencobanya. Tapi itu tidak mengubah ukuran marker dalam legenda.
greeeeeeen
@greeeeeeen Sudahkah Anda mengubah ukuran marker dari pencar agen? Lihat posting saya, saya menambahkan potongan kode contoh.
Syrtis Major
7

Retas cepat yang mungkin sesuai dengan kebutuhan Anda ..

Lepaskan bingkai kotak dan posisikan secara manual kedua legenda di samping satu sama lain. Sesuatu seperti ini..

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

Di mana loc locup adalah persentase dari kiri ke kanan dan dari bawah ke atas yang mewakili lokasi dalam bagan.

pengguna2105997
sumber
5

Saya menemukan contoh resmi matplotlib berikut yang menggunakan host_subplot untuk menampilkan banyak sumbu y dan semua label berbeda dalam satu legenda. Tidak ada solusi yang diperlukan. Solusi terbaik yang saya temukan sejauh ini. 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()
gerrit
sumber
Selamat Datang di Stack Overflow! Silakan kutip bagian yang paling relevan dari tautan, jika situs target tidak dapat dijangkau atau offline secara permanen. Lihat Bagaimana cara menulis jawaban yang baik . Berfokuslah pada pertanyaan-pertanyaan terkini di masa depan, pertanyaan ini sudah hampir 4 tahun.
ByteHamster
Memang temuan yang bagus tetapi saya berharap Anda akan mengambil apa yang Anda pelajari dari contoh, menerapkannya pada OP OP, dan memasukkan gambar.
aeroNotAuto