Trục phụ với Twinx (): làm thế nào để thêm vào huyền thoại?


288

Tôi có một âm mưu với hai trục y, sử dụng twinx(). Tôi cũng đưa nhãn cho các dòng và muốn hiển thị chúng legend(), nhưng tôi chỉ thành công khi lấy nhãn của một trục trong truyền thuyết:

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

Vì vậy, tôi chỉ lấy nhãn của trục thứ nhất trong truyền thuyết, chứ không phải nhãn 'temp' của trục thứ hai. Làm thế nào tôi có thể thêm nhãn thứ ba này vào truyền thuyết?

nhập mô tả hình ảnh ở đây


4
[ Đừng làm điều này ở bất cứ nơi nào gần với bất kỳ mã sản xuất nào ] Khi mục đích duy nhất của tôi là tạo ra một cốt truyện đẹp với huyền thoại ASAP, tôi sử dụng một cách xấu xí để vẽ một mảng trống axvới phong cách tôi sử dụng ax2: trường hợp của bạn , ax.plot([], [], '-r', label = 'temp'). Nó nhanh hơn và đơn giản hơn nhiều so với thực hiện đúng cách ...
Neinstein

Câu trả lời:


370

Bạn có thể dễ dàng thêm một huyền thoại thứ hai bằng cách thêm dòng:

ax2.legend(loc=0)

Bạn sẽ nhận được điều này:

nhập mô tả hình ảnh ở đây

Nhưng nếu bạn muốn tất cả các nhãn trên một huyền thoại thì bạn nên làm một cái gì đó như thế này:

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

Điều này sẽ cung cấp cho bạn điều này:

nhập mô tả hình ảnh ở đây


2
Điều này thất bại với errorbarâm mưu. Để biết giải pháp xử lý chính xác chúng, hãy xem bên dưới: stackoverflow.com/a/10129461/1319447
Davide

1
Để ngăn hai truyền thuyết chồng chéo như trong trường hợp của tôi khi tôi chỉ định hai .legend (loc = 0), bạn nên chỉ định hai giá trị khác nhau cho giá trị vị trí chú thích (cả hai đều khác 0). Xem: matplotlib.org/api/legend_api.html
Roalt

Tôi gặp một số khó khăn khi thêm một dòng vào một số subplot với nhiều dòng ax1. Trong trường hợp này sử dụng lns1=ax1.linesvà sau đó nối lns2vào danh sách này.
Bàn Bobby nhỏ

Các giá trị khác nhau được sử dụng locđược giải thích ở đây
Dror

1
Xem câu trả lời dưới đây để biết cách tự động hơn (với matplotlib> = 2.1): stackoverflow.com/a/47370214/653364
joris

183

Tôi không chắc chức năng này có mới không, nhưng bạn cũng có thể sử dụng phương thức get_legend_handles_labels () thay vì tự theo dõi các dòng và nhãn:

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
Đây là giải pháp duy nhất có thể xử lý các trục trong đó các ô trùng với các huyền thoại (các trục cuối cùng là các trục nên vẽ các huyền thoại)
Amelio Vazquez-Reina

5
Giải pháp này cũng hoạt động với errorbarcác ô, trong khi giải pháp được chấp nhận không thành công (hiển thị một dòng và các lỗi của nó một cách riêng biệt và không có phần nào trong số chúng có nhãn phù hợp). Thêm vào đó, nó đơn giản hơn.
Davide

Bắt nhẹ: nó không hoạt động nếu bạn muốn ghi đè lên nhãn ax2và nó không có một bộ nào từ đầu
Ciprian Tomoiagă

Lưu ý: Đối với các lô cổ điển, bạn không cần chỉ định đối số nhãn. Nhưng đối với những người khác, ví dụ. thanh bạn cần.
belka

Điều này cũng làm cho mọi thứ dễ dàng hơn nhiều nếu bạn không biết trước có bao nhiêu dòng sẽ được vẽ.
Vegard Jervell

77

Từ phiên bản matplotlib 2.1 trở đi, bạn có thể sử dụng chú thích hình . Thay vì ax.legend(), tạo ra một huyền thoại với các tay cầm từ các trục ax, người ta có thể tạo ra một huyền thoại hình

fig.legend (loc = "phía trên bên phải")

sẽ tập hợp tất cả các thẻ điều khiển từ tất cả các ô con trong hình. Vì nó là một huyền thoại hình, nó sẽ được đặt ở góc của hình và locđối số có liên quan đến hình.

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

nhập mô tả hình ảnh ở đây

Để đặt huyền thoại trở lại vào các trục, người ta sẽ cung cấp a bbox_to_anchorvà a bbox_transform. Cái sau sẽ là biến đổi trục của các trục mà huyền thoại sẽ cư trú. Cái trước có thể là tọa độ của cạnh được xác định bởi locđược cho trong tọa độ trục.

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

nhập mô tả hình ảnh ở đây


Vậy, phiên bản 2.1 đã được phát hành chưa? Nhưng trong Anaconda 3, tôi đã thử conda upgrade matplotlibkhông tìm thấy phiên bản mới hơn, tôi vẫn đang sử dụng v.2.0.2
StayFoolish

1
Đây là một cách sạch hơn để đạt được kết quả cuối cùng.
Goutham

1
đẹp và pythonic
Danoodrick 16/07/19

1
Điều này dường như không hoạt động khi bạn có nhiều ô phụ. Nó thêm một huyền thoại duy nhất cho tất cả các ô phụ. Người ta thường cần một chú giải cho mỗi ô phụ, chứa chuỗi ở cả trục chính và trục phụ trong mỗi chú giải.
sancho.s RebstateMonicaCellio

@sancho Đúng, đó là những gì được viết trong câu thứ ba của câu trả lời này, "... sẽ tập hợp tất cả các thẻ điều khiển từ tất cả các ô con trong hình.".
ImportanceOfByingErnest

38

Bạn có thể dễ dàng có được những gì bạn muốn bằng cách thêm dòng trong ax:

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

hoặc là

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

Điều này sẽ không có gì ngoài việc thêm một nhãn cho truyền thuyết về rìu.

Tôi nghĩ rằng đây là một cách dễ dàng hơn nhiều. Không cần thiết phải tự động theo dõi các dòng khi bạn chỉ có một vài dòng trong các trục thứ hai, vì việc sửa bằng tay như trên sẽ khá dễ dàng. Dù sao, nó phụ thuộc vào những gì bạn cần.

Toàn bộ mã như sau:

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

Cốt truyện như sau:

nhập mô tả hình ảnh ở đây


Cập nhật: thêm phiên bản tốt hơn:

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

Điều này sẽ không làm gì trong khi plot(0, 0)có thể thay đổi phạm vi trục.


Một ví dụ nữa cho sự phân tán

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
Tôi thích điều này. Đó là loại xấu xí trong cách nó "lừa" hệ thống, nhưng rất đơn giản để thực hiện.
Daniel Power

Điều này thực sự đơn giản để thực hiện. Nhưng khi sử dụng điều này với phân tán, kích thước phân tán kết quả trong truyền thuyết chỉ là một điểm rất nhỏ.
greeeeeeen

@greeeeeeen Sau đó, bạn chỉ cần chỉ định kích thước điểm đánh dấu khi thực hiện biểu đồ phân tán :-)
Syrtis Major

@SyrtisMajor Tôi, tất nhiên, đã thử điều đó. Nhưng điều đó đã không thay đổi kích thước điểm đánh dấu trong truyền thuyết.
greeeeeeen

@greeeeeeen Bạn đã thay đổi kích thước điểm đánh dấu của phân tán tác nhân chưa? Xem bài đăng của tôi, tôi đã thêm một đoạn mã ví dụ.
Syrtis Major

7

Một bản hack nhanh có thể phù hợp với nhu cầu của bạn ..

Tháo khung của hộp và đặt thủ công hai huyền thoại cạnh nhau. Một cái gì đó như thế này ..

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

Trong đó tuple loc là tỷ lệ phần trăm từ trái sang phải và từ dưới lên trên đại diện cho vị trí trong biểu đồ.


5

Tôi đã tìm thấy một ví dụ matplotlib chính thức sau đây sử dụng host_subplot để hiển thị nhiều trục y và tất cả các nhãn khác nhau trong một chú giải. Không có cách giải quyết cần thiết. Giải pháp tốt nhất tôi tìm thấy cho đến nay. 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()

Chào mừng bạn đến với Stack Overflow! Vui lòng trích dẫn phần có liên quan nhất của liên kết, trong trường hợp trang đích không thể truy cập được hoặc ngoại tuyến vĩnh viễn. Xem Làm thế nào để tôi viết một câu trả lời tốt . Tập trung vào các câu hỏi hiện tại hơn trong tương lai, câu hỏi này đã gần 4 tuổi.
Byteroulette

Quả thực là một phát hiện tốt nhưng tôi ước rằng bạn đã lấy những gì bạn học được từ ví dụ, áp dụng nó vào MWE của OP và bao gồm một hình ảnh.
aeroNotAuto
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.