将matplotlib图例移动到轴的外侧,使其被图框截断
我熟悉以下问题:
Matplotlib savefig与图外的图例
如何把这个传说排除在阴谋之外
似乎这些问题的答案有能力摆脱轴的精确缩小的豪华,以便传说适合。
但是,缩小坐标轴并不是一个理想的解决scheme,因为它使数据变小,实际上更难解释。 特别是当它的复杂和有很多事情发生…因此需要一个大的传奇
文档中复杂图例的示例演示了对此的需求,因为图中的图例实际上完全遮挡了多个数据点。
http://matplotlib.sourceforge.net/users/legend_guide.html#legend-of-complex-plots
我希望能够做的是dynamic扩大数字框的大小,以适应扩大的数字图例。
import matplotlib.pyplot as plt import numpy as np x = np.arange(-2*np.pi, 2*np.pi, 0.1) fig = plt.figure(1) ax = fig.add_subplot(111) ax.plot(x, np.sin(x), label='Sine') ax.plot(x, np.cos(x), label='Cosine') ax.plot(x, np.arctan(x), label='Inverse tan') lgd = ax.legend(loc=9, bbox_to_anchor=(0.5,0)) ax.grid('on')
请注意,最终的标签“反转棕褐色”实际上是在数字框外面(而且看起来很差 – 不是出版质量!)
最后,我被告知这是在R和LaTeX中的正常行为,所以我有点困惑,为什么在python中如此困难…是否有历史原因? Matlab在这个问题上同样很差吗?
我在pastebin http://pastebin.com/grVjc007上有这个代码的(稍微)更长的版本
对不起EMS,但我实际上刚刚从matplotlib maill list中得到了另一个回应(感谢Benjamin Root)。
我正在寻找的代码是调整savefig调用:
fig.savefig('samplefigure', bbox_extra_artists=(lgd,), bbox_inches='tight') #Note that the bbox_extra_artists must be an iterable
这显然与调用tight_layout类似,但是允许savefig在计算中考虑额外的艺术家。 这实际上是根据需要调整了数字框的大小。
import matplotlib.pyplot as plt import numpy as np x = np.arange(-2*np.pi, 2*np.pi, 0.1) fig = plt.figure(1) ax = fig.add_subplot(111) ax.plot(x, np.sin(x), label='Sine') ax.plot(x, np.cos(x), label='Cosine') ax.plot(x, np.arctan(x), label='Inverse tan') handles, labels = ax.get_legend_handles_labels() lgd = ax.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5,-0.1)) ax.grid('on') fig.savefig('samplefigure', bbox_extra_artists=(lgd,), bbox_inches='tight')
这产生:
补充:我发现了一些应该马上做的伎俩,但下面的代码还提供了一个替代scheme。
使用subplots_adjust()
函数向上移动子图的底部:
fig.subplots_adjust(bottom=0.2) # <-- Change the 0.02 to work for your plot.
然后使用图例命令的图例bbox_to_anchor
部分中的偏移量来获取您想要的图例框。 设置figsize
和使用figsize
subplots_adjust(bottom=...)
一些组合应该会为您制作一个高质量的情节。
另类:我只是改变了线路:
fig = plt.figure(1)
至:
fig = plt.figure(num=1, figsize=(13, 13), dpi=80, facecolor='w', edgecolor='k')
并改变了
lgd = ax.legend(loc=9, bbox_to_anchor=(0.5,0))
至
lgd = ax.legend(loc=9, bbox_to_anchor=(0.5,-0.02))
而且在我的屏幕(24英寸CRT显示器)上显示的很好。
这里figsize=(M,N)
将数字窗口设置为M英寸乘N英寸。 只要玩这个,直到它看起来适合你。 将其转换为更具可缩放性的图像格式,并在需要时使用GIMP进行编辑,或者在包含graphics时只使用LaTeX viewport
选项进行裁剪。
这是另一个非常手动的解决scheme。 您可以定义轴的大小,并相应地考虑填充(包括图例和标记)。 希望对某人有用处。
示例(坐标轴大小相同!):
码:
#================================================== # Plot table colmap = [(0,0,1) #blue ,(1,0,0) #red ,(0,1,0) #green ,(1,1,0) #yellow ,(1,0,1) #magenta ,(1,0.5,0.5) #pink ,(0.5,0.5,0.5) #gray ,(0.5,0,0) #brown ,(1,0.5,0) #orange ] import matplotlib.pyplot as plt import numpy as np import collections df = collections.OrderedDict() df['labels'] = ['GWP100a\n[kgCO2eq]\n\nasedf\nasdf\nadfs','human\n[pts]','ressource\n[pts]'] df['all-petroleum long name'] = [3,5,2] df['all-electric'] = [5.5, 1, 3] df['HEV'] = [3.5, 2, 1] df['PHEV'] = [3.5, 2, 1] numLabels = len(df.values()[0]) numItems = len(df)-1 posX = np.arange(numLabels)+1 width = 1.0/(numItems+1) fig = plt.figure(figsize=(2,2)) ax = fig.add_subplot(111) for iiItem in range(1,numItems+1): ax.bar(posX+(iiItem-1)*width, df.values()[iiItem], width, color=colmap[iiItem-1], label=df.keys()[iiItem]) ax.set(xticks=posX+width*(0.5*numItems), xticklabels=df['labels']) #-------------------------------------------------- # Change padding and margins, insert legend fig.tight_layout() #tight margins leg = ax.legend(loc='upper left', bbox_to_anchor=(1.02, 1), borderaxespad=0) plt.draw() #to know size of legend padLeft = ax.get_position().x0 * fig.get_size_inches()[0] padBottom = ax.get_position().y0 * fig.get_size_inches()[1] padTop = ( 1 - ax.get_position().y0 - ax.get_position().height ) * fig.get_size_inches()[1] padRight = ( 1 - ax.get_position().x0 - ax.get_position().width ) * fig.get_size_inches()[0] dpi = fig.get_dpi() padLegend = ax.get_legend().get_frame().get_width() / dpi widthAx = 3 #inches heightAx = 3 #inches widthTot = widthAx+padLeft+padRight+padLegend heightTot = heightAx+padTop+padBottom # resize ipython window (optional) posScreenX = 1366/2-10 #pixel posScreenY = 0 #pixel canvasPadding = 6 #pixel canvasBottom = 40 #pixel ipythonWindowSize = '{0}x{1}+{2}+{3}'.format(int(round(widthTot*dpi))+2*canvasPadding ,int(round(heightTot*dpi))+2*canvasPadding+canvasBottom ,posScreenX,posScreenY) fig.canvas._tkcanvas.master.geometry(ipythonWindowSize) plt.draw() #to resize ipython window. Has to be done BEFORE figure resizing! # set figure size and ax position fig.set_size_inches(widthTot,heightTot) ax.set_position([padLeft/widthTot, padBottom/heightTot, widthAx/widthTot, heightAx/heightTot]) plt.draw() plt.show() #-------------------------------------------------- #==================================================