python - كيفية وضع أسطورة من المؤامرة




matplotlib legend (11)

وضع bbox_to_anchor الإيضاح ( bbox_to_anchor )

يتم وضع plt.legend إيضاح داخل المربع المحيط للمحاور باستخدام وسيطة loc إلى plt.legend .
على سبيل المثال ، loc="upper right" تضع وسيلة الإيضاح في الزاوية اليمنى العليا من المربع المحيط ، والتي تقلص بشكل افتراضي من (0,0) إلى (1,1) في إحداثيات المحاور (أو في ترميز المربع المحيط (x0,y0, width, height)=(0,0,1,1) ).

لوضع وسيلة الإيضاح خارج مربع محاور المحاور ، يمكن تحديد مجموعة (x0,y0) من إحداثيات المحاور للركن السفلي الأيسر من وسيلة الإيضاح.

plt.legend(loc=(1.04,0))

ومع ذلك ، قد يكون أسلوبًا أكثر تنوعًا هو تحديد المربع المحيط الذي يجب وضع bbox_to_anchor فيه ، باستخدام الوسيطة bbox_to_anchor . يمكن للمرء أن يقيد نفسه لتزويد فقط (x0,y0) جزء من bbox. يؤدي هذا إلى إنشاء مربع نطاق صفري ، حيث يتم توسيع وسيلة الإيضاح في الاتجاه المعطى بواسطة وسيطة loc . على سبيل المثال

plt.legend(bbox_to_anchor=(1.04,1), loc="upper left")

يضع الأسطورة خارج المحاور ، بحيث الزاوية اليسرى العليا من (1.04,1) الإيضاح في موضع (1.04,1) في إحداثيات محاور.

يتم إعطاء أمثلة أخرى أدناه ، بالإضافة إلى ذلك يتم عرض التفاعل بين الحجج المختلفة مثل mode و ncols .

l1 = plt.legend(bbox_to_anchor=(1.04,1), borderaxespad=0)
l2 = plt.legend(bbox_to_anchor=(1.04,0), loc="lower left", borderaxespad=0)
l3 = plt.legend(bbox_to_anchor=(1.04,0.5), loc="center left", borderaxespad=0)
l4 = plt.legend(bbox_to_anchor=(0,1.02,1,0.2), loc="lower left",
                mode="expand", borderaxespad=0, ncol=3)
l5 = plt.legend(bbox_to_anchor=(1,0), loc="lower right", 
                bbox_transform=fig.transFigure, ncol=3)
l6 = plt.legend(bbox_to_anchor=(0.4,0.8), loc="upper right")

يمكن العثور على تفاصيل حول كيفية تفسير الوسيطة 4-tuple إلى bbox_to_anchor ، كما هو الحال في l4 ، في هذا السؤال . يقوم mode="expand" الإيضاح أفقيًا داخل مربع الإحاطة الموضح بـ 4-tuple. للحصول على وسيلة إيضاح موسعة رأسياً ، انظر هذا السؤال .

في بعض الأحيان قد يكون من المفيد تحديد المربع المحيط في إحداثيات الشكل بدلاً من إحداثيات المحاور. يظهر هذا في المثال l5 من أعلى ، حيث يتم استخدام الوسيطة bbox_transform الإيضاح في الزاوية السفلية اليسرى من الشكل.

المعالجة البعدية

إن وضع الأسطورة خارج المحاور يؤدي غالبًا إلى الوضع غير المرغوب فيه تمامًا أو جزئيًا خارج لوحة الشكل.

حلول لهذه المشكلة هي:

  • اضبط معلمات subplot
    يمكن للمرء ضبط معلمات subplot مثل ، أن تأخذ المحاور مساحة أقل داخل الشكل (وبالتالي ترك مساحة أكبر إلى plt.subplots_adjust الإيضاح) باستخدام plt.subplots_adjust . على سبيل المثال

    plt.subplots_adjust(right=0.7)
    

    يترك مساحة 30 ٪ على الجانب الأيمن من الشكل ، حيث يمكن للمرء وضع الأسطورة.

  • تخطيط ضيق
    استخدام plt.tight_layout يسمح بضبط المعلمات الفرعية تلقائيًا بحيث تبقى العناصر في الشكل محكمة ضد حواف الشكل. لسوء الحظ ، لا تؤخذ هذه الأسطورة في الحسبان في هذه الآلية ، ولكن يمكننا توفير مربع مستطيل بحيث يتم احتواء المساحة الفرعية بأكملها (بما في ذلك الملصقات).

    plt.tight_layout(rect=[0,0,0.75,1])
    
  • حفظ الرقم مع bbox_inches = "tight"
    يمكن استخدام الوسيطة bbox_inches = "tight" إلى plt.savefig لحفظ الرقم بحيث يتم احتواء كل الفنان على اللوحة (بما في ذلك plt.savefig الإيضاح) في المساحة المحفوظة. إذا لزم الأمر ، يتم ضبط حجم الشكل تلقائيًا.

    plt.savefig("output.png", bbox_inches="tight")
    
  • ضبط تلقائيا params subplot
    طريقة لضبط موضع الوحدة الفرعية تلقائيًا بحيث يمكن إدراج الأسطورة داخل اللوحة بدون تغيير حجم الشكل في هذه الإجابة: إنشاء شكل بحجم دقيق وبدون حشوة (والأسطورة خارج المحاور)

مقارنة بين الحالات التي نوقشت أعلاه:

البدائل

أسطورة الرقم
يمكن للمرء استخدام وسيلة إيضاح إلى الشكل بدلاً من المحاور ، matplotlib.figure.Figure.legend . لقد أصبح هذا مفيدًا بشكل خاص لإصدار matplotlib> = 2.1 ، حيث لا توجد حاجة إلى وسيطات خاصة

fig.legend(loc=7) 

لإنشاء أسطورة لجميع الفنانين في محاور مختلفة من هذا الرقم. يتم وضع وسيلة الإيضاح باستخدام وسيطة loc ، شبيهة بكيفية وضعه داخل محاور ، ولكن بالإشارة إلى الشكل الكامل - وبالتالي سيكون خارج المحاور إلى حد ما تلقائيًا. ما تبقى هو ضبط الوحدات الفرعية بحيث لا يوجد تداخل بين وسيلة الإيضاح والفؤوس. هنا ستكون نقطة "ضبط المعلمات الفرعية" من الأعلى مفيدة. مثال:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,2*np.pi)
colors=["#7aa0c4","#ca82e1" ,"#8bcd50","#e18882"]
fig, axes = plt.subplots(ncols=2)
for i in range(4):
    axes[i//2].plot(x,np.sin(x+i), color=colors[i],label="y=sin(x+{})".format(i))

fig.legend(loc=7)
fig.tight_layout()
fig.subplots_adjust(right=0.75)   
plt.show()

أسطورة داخل محاور subplot مخصصة
قد يكون بديل استخدام bbox_to_anchor الإيضاح في محاور subplot المخصصة ( lax ). نظرًا لأنه يجب أن تكون الوحدة الفرعية الأسطورة أصغر من gridspec_kw={"width_ratios":[4,1]} ، فقد نستخدم gridspec_kw={"width_ratios":[4,1]} عند إنشاء المحاور. يمكننا إخفاء المحاور lax.axis("off") ولكن لا يزال وضع وسيلة إيضاح فيها. يجب الحصول على مقابض التسمية المميزة والتسميات من المؤامرة الحقيقية عبر h,l = ax.get_legend_handles_labels() ، ويمكن بعد ذلك تزويدك أسطورة في subplot lax ، lax.legend(h,l) . مثال كامل هو أدناه.

import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = 6,2

fig, (ax,lax) = plt.subplots(ncols=2, gridspec_kw={"width_ratios":[4,1]})
ax.plot(x,y, label="y=sin(x)")
....

h,l = ax.get_legend_handles_labels()
lax.legend(h,l, borderaxespad=0)
lax.axis("off")

plt.tight_layout()
plt.show()

ينتج عن ذلك قطعة أرض تشبه بصريا القطعة من فوق:

يمكننا أيضًا استخدام المحاور الأولى لوضع bbox_transform الإيضاح ، ولكن استخدام bbox_transform من محاور bbox_transform الإيضاح ،

ax.legend(bbox_to_anchor=(0,0,1,1), bbox_transform=lax.transAxes)
lax.axis("off")

في هذا الأسلوب ، لا نحتاج إلى الحصول على مؤشرات bbox_to_anchor الإيضاح خارجيًا ، ولكننا نحتاج إلى تحديد الوسيطة bbox_to_anchor .

مزيد من القراءة والملاحظات:

  • ضع في اعتبارك دليل الأساطير matplotlib مع بعض أمثلة الأشياء الأخرى التي تريد القيام بها مع الأساطير.
  • يمكن العثور على بعض أمثلة التعليمات البرمجية الخاصة بوضع الأساطير للمخططات الدائرية مباشرة في الإجابة على هذا السؤال: Python - Legend يتداخل مع المخطط الدائري
  • يمكن لوسيطة loc أن تأخذ الأرقام بدلاً من السلاسل ، مما يجعل المكالمات قصيرة ، ومع ذلك ، فهي غير محددة بشكل حدسي جدًا لبعضها البعض. هنا هو الخريطة كمرجع:

لديّ سلسلة من 20 قطعة (وليس نماذج فرعية) يتم إجراؤها في شكل واحد. أريد أن تكون الأسطورة خارج المربع. في الوقت نفسه ، لا أريد تغيير المحاور ، حيث يتم تقليل حجم الشكل. يرجى مساعدتي للاستفسارات التالية:

  1. أرغب في الاحتفاظ بمربع وسيلة الإيضاح خارج منطقة الرسم. (أريد أن تكون الأسطورة بالخارج على الجانب الأيمن من منطقة الرسم).
  2. هل هناك على أي حال أنا تقليل حجم الخط من النص داخل المربع وسيلة الإيضاح ، بحيث يكون حجم مربع وسيلة الإيضاح صغيرًا.

إجابة مختصرة: يمكنك استخدام bbox_to_anchor + bbox_extra_artists + bbox_inches='tight' .

إجابة أطول: يمكنك استخدام bbox_to_anchor لتحديد موقع مربع bbox_to_anchor يدويًا ، كما أشار بعض الأشخاص الآخرين في الإجابات.

ومع ذلك ، فإن المشكلة المعتادة هي أن صندوق الأسطورة يتم اقتصاصه ، على سبيل المثال:

import matplotlib.pyplot as plt

# data 
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]

# Plot
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(all_x, all_y)

# Add legend, title and axis labels
lgd = ax.legend( [ 'Lag ' + str(lag) for lag in all_x], loc='center right', bbox_to_anchor=(1.3, 0.5))
ax.set_title('Title')
ax.set_xlabel('x label')
ax.set_ylabel('y label')

fig.savefig('image_output.png', dpi=300, format='png')

لمنع المربع الأسطورة من الحصول على الاقتصاص ، عند حفظ الشكل ، يمكنك استخدام المعلمات bbox_extra_artists و bbox_inches لطلب savefig لتضمين العناصر التي تم اقتصاصها في الصورة المحفوظة:

fig.savefig('image_output.png', bbox_extra_artists=(lgd,), bbox_inches='tight')

مثال (قمت فقط بتغيير السطر الأخير لإضافة معلمتين إلى fig.savefig() ):

import matplotlib.pyplot as plt

# data 
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]

# Plot
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(all_x, all_y)

# Add legend, title and axis labels
lgd = ax.legend( [ 'Lag ' + str(lag) for lag in all_x], loc='center right', bbox_to_anchor=(1.3, 0.5))
ax.set_title('Title')
ax.set_xlabel('x label')
ax.set_ylabel('y label')    

fig.savefig('image_output.png', dpi=300, format='png', bbox_extra_artists=(lgd,), bbox_inches='tight')

أتمنى لو أن matplotlib قد يسمح أصلاً بالوصول إلى موقع خارجي لمربع الأسطورة كما يفعل Matlab :

figure
x = 0:.2:12;
plot(x,besselj(1,x),x,besselj(2,x),x,besselj(3,x));
hleg = legend('First','Second','Third',...
              'Location','NorthEastOutside')
% Make the text of the legend italic and color it brown
set(hleg,'FontAngle','italic','TextColor',[.3,.2,.1])


إليك حلاً آخر ، مشابهًا لإضافة bbox_extra_artists و bbox_inches ، حيث لا يلزم أن يكون لديك فنانين إضافيين في نطاق اتصال savefig . لقد توصلت إلى هذا منذ أن أنشأت معظم مؤامراتي الداخلية.

بدلاً من إضافة جميع الإضافات إلى المربع المحيط عندما تريد كتابتها ، يمكنك إضافتها مسبقًا إلى فناني Figure . باستخدام شيء مماثل لإجابة فرانك Dernoncourt أعلاه :

import matplotlib.pyplot as plt

# data 
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]

# plotting function
def gen_plot(x, y):
    fig = plt.figure(1)
    ax = fig.add_subplot(111)
    ax.plot(all_x, all_y)
    lgd = ax.legend( [ "Lag " + str(lag) for lag in all_x], loc="center right", bbox_to_anchor=(1.3, 0.5))
    fig.artists.append(lgd) # Here's the change
    ax.set_title("Title")
    ax.set_xlabel("x label")
    ax.set_ylabel("y label")
    return fig

# plotting
fig = gen_plot(all_x, all_y)

# No need for `bbox_extra_artists`
fig.savefig("image_output.png", dpi=300, format="png", bbox_inches="tight")

وهنا المؤامرة ولدت.


إنشاء خصائص الخط

from matplotlib.font_manager import FontProperties

fontP = FontProperties()
fontP.set_size('small')
legend([plot1], "title", prop=fontP)

عمل شيء على طول هذه الخطوط بالنسبة لي. بدءًا من جزء من التعليمة البرمجية المأخوذة من جو ، تعدّل هذه الطريقة عرض النافذة ليتلاءم مع وسيلة إيضاح بشكل تلقائي على يمين الشكل.

import matplotlib.pyplot as plt
import numpy as np

plt.ion()

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$'%i)

# Put a legend to the right of the current axis
leg = ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))

plt.draw()

# Get the ax dimensions.
box = ax.get_position()
xlocs = (box.x0,box.x1)
ylocs = (box.y0,box.y1)

# Get the figure size in inches and the dpi.
w, h = fig.get_size_inches()
dpi = fig.get_dpi()

# Get the legend size, calculate new window width and change the figure size.
legWidth = leg.get_window_extent().width
winWidthNew = w*dpi+legWidth
fig.set_size_inches(winWidthNew/dpi,h)

# Adjust the window size to fit the figure.
mgr = plt.get_current_fig_manager()
mgr.window.wm_geometry("%ix%i"%(winWidthNew,mgr.window.winfo_height()))

# Rescale the ax to keep its original size.
factor = w*dpi/winWidthNew
x0 = xlocs[0]*factor
x1 = xlocs[1]*factor
width = box.width*factor
ax.set_position([x0,ylocs[0],x1-x0,ylocs[1]-ylocs[0]])

plt.draw()

كان الحل الذي عمل بالنسبة لي عندما كان لدي أسطورة ضخمة هو استخدام تخطيط صورة فارغ إضافي. في المثال التالي ، قمت بعمل 4 صفوف وفي الجزء السفلي ، أرسم الصورة مع إزاحة عن الأسطورة (bbox_to_anchor) في الأعلى لا تحصل على قطع.

f = plt.figure()
ax = f.add_subplot(414)
lgd = ax.legend(loc='upper left', bbox_to_anchor=(0, 4), mode="expand", borderaxespad=0.3)
ax.autoscale_view()
plt.savefig(fig_name, format='svg', dpi=1200, bbox_extra_artists=(lgd,), bbox_inches='tight')

لا أعرف ما إذا كنت قد قمت بالفعل بتصنيف مشكلتك ... ربما نعم ، ولكن ... أنا ببساطة استخدمت السلسلة "خارج" للموقع ، كما هو الحال في matlab. لقد استوردت pylab من matplotlib. انظر الكود على النحو التالي:

from matplotlib as plt
from matplotlib.font_manager import FontProperties
...
...
t = A[:,0]
sensors = A[:,index_lst]

for i in range(sensors.shape[1]):
    plt.plot(t,sensors[:,i])

plt.xlabel('s')
plt.ylabel('°C')
lgd = plt.legend(b,loc='center left', bbox_to_anchor=(1, 0.5),fancybox = True, shadow = True)

انقر لرؤية المؤامرة


لوضع وسيلة الإيضاح خارج منطقة الرسم ، استخدم loc و bbox_to_anchor الكلمات الأساسية من وسيلة الإيضاح (). على سبيل المثال ، سيضع التعليمة البرمجية التالية وسيلة الإيضاح على يمين ناحية الرسم:

legend(loc="upper left", bbox_to_anchor=(1,1))

لمزيد من المعلومات ، راجع دليل وسيلة الإيضاح


هنا مثال من البرنامج التعليمي matplotlib وجدت هنا . هذا هو أحد الأمثلة الأكثر بساطة ولكنني أضفت الشفافية إلى وسيلة الإيضاح وإضافة plt.show () حتى تتمكن من لصقها في الغلاف التفاعلي والحصول على نتيجة:

import matplotlib.pyplot as plt
p1, = plt.plot([1, 2, 3])
p2, = plt.plot([3, 2, 1])
p3, = plt.plot([2, 3, 1])
plt.legend([p2, p1, p3], ["line 1", "line 2", "line 3"]).get_frame().set_alpha(0.5)
plt.show()

هناك عدد من الطرق لفعل ما تريد. للإضافة إلى ما ذكرهinalis وNavi مسبقًا ، يمكنك استخدام الوسيطة الكلمة الأساسية bbox_to_anchor الإيضاح بشكل جزئي خارج المحاور و / أو تقليل حجم الخط.

قبل أن تفكر في تقليل حجم الخط (الذي يمكن أن يجعل الأمور صعبًا للقراءة) ، حاول اللعب مع وضع الأسطورة في أماكن مختلفة:

لذا ، لنبدأ بمثال عام:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$' % i)

ax.legend()

plt.show()

إذا فعلنا نفس الشيء ، ولكن bbox_to_anchor الوسيطة الكلمة الرئيسية bbox_to_anchor يمكننا نقل الأسطورة قليلاً خارج حدود المحاور:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$' % i)

ax.legend(bbox_to_anchor=(1.1, 1.05))

plt.show()

وبالمثل ، يمكنك جعل الأسطورة أكثر أفقيًا و / أو وضعها في أعلى الشكل (أنا أيضًا أقوم بتشغيل الزوايا الدائرية والظل المسقط البسيط):

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    line, = ax.plot(x, i * x, label='$y = %ix$'%i)

ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.05),
          ncol=3, fancybox=True, shadow=True)
plt.show()

بدلاً من ذلك ، يمكنك تقليص عرض المخطط الحالي ووضع الأسطورة بالكامل خارج محور الشكل:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$'%i)

# Shrink current axis by 20%
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])

# Put a legend to the right of the current axis
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))

plt.show()

وبطريقة مشابهة ، يمكنك تقليص المؤامرة عموديًا ، ووضع الأسطورة الأفقية في الأسفل:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    line, = ax.plot(x, i * x, label='$y = %ix$'%i)

# Shrink current axis's height by 10% on the bottom
box = ax.get_position()
ax.set_position([box.x0, box.y0 + box.height * 0.1,
                 box.width, box.height * 0.9])

# Put a legend below current axis
ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05),
          fancybox=True, shadow=True, ncol=5)

plt.show()

إلقاء نظرة على دليل أسطورة matplotlib . يمكنك أيضًا إلقاء نظرة على plt.figlegend() . نأمل أن يساعد قليلا ، على أي حال!


الجواب القصير : يمكنك استدعاء السحب على وسيلة الإيضاح ونقلها بشكل تبادلي أينما تريد:

ax.legend().draggable()

إجابة طويلة : إذا كنت تفضل وضع وسيلة الإيضاح بشكل تفاعلي / يدوي بدلاً من برمجياً ، يمكنك تبديل الوضع القابل للسحب من وسيلة الإيضاح بحيث يمكنك سحبها إلى أي مكان تريده. تحقق من المثال أدناه:

import matplotlib.pylab as plt
import numpy as np
#define the figure and get an axes instance
fig = plt.figure()
ax = fig.add_subplot(111)
#plot the data
x = np.arange(-5, 6)
ax.plot(x, x*x, label='y = x^2')
ax.plot(x, x*x*x, label='y = x^3')
ax.legend().draggable()
plt.show()






legend