Matplotlib也在情节之外扩展了最佳定位(Matplotlib extend best positioning also outside the plot)

编程入门 行业动态 更新时间:2024-10-27 21:12:05
Matplotlib也在情节之外扩展了最佳定位(Matplotlib extend best positioning also outside the plot)

我试图绘制几个数据,在某些情况下,这些数据占据了整个情节。

版本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.

enter image description here

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

enter image description here

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

enter image description here

更多推荐

本文发布于:2023-04-27 20:11:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1328620.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:也在   情节   Matplotlib   plot   positioning

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!