matplotlib 嵌套格网规范对齐

jaql4c8m  于 2023-03-03  发布在  其他
关注(0)|答案(1)|浏览(166)

当使用gridspecs时,我发现很难对齐不同的嵌套gridspecs,我经常将gridspecs用于大多数或所有子图都有固定纵横比的图(例如,为了显示图像)。
下面的图就是一个最小的示例,其中在嵌套子图中,两个正方形图像显示在4个较小图像旁边:

import matplotlib.pyplot as plt 
import numpy as np

n_cols = 3
fig = plt.figure(1, figsize=(6, 6 / n_cols * 1.5))
gs = fig.add_gridspec(1, n_cols)

test_img = np.ones((64, 64, 3)) * np.linspace(0.3, 1, 64)[:, None] # simple, square test image
for col in range(n_cols - 1):
    ax = fig.add_subplot(gs[col])
    ax.imshow(test_img)

gs_sub = gs[-1].subgridspec(2, 2, wspace=0.02, hspace=0.02)
for i in range(4):
    ax = fig.add_subplot(gs_sub[i])
    ax.imshow(test_img)

# use tight layout to remove excess white space
gs.tight_layout(fig, rect=[0, 0, 1, 1], pad=0.001)
gs.update(wspace=0.025, hspace=0.0)

这将生成以下图:

正如你所看到的,较小的图像在垂直方向上要比较大的图像占用更多的空间。我猜嵌套gridspec会尝试使用所有可用的空间,而不会被限制为匹配左边的两个较大的图像。另一方面,它可以很好地对齐具有灵活纵横比的图(例如线图),因为子图的纵横比会自动拉伸:

(don'不介意重叠的轴刻度,如果需要,很容易添加更多的空间)。
我也经常可以通过缩放绘图的高度甚至是调整高度/宽度比来使事情顺利进行。在上面的绘图中,可以通过删除应用于绘图高度的任意比例因子"1.5"来改善结果。然而,这不是一个好的解决方案,因为它经常需要大量的手动实验,并且很少是完美的(特别是对于更复杂的布局)。
是否有更好的方法来实现这一点?是否有一种方法来通知嵌套的gridspec所需的对齐方式?理想情况下,我希望控制嵌套的gridspec来匹配其他子图的高度,而不是耗尽所有可用的空间。
谢谢你的帮忙!

i86rm4rw

i86rm4rw1#

matplotlib图中显示图像的问题是图形通常受到过度约束。在您的示例中,您为图形设置了特定的纵横比,然后在子图之间指定了任意间距。与图相反,图像具有固定的纵横比,这会导致图形参数不一致,因此您尝试设置的某个属性最终会被违反。
解决方法是确保设置的参数一致。由于图像的纵横比是固定的,您可以选择子图之间的间距或图形的全局纵横比,并相应地进行约束。
在下文中,我将假设图形本身的纵横比并不重要,因此我们将设置子图之间的间距,并推导出figsize应满足的约束。
根据documentationwspacehspace参数表示为平均轴宽/轴高的分数。因此,对于绘制大小为w x hn_rows x x n_cols图像网格的图形,总宽度为
W = n_cols * w + (n_cols-1) * w_space * w
同样,总高度为:
H = n_rows * h + (n_rows-1) * h_space * h
然后我们可以使用比率W / H作为主图形的纵横比。
为了简单起见,我假设所有的图像都有相同的大小,我们可以推导出一个类似的任意宽/高比的公式,但思路是一样的。
为了让它在subgridspecs上工作,你只需要先对subgridspecs做这个,它会给予你它们在主图形中的大小,你可以用它来计算主图形的长宽比。
下面是对示例图稍作修改后的一个例子,我实现了一个gridspec_aspect函数,该函数计算给定任意宽/高比和w/hspace的(子)图形的实际纵横比。

import matplotlib.pyplot as plt
from matplotlib import gridspec
import numpy as np

def gridspec_aspect(n_rows, n_cols, w, h, wspace=0, hspace=0):
    if isinstance(w, int):
        Ws = n_cols * w
    elif isinstance(w, list) or isinstance(w, tuple):
        Ws = sum(w)

    if isinstance(h, int):
        Hs = n_rows * h
    elif isinstance(h, list) or isinstance(h, tuple):
        Hs = sum(h)

    w_spacing = wspace * Ws / n_cols
    h_spacing = hspace * Hs / n_rows

    return (Ws + (n_cols - 1) * w_spacing) / (Hs + (n_rows - 1) * h_spacing)

n_cols = 3
n_rows = 1
test_img = np.ones((150, 200, 3)) * np.linspace(0.3, 0.9, 200)[:, None] # simple test image

# Image aspect ratio
h,w = test_img.shape[:2]
r = w / h

# Spacing in the inner gridspec
inner_wspace = 0.05
inner_hspace = inner_wspace * r # same vertical spacing as horizontal spacing
inner_aspect = gridspec_aspect(2, 2, w, h, inner_wspace, inner_hspace)

# Spacing in the main griddpec
outer_wspace = 0.1
outer_aspect = gridspec_aspect(n_rows, n_cols, [r, r, inner_aspect], 1, outer_wspace)

fig = plt.figure(1, figsize=(10, 10 / outer_aspect))
gs = fig.add_gridspec(1, n_cols, wspace=outer_wspace, width_ratios=[r, r, inner_aspect])
inner_gs = gridspec.GridSpecFromSubplotSpec(2, 2, subplot_spec=gs[2], wspace=inner_wspace, hspace=inner_hspace)

for col in range(n_cols - 1):
    ax = fig.add_subplot(gs[col])
    ax.imshow(test_img)
    ax.axis('off')

for i in range(2):
    for j in range(2):
        ax = fig.add_subplot(inner_gs[i,j])
        ax.imshow(test_img)
        ax.axis('off')

生成下图:

相关问题