我正在尝试为科学文章制作一些数字,所以我希望我的数字具有特定的大小.我还看到默认情况下Matplotlib在图形的边框上添加了大量填充,这是我不需要的(因为无论如何数字都将在白色背景上).
要设置一个特定的图形大小,我只需使用plt.figure(figsize = [w, h]),并添加参数tight_layout = {'pad': 0}以删除填充.这非常有效,如果我添加标题,y/x标签等,它甚至可以工作.示例:
fig = plt.figure(
figsize = [3,2],
tight_layout = {'pad': 0}
)
ax = fig.add_subplot(111)
plt.title('title')
ax.set_ylabel('y label')
ax.set_xlabel('x label')
plt.savefig('figure01.pdf')
Run Code Online (Sandbox Code Playgroud)
这将创建一个精确大小为3x2(英寸)的pdf文件.
我遇到的问题是,当我在轴外添加一个文本框(通常是一个图例框)时,Matplotlib不会像添加标题/轴标签时那样为文本框腾出空间.通常,文本框被截断,或者根本不显示在已保存的图中.例:
plt.close('all')
fig = plt.figure(
figsize = [3,2],
tight_layout = {'pad': 0}
)
ax = fig.add_subplot(111)
plt.title('title')
ax.set_ylabel('y label')
ax.set_xlabel('x label')
t = ax.text(0.7, 1.1, 'my text here', bbox = dict(boxstyle = 'round'))
plt.savefig('figure02.pdf')
Run Code Online (Sandbox Code Playgroud)
我在其他地方找到的解决方案是将参数添加bbox_inches = 'tight'到savefig命令.文本框现在包含在我想要的内容中,但pdf现在的大小错误.似乎Matplotlib只是使图形更大,而不是像添加标题和x/y标签时那样减小轴的大小.
例:
plt.close('all')
fig = plt.figure(
figsize = [3,2],
tight_layout = {'pad': 0}
)
ax = fig.add_subplot(111)
plt.title('title')
ax.set_ylabel('y label')
ax.set_xlabel('x label')
t = ax.text(0.7, 1.1, 'my text here', bbox = dict(boxstyle = 'round'))
plt.savefig('figure03.pdf', bbox_inches = 'tight')
Run Code Online (Sandbox Code Playgroud)
(这个数字是3.307x2.248)
有没有任何解决方案可以覆盖大多数情况下只有轴外的传奇?
Imp*_*est 14
所以要求是:
所以tight_layout用pad = 0,解决了1和4,但矛盾2.
人们可以考虑设定pad更大的价值.这将解决2.然而,由于它在所有方向上都是对称的,因此它会与4相矛盾.
使用bbox_inches = 'tight'更改图形大小.矛盾1.
所以我认为这个问题没有通用的解决方案.
我能想到的是:它以图形坐标设置文本,然后在水平或垂直方向上调整轴的大小,使轴和文本之间没有重叠.
import matplotlib.pyplot as plt
import matplotlib.transforms
fig = plt.figure(figsize = [3,2])
ax = fig.add_subplot(111)
plt.title('title')
ax.set_ylabel('y label')
ax.set_xlabel('x label')
def text_legend(ax, x0, y0, text, direction = "v", padpoints = 3, margin=1.,**kwargs):
ha = kwargs.pop("ha", "right")
va = kwargs.pop("va", "top")
t = ax.figure.text(x0, y0, text, ha=ha, va=va, **kwargs)
otrans = ax.figure.transFigure
plt.tight_layout(pad=0)
ax.figure.canvas.draw()
plt.tight_layout(pad=0)
offs = t._bbox_patch.get_boxstyle().pad * t.get_size() + margin # adding 1pt
trans = otrans + \
matplotlib.transforms.ScaledTranslation(-offs/72.,-offs/72.,fig.dpi_scale_trans)
t.set_transform(trans)
ax.figure.canvas.draw()
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 = trans2.transform(t._bbox_patch.get_window_extent())
bbox = ax.get_position()
if direction=="v":
ax.set_position([bbox.x0, bbox.y0,bbox.width, tbox[0][1]-bbox.y0])
else:
ax.set_position([bbox.x0, bbox.y0,tbox[0][0]-bbox.x0, bbox.height])
# case 1: place text label at top right corner of figure (1,1). Adjust axes height.
#text_legend(ax, 1,1, 'my text here', bbox = dict(boxstyle = 'round'), )
# case 2: place text left of axes, (1, y), direction=="v"
text_legend(ax, 1., 0.8, 'my text here', margin=2., direction="h", bbox = dict(boxstyle = 'round') )
plt.savefig(__file__+'.pdf')
plt.show()
Run Code Online (Sandbox Code Playgroud)
bbox_to_anchor参数而不需要控制图例周围的花式框.
import matplotlib.pyplot as plt
import matplotlib.transforms
fig = plt.figure(figsize = [3.5,2])
ax = fig.add_subplot(111)
ax.set_title('title')
ax.set_ylabel('y label')
ax.set_xlabel('x label')
ax.plot([1,2,3], marker="o", label="quantity 1")
ax.plot([2,1.7,1.2], marker="s", label="quantity 2")
def legend(ax, x0=1,y0=1, direction = "v", padpoints = 3,**kwargs):
otrans = ax.figure.transFigure
t = ax.legend(bbox_to_anchor=(x0,y0), loc=1, bbox_transform=otrans,**kwargs)
plt.tight_layout(pad=0)
ax.figure.canvas.draw()
plt.tight_layout(pad=0)
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])
# case 1: place text label at top right corner of figure (1,1). Adjust axes height.
#legend(ax, borderaxespad=0)
# case 2: place text left of axes, (1, y), direction=="h"
legend(ax,y0=0.8, direction="h", borderaxespad=0.2)
plt.savefig(__file__+'.pdf')
plt.show()
Run Code Online (Sandbox Code Playgroud)
72?这72是每英寸点数(ppi).这是一个固定的印刷单位,例如,字体大小总是以点(如12pt)给出.因为matplotlib以相对于fontsize的单位(即点)定义文本框的填充,我们需要使用72转换回英寸(然后显示坐标).此处未触及默认的每英寸点数(dpi),但会计入fig.dpi_scale_trans.如果你想改变dpi的,你需要确保在创建人物的时候,以及在保存时(用作数字DPI设置dpi=..在调用plt.figure()以及plt.savefig()).
小智 6
从 开始matplotlib==3.1.3,您可以使用它constrained_layout=True来达到所需的结果。目前这是实验性的,但请参阅文档以获取非常有用的指南(以及专门关于图例的部分)。请注意,图例会占用情节的空间,但这是不可避免的。我发现,只要图例相对于图的大小不占用太多空间,那么该图就会被保存而不会裁剪任何内容。
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(3, 2), constrained_layout=True)
ax.set_title('title')
ax.set_ylabel('y label')
ax.set_xlabel('x label')
ax.plot([0,1], [0,1], label='my text here')
ax.legend(loc='center left', bbox_to_anchor=(1.1, 0.5))
fig.savefig('figure03.pdf')
Run Code Online (Sandbox Code Playgroud)
