我试图绘制几个数据,在某些情况下,这些数据占据了整个情节。
版本2的默认选项应该是“最佳”,它会尝试找到将图例放置在图中的最佳位置。 有没有办法扩展选项,以便在空间不足的情况下将图例放在图表之外?
否则,matplotlib是否有一个选项(不占用所有系列的最大值并添加手动填充)以自动添加ylim填充并为图例提供空间并放置在绘图内?
主要思想是避免手动调整绘图,自动创建多个绘图。
简单的MWE如下:
%matplotlib inline %config InlineBackend.figure_format = 'svg' import scipy as sc import matplotlib.pyplot as plt plt.close('all') x = sc.linspace(0, 1, 50) y = sc.array([sc.ones(50)*0.5, x, x**2, (1-x), (1-x**2)]).T fig = plt.figure('Fig') ax = fig.add_subplot(111) lines = ax.plot(x, y) leg = ax.legend([lines[0], lines[1], lines[2], lines[3], lines[4]], [r'$\mathrm{line} = 0.5$', r'$\mathrm{line} = x$', r'$\mathrm{line} = x^2$', r'$\mathrm{line} = 1-x$',r'$\mathrm{line} = 1-x^2$'], ncol=2) fig.tight_layout()
I am trying to plot several data which, in some cases, occupies the entire plot.
The default option, from version 2, should be 'best', which tries to find the best position to place the legend inside the plot. Is there a way to extend the option to be able to place the legend outside the plot if the space is insufficient?
Otherwise, is there an option for matplotlib (without taking the max of all the series and add a manual padding) to automatically add an ylim padding and give space to the legend and be placed inside the plot?
The main idea is to avoid manual tweaking of the plots, having several plots to be created automatically.
A simple MWE is in the following:
%matplotlib inline %config InlineBackend.figure_format = 'svg' import scipy as sc import matplotlib.pyplot as plt plt.close('all') x = sc.linspace(0, 1, 50) y = sc.array([sc.ones(50)*0.5, x, x**2, (1-x), (1-x**2)]).T fig = plt.figure('Fig') ax = fig.add_subplot(111) lines = ax.plot(x, y) leg = ax.legend([lines[0], lines[1], lines[2], lines[3], lines[4]], [r'$\mathrm{line} = 0.5$', r'$\mathrm{line} = x$', r'$\mathrm{line} = x^2$', r'$\mathrm{line} = 1-x$',r'$\mathrm{line} = 1-x^2$'], ncol=2) fig.tight_layout()最满意答案
没有自动方式将图例放置在轴外的“最佳”位置。
在情节内
您可能决定始终在轴内留出足够的空间 ,以使图例不会与任何内容重叠。 为此你可以使用ax.margins 。 例如
ax.margins(y=0.25)将在y轴的两端产生25%的余量,如果它有3列,则有足够的空间来托管图例。
然后,您可以决定始终使用相同的位置,例如loc="upper center"以便在所有图中获得一致的结果。 这样做的缺点在于它取决于图形尺寸,并且它在轴的另一端也增加了(可能不希望的)边缘。 如果您可以使用该保证金,则自动确定所需保证金的方法如下:
import numpy as np import matplotlib.pyplot as plt import matplotlib.transforms x = np.linspace(0, 1, 50) y = np.array([np.ones(50)*0.5, x, x**2, (1-x), (1-x**2)]).T fig = plt.figure('Fig') ax = fig.add_subplot(111) lines = ax.plot(x, y) def legend_adjust(legend, ax=None ): if ax == None: ax =plt.gca() ax.figure.canvas.draw() bbox = legend.get_window_extent().transformed(ax.transAxes.inverted() ) print bbox.height ax.margins(y = 2.*bbox.height) leg = plt.legend(handles=[lines[0], lines[1], lines[2], lines[3], lines[4]], labels= [r'$\mathrm{line} = 0.5$', r'$\mathrm{line} = x$', r'$\mathrm{line} = x^2$', r'$\mathrm{line} = 1-x$',r'$\mathrm{line} = 1-x^2$'], loc="upper center", ncol=2) legend_adjust(leg) plt.show()如果设置限制很好,您也可以自己调整限制:
import numpy as np import matplotlib.pyplot as plt import matplotlib.transforms x = np.linspace(0, 1, 50) y = np.array([np.ones(50)*0.5, x, x**2, (1-x), (1-x**2)]).T fig = plt.figure('Fig') ax = fig.add_subplot(111) lines = ax.plot(x, y) def legend_adjust(legend, ax=None, pad=0.05 ): if ax == None: ax =plt.gca() ax.figure.canvas.draw() bbox = legend.get_window_extent().transformed(ax.transAxes.inverted() ) ymin, ymax = ax.get_ylim() ax.set_ylim(ymin, ymax+(ymax-ymin)*(1.+pad-bbox.y0)) leg = plt.legend(handles=[lines[0], lines[1], lines[2], lines[3], lines[4]], labels= [r'$\mathrm{line} = 0.5$', r'$\mathrm{line} = x$', r'$\mathrm{line} = x^2$', r'$\mathrm{line} = 1-x$',r'$\mathrm{line} = 1-x^2$'], loc="upper center", ncol=2) legend_adjust(leg) plt.show()
走出阴谋
否则,您可能决定始终将图例放在图表之外 。 在这个答案中收集了一些技术。
特别感兴趣的可能是将图例放在图形外部而不更改图形,如本问题中所详述: 创建具有精确尺寸且无填充的图形(以及轴外的图例)
使其适应这种情况看起来像:
import numpy as np import matplotlib.pyplot as plt import matplotlib.transforms x = np.linspace(0, 1, 50) y = np.array([np.ones(50)*0.5, x, x**2, (1-x), (1-x**2)]).T fig = plt.figure('Fig') ax = fig.add_subplot(111) lines = ax.plot(x, y) def legend(ax=None, x0=1,y0=1, direction = "v", padpoints = 3,**kwargs): if ax == None: ax =plt.gca() otrans = ax.figure.transFigure t = ax.legend(bbox_to_anchor=(x0,y0), loc=1, bbox_transform=otrans,**kwargs) plt.tight_layout() ax.figure.canvas.draw() plt.tight_layout() ppar = [0,-padpoints/72.] if direction == "v" else [-padpoints/72.,0] trans2=matplotlib.transforms.ScaledTranslation(ppar[0],ppar[1],fig.dpi_scale_trans)+\ ax.figure.transFigure.inverted() tbox = t.get_window_extent().transformed(trans2 ) bbox = ax.get_position() if direction=="v": ax.set_position([bbox.x0, bbox.y0,bbox.width, tbox.y0-bbox.y0]) else: ax.set_position([bbox.x0, bbox.y0,tbox.x0-bbox.x0, bbox.height]) legend(handles=[lines[0], lines[1], lines[2], lines[3], lines[4]], labels= [r'$\mathrm{line} = 0.5$', r'$\mathrm{line} = x$', r'$\mathrm{line} = x^2$', r'$\mathrm{line} = 1-x$',r'$\mathrm{line} = 1-x^2$'], y0=0.8, direction="h", borderaxespad=0.2) plt.show()
There is no automatic way to place the legend at "the best" position outside the axes.
inside the plot
You may decide to always leave enough space inside the axes, such that the legend doesn't overlap with anything. To this end you can use ax.margins. e.g.
ax.margins(y=0.25)will produce 25% margin on both ends of the y axis, enough space to host the legend if it has 3 columns.
You may then decide to always use the same location, e.g. loc="upper center" for a consistent result among all plots. The drawback of this is that it depends on figure size and that it adds a (potentially undesired) margin at the other end of the axis as well. If you can live with that margin, a way to automatically determine the needed margin would be the following:
import numpy as np import matplotlib.pyplot as plt import matplotlib.transforms x = np.linspace(0, 1, 50) y = np.array([np.ones(50)*0.5, x, x**2, (1-x), (1-x**2)]).T fig = plt.figure('Fig') ax = fig.add_subplot(111) lines = ax.plot(x, y) def legend_adjust(legend, ax=None ): if ax == None: ax =plt.gca() ax.figure.canvas.draw() bbox = legend.get_window_extent().transformed(ax.transAxes.inverted() ) print bbox.height ax.margins(y = 2.*bbox.height) leg = plt.legend(handles=[lines[0], lines[1], lines[2], lines[3], lines[4]], labels= [r'$\mathrm{line} = 0.5$', r'$\mathrm{line} = x$', r'$\mathrm{line} = x^2$', r'$\mathrm{line} = 1-x$',r'$\mathrm{line} = 1-x^2$'], loc="upper center", ncol=2) legend_adjust(leg) plt.show()If setting the limits is fine with you, you may also adapt the limits themselves:
import numpy as np import matplotlib.pyplot as plt import matplotlib.transforms x = np.linspace(0, 1, 50) y = np.array([np.ones(50)*0.5, x, x**2, (1-x), (1-x**2)]).T fig = plt.figure('Fig') ax = fig.add_subplot(111) lines = ax.plot(x, y) def legend_adjust(legend, ax=None, pad=0.05 ): if ax == None: ax =plt.gca() ax.figure.canvas.draw() bbox = legend.get_window_extent().transformed(ax.transAxes.inverted() ) ymin, ymax = ax.get_ylim() ax.set_ylim(ymin, ymax+(ymax-ymin)*(1.+pad-bbox.y0)) leg = plt.legend(handles=[lines[0], lines[1], lines[2], lines[3], lines[4]], labels= [r'$\mathrm{line} = 0.5$', r'$\mathrm{line} = x$', r'$\mathrm{line} = x^2$', r'$\mathrm{line} = 1-x$',r'$\mathrm{line} = 1-x^2$'], loc="upper center", ncol=2) legend_adjust(leg) plt.show()out of the plot
Otherwise you may decide to always put the legend out of the plot. Some techniques are collected in this answer.
Of special interest may be to place the legend outside the figure without changing the figuresize, as detailed in this question: Creating figure with exact size and no padding (and legend outside the axes)
Adapting it to this case would look like:
import numpy as np import matplotlib.pyplot as plt import matplotlib.transforms x = np.linspace(0, 1, 50) y = np.array([np.ones(50)*0.5, x, x**2, (1-x), (1-x**2)]).T fig = plt.figure('Fig') ax = fig.add_subplot(111) lines = ax.plot(x, y) def legend(ax=None, x0=1,y0=1, direction = "v", padpoints = 3,**kwargs): if ax == None: ax =plt.gca() otrans = ax.figure.transFigure t = ax.legend(bbox_to_anchor=(x0,y0), loc=1, bbox_transform=otrans,**kwargs) plt.tight_layout() ax.figure.canvas.draw() plt.tight_layout() ppar = [0,-padpoints/72.] if direction == "v" else [-padpoints/72.,0] trans2=matplotlib.transforms.ScaledTranslation(ppar[0],ppar[1],fig.dpi_scale_trans)+\ ax.figure.transFigure.inverted() tbox = t.get_window_extent().transformed(trans2 ) bbox = ax.get_position() if direction=="v": ax.set_position([bbox.x0, bbox.y0,bbox.width, tbox.y0-bbox.y0]) else: ax.set_position([bbox.x0, bbox.y0,tbox.x0-bbox.x0, bbox.height]) legend(handles=[lines[0], lines[1], lines[2], lines[3], lines[4]], labels= [r'$\mathrm{line} = 0.5$', r'$\mathrm{line} = x$', r'$\mathrm{line} = x^2$', r'$\mathrm{line} = 1-x$',r'$\mathrm{line} = 1-x^2$'], y0=0.8, direction="h", borderaxespad=0.2) plt.show()更多推荐
发布评论