简体   繁体   English

精确控制matplotlib中子图的位置

[英]Precise control over subplot locations in matplotlib

I am currently producing a figure for a paper, which looks like this: 我目前正在为纸制作图形,如下所示:

在此处输入图片说明

The above is pretty close to how I want it to look, but I have a strong feeling that I'm not doing this the "right way", since it was really fiddly to produce, and my code is full of all sorts of magic numbers where I fine-tuned the positioning by hand. 上面的代码非常接近我想要的样子,但是我有一种强烈的感觉,我没有以“正确的方式”执行此操作,因为它确实很容易产生,并且我的代码充满了各种魔术我手动调整位置的数字。 Thus my question is, what is the right way to produce a plot like this? 因此,我的问题是,制作这样的情节的正确方法是什么?

Here are the important features of this plot that made it hard to produce: 这是该绘图难以制作的重要特征:

  • The aspect ratios of the three subplots are fixed by the data, but the images are not all at the same resolution. 这三个子图的长宽比由数据固定,但是图像的分辨率并非全部相同。

  • I wanted all three plots to take up the full height of the figure 我希望所有三个情节都占据图的全部高度

  • I wanted (a) and (b) to be close together since they share their y axis, while (c) is further away 我希望(a)和(b)靠近,因为它们共享y轴,而(c)距离较远

  • Ideally, I would like the top of the top colour bar to exactly match the top of the three images, and similarly with the bottom of the lower colour bar. 理想情况下,我希望顶部颜色条的顶部与三个图像的顶部完全匹配,并且与底部颜色条的底部完全匹配。 (In fact they aren't quite aligned, because I did this by guessing numbers and re-compiling the image.) (实际上它们并没有完全对齐,因为我通过猜测数字并重新编译图像来做到这一点。)

In producing this figure, I first tried using GridSpec , but I wasn't able to control the relative spacing between the three main subplots. 在制作此图时,我首先尝试使用GridSpec ,但是我无法控制三个主要子图之间的相对间距。 I then tried ImageGrid, which is part of the AxisGrid toolkit , but the differing resolutions between the three images caused that to behave strangely. 然后,我尝试了AxisGrid工具包中的ImageGrid ,但是三张图像之间的分辨率不同导致其表现异常。 Delving deeper into AxesGrid, I was able to position the three main subplots using the append_axes function, but I still had to position the three colourbars by hand. 深入研究AxesGrid,我能够使用append_axes函数定位三个主要的子图,但是我仍然必须手动定位三个色标。 (I created the colourbars manually.) (我手动创建了颜色条。)

I'd rather not post my existing code, because it's a horrible collection of hacks and magic numbers. 我不想发布我现有的代码,因为这是骇客和魔术数字的可怕集合。 Rather my question is, is there any way in MatPlotLib to just specify the logical layout of the figure (ie the content of the bullet points above) and have the layout calculated for me automatically? 而是我的问题是,在MatPlotLib中,有什么方法可以仅指定图形的逻辑布局(即,上面项目符号的内容),并自动为我计算布局吗?

Here is a possible solution. 这是一个可能的解决方案。 You'd start with the figure width (which makes sense when preparing a paper) and calculate your way through, using the aspects of the figures, some arbitrary spacings between the subplots and the margins. 您将从图形宽度开始(这在准备纸张时很有意义),然后使用图形的各个方面,子图和边距之间的任意间距来计算遍历。 The formulas are similar to the ones I used in this answer . 这些公式与我在此答案中使用的公式相似。 And the unequal aspects are taken care of by GridSpec 's width_ratios argument. GridSpecwidth_ratios参数可以解决不平等的方面。 You then end up with a figure height such that the subplots' are equal in height. 然后,您得到一个图形高度,以使子图的高度相等。

So you cannot avoid typing in some numbers, but they are not "magic". 因此,您无法避免输入一些数字,但是它们不是“魔术”。 All are related to acessible quatities like fraction of figure size or fraction of mean subplots size. 所有这些都与必要的数量有关,例如图形大小的分数或平均子图大小的分数。 Since the system is closed, changing any number will simply produce a different figure height, but will not destroy the layout. 由于系统是封闭的,因此更改任何数字都只会产生不同的图形高度,但不会破坏布局。

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np; np.random.seed(42)

imgs = []
shapes = [(550,200), ( 550,205), (1100,274) ]
for shape in shapes:
    imgs.append(np.random.random(shape))

# calculate inverse aspect(width/height) for all images
inva = np.array([ img.shape[1]/float(img.shape[0]) for img in imgs])
# set width of empty column used to stretch layout
emptycol = 0.02
r = np.array([inva[0],inva[1], emptycol, inva[2], 3*emptycol, emptycol])
# set a figure width in inch
figw = 8
# border, can be set independently of all other quantities
left = 0.1; right=1-left
bottom=0.1; top=1-bottom
# wspace (=average relative space between subplots)
wspace = 0.1
#calculate scale
s = figw*(right-left)/(len(r)+(len(r)-1)*wspace) 
# mean aspect
masp = len(r)/np.sum(r)
#calculate figheight
figh = s*masp/float(top-bottom)


gs = gridspec.GridSpec(3,len(r), width_ratios=r)

fig = plt.figure(figsize=(figw,figh))
plt.subplots_adjust(left, bottom, right, top, wspace)

ax1 = plt.subplot(gs[:,0])
ax2 = plt.subplot(gs[:,1])
ax2.set_yticks([])

ax3 = plt.subplot(gs[:,3])
ax3.yaxis.tick_right()
ax3.yaxis.set_label_position("right")

cax1 = plt.subplot(gs[0,5])
cax2 = plt.subplot(gs[1,5])
cax3 = plt.subplot(gs[2,5])


im1 = ax1.imshow(imgs[0], cmap="viridis")
im2 = ax2.imshow(imgs[1], cmap="plasma")
im3 = ax3.imshow(imgs[2], cmap="RdBu")

fig.colorbar(im1, ax=ax1, cax=cax1)
fig.colorbar(im2, ax=ax2, cax=cax2)
fig.colorbar(im3, ax=ax3, cax=cax3)

ax1.set_title("image title")
ax1.set_xlabel("xlabel")
ax1.set_ylabel("ylabel")

plt.show()

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM