แกนทุติยภูมิที่มี twinx (): จะเพิ่มตำนานอย่างไร?


288

ฉันมีโครงเรื่องที่มีแกน y สองแกนใช้twinx()อยู่ ฉันยังให้ป้ายกำกับให้กับบรรทัดและต้องการแสดงด้วยlegend()แต่ฉันก็ทำได้เพียงได้รับเลเบลของหนึ่งแกนในตำนาน:

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

ดังนั้นฉันจะได้รับเลเบลของแกนแรกในตำนานเท่านั้นไม่ใช่เลเบล 'ชั่วคราว' ของแกนที่สอง ฉันจะเพิ่มป้ายกำกับที่สามนี้ในตำนานได้อย่างไร

ป้อนคำอธิบายรูปภาพที่นี่


4
[ ไม่ทำเช่นนี้ในทุกที่ระยะไกลใกล้กับรหัสการผลิตใด ๆ ] เมื่อจุดมุ่งหมายเดียวของฉันคือการสร้างพล็อตที่สวยงามที่มีตำนานที่เหมาะสมโดยเร็วผมใช้สับน่าเกลียดวางแผนอาร์เรย์ว่างบนaxกับการใช้งานผมสไตล์ax2ใน ax.plot([], [], '-r', label = 'temp')กรณีของคุณ มันมากเร็วขึ้นและง่ายกว่าการทำมันอย่างถูกต้อง ...
Neinstein

คำตอบ:


370

คุณสามารถเพิ่มตำนานที่สองได้อย่างง่ายดายโดยเพิ่มบรรทัด:

ax2.legend(loc=0)

คุณจะได้รับสิ่งนี้:

ป้อนคำอธิบายรูปภาพที่นี่

แต่ถ้าคุณต้องการป้ายกำกับทั้งหมดในหนึ่งตำนานคุณควรทำสิ่งนี้:

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

ซึ่งจะให้สิ่งนี้กับคุณ:

ป้อนคำอธิบายรูปภาพที่นี่


2
สิ่งนี้ล้มเหลวด้วยerrorbarแปลง สำหรับวิธีแก้ปัญหาที่จัดการได้อย่างถูกต้องโปรดดูด้านล่าง: stackoverflow.com/a/10129461/1319447
Davide

1
เพื่อป้องกันสองตำนานที่ทับซ้อนกันในกรณีของฉันที่ฉันระบุสอง. legend (loc = 0) คุณควรระบุสองค่าที่แตกต่างกันสำหรับค่าตำแหน่งตำนาน (ทั้งที่ไม่ใช่ 0) โปรดดู: matplotlib.org/api/legend_api.html
Roalt

ax1ผมมีปัญหาบางอย่างเพิ่มบรรทัดเดียวเพื่อแผนบางคนที่มีหลายบรรทัด ในกรณีนี้ใช้lns1=ax1.linesแล้วผนวกlns2เข้ากับรายการนี้
ตาราง Bobby น้อย

ค่าที่แตกต่างกันมาใช้โดยlocมีการอธิบายที่นี่
Dror

1
ดูคำตอบด้านล่างเพื่อหาวิธีอัตโนมัติ (พร้อม matplotlib> = 2.1): stackoverflow.com/a/47370214/653364
joris

183

ฉันไม่แน่ใจว่าการทำงานนี้เป็นของใหม่หรือไม่ แต่คุณสามารถใช้เมธอด get_legend_handles_labels () แทนการติดตามบรรทัดและเลเบลด้วยตัวคุณเอง:

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

1
นี่เป็นทางออกเดียวที่สามารถจัดการขวานที่พล็อตซ้อนทับกับตำนาน (แกนสุดท้ายคืออันที่ควรพล็อตเรื่องตำนาน)
Amelio Vazquez-Reina

5
วิธีการแก้ปัญหานี้ยังทำงานร่วมกับerrorbarแผนการในขณะที่การยอมรับล้มเหลว (แสดงบรรทัดและแถบข้อผิดพลาดแยกจากกันและไม่มีพวกเขาด้วยป้ายขวา) นอกจากนี้มันง่ายกว่า
Davide

จับเล็กน้อย: มันไม่ทำงานถ้าคุณต้องการเขียนทับฉลากax2และมันไม่ได้มีหนึ่งชุดตั้งแต่เริ่มต้น
Ciprian Tomoiagă

หมายเหตุ: สำหรับแปลงแบบคลาสสิกคุณไม่จำเป็นต้องระบุอาร์กิวเมนต์ฉลาก แต่สำหรับคนอื่น ๆ เช่น บาร์ที่คุณต้องการ
belka

นอกจากนี้ยังทำให้ทุกอย่างง่ายขึ้นถ้าคุณไม่รู้ก่อนว่าจะมีการลงจุดเป็นจำนวนเท่าใด
Vegard Jervell

77

จาก matplotlib รุ่น 2.1 เป็นต้นไปคุณอาจใช้ตำนานร่าง แทนที่จะax.legend()สร้างตำนานที่มีด้ามจับจากแกนaxหนึ่งสามารถสร้างตำนานรูป

fig.legend (loc = "มุมขวาบน")

ซึ่งจะรวบรวมการจัดการทั้งหมดจากแผนย่อยทั้งหมดในรูป เนื่องจากมันเป็นตำนานรูปมันจะถูกวางไว้ที่มุมของรูปและการlocโต้แย้งนั้นสัมพันธ์กับรูป

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

ป้อนคำอธิบายรูปภาพที่นี่

เพื่อที่จะวางกลับเข้าไปในตำนานแกนหนึ่งจะจัดหาและbbox_to_anchor bbox_transformหลังคือการเปลี่ยนแกนของแกนที่ตำนานควรอยู่ในอดีตอาจเป็นพิกัดของขอบที่กำหนดโดยlocกำหนดในพิกัดแกน

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

ป้อนคำอธิบายรูปภาพที่นี่


ดังนั้นเวอร์ชัน 2.1 วางจำหน่ายแล้ว? แต่ใน Anaconda 3 ฉันconda upgrade matplotlibไม่พบรุ่นที่ใหม่กว่าฉันยังใช้ v.2.0.2
StayFoolish

1
นี่เป็นวิธีที่สะอาดกว่าในการบรรลุเป้าหมาย
Goutham

1
สวยงามและ
ไพเราะ

1
สิ่งนี้ดูเหมือนจะไม่ทำงานเมื่อคุณมีซับพล็อตจำนวนมาก มันเพิ่มตำนานเดียวสำหรับแผนการย่อยทั้งหมด โดยทั่วไปแล้วหนึ่งต้องการหนึ่งตำนานสำหรับแต่ละแผนย่อยที่มีซีรีส์ในแกนหลักและรองในแต่ละตำนาน
sancho.s ReinstateMonicaCellio

@Sancho ถูกต้องนั่นคือสิ่งที่เขียนในประโยคที่สามของคำตอบนี้ "... ซึ่งจะรวบรวมหมายเลขอ้างอิงทั้งหมดจากรูปแบบย่อยทั้งหมดในรูป"
ImportanceOfBeingErnest

38

คุณสามารถได้รับสิ่งที่คุณต้องการโดยการเพิ่มบรรทัดเป็นขวาน:

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

หรือ

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

สิ่งนี้จะไม่ลงจุด แต่เพิ่มป้ายกำกับให้กับตำนานของขวาน

ฉันคิดว่านี่เป็นวิธีที่ง่ายกว่ามาก ไม่จำเป็นต้องติดตามเส้นโดยอัตโนมัติเมื่อคุณมีเพียงไม่กี่บรรทัดในแกนที่สองเนื่องจากการยึดด้วยมือตามข้างบนนั้นค่อนข้างง่าย อย่างไรก็ตามมันขึ้นอยู่กับสิ่งที่คุณต้องการ

รหัสทั้งหมดดังต่อไปนี้:

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

เนื้อเรื่องมีดังนี้:

ป้อนคำอธิบายรูปภาพที่นี่


อัปเดต: เพิ่มรุ่นที่ดีกว่า:

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

สิ่งนี้จะไม่ทำอะไรเลยในขณะที่plot(0, 0)อาจเปลี่ยนช่วงแกน


ตัวอย่างเพิ่มเติมสำหรับการกระจาย

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)

3
ฉันชอบสิ่งนี้. มันเป็นเรื่องที่น่าเกลียดในแบบที่มัน "หลอก" ระบบ แต่มันง่ายที่จะนำมาใช้
Daniel Power

มันง่ายมากที่จะใช้ แต่เมื่อใช้สิ่งนี้กับการกระจายขนาดผลที่กระจัดกระจายในตำนานเป็นเพียงจุดเล็ก ๆ
greeeeeeen

@greeeeeeen จากนั้นคุณควรระบุขนาดเครื่องหมายเมื่อทำพล็อตการกระจาย :-)
Syrtis Major

@SyrtisMajor แน่นอนว่าฉันลองแล้ว แต่นั่นไม่ได้เปลี่ยนขนาดเครื่องหมายในตำนาน
greeeeeeen

@greeeeeeen คุณเปลี่ยนขนาดเครื่องหมายของตัวแทนการกระจายใช่ไหม ดูโพสต์ของฉันฉันเพิ่มตัวอย่างโค้ด
Syrtis Major

7

แฮ็คด่วนที่อาจเหมาะกับความต้องการของคุณ

ถอดเฟรมออกจากกล่องและวางตำแหน่งของตำนานสองตัวที่อยู่ติดกัน บางสิ่งเช่นนี้ ..

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

ตำแหน่งที่ tuple loc เป็นเปอร์เซ็นต์จากซ้ายไปขวาและจากล่างขึ้นบนลงล่างที่แสดงถึงตำแหน่งในแผนภูมิ


5

ฉันพบตัวอย่าง matplotlib อย่างเป็นทางการต่อไปนี้ที่ใช้ host_subplot เพื่อแสดงแกน y หลายแกนและป้ายกำกับที่แตกต่างกันทั้งหมดในตำนานเดียว ไม่จำเป็นต้องแก้ไขปัญหาใด ๆ ทางออกที่ดีที่สุดที่ฉันพบ 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()

ยินดีต้อนรับสู่ Stack Overflow! โปรดอ้างอิงส่วนที่เกี่ยวข้องมากที่สุดของลิงค์ในกรณีที่เว็บไซต์เป้าหมายไม่สามารถเข้าถึงหรือออฟไลน์อย่างถาวร ดูฉันจะเขียนคำตอบที่ดีได้อย่างไร มุ่งเน้นไปที่คำถามปัจจุบันมากขึ้นในอนาคตอันนี้มีอายุเกือบ 4 ปี
ByteHamster

แน่นอนการค้นหาที่ดี แต่ฉันหวังว่าคุณจะได้นำสิ่งที่คุณเรียนรู้จากตัวอย่างนำไปใช้กับ MWE ของ OP และรวมภาพ
aeroNotAuto
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.