我花了太长时间研究如何在Matplotlib中让两个子图共享相同的y轴,并在两者之间共享一个颜色条。

发生的事情是,当我在subplot1或subplot2中调用colorbar()函数时,它会自动缩放图形,以便颜色条加上图形将适合'subplot'边界框,导致两个并排的图形具有两个非常不同的大小。

为了解决这个问题,我试着创建了第三个子图,然后我把它黑了,只渲染一个颜色条。 唯一的问题是,现在两个地块的高度和宽度不均匀,我不知道如何让它看起来还好。

这是我的代码:

from __future__ import division
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import patches
from matplotlib.ticker import NullFormatter

# SIS Functions
TE = 1 # Einstein radius
g1 = lambda x,y: (TE/2) * (y**2-x**2)/((x**2+y**2)**(3/2)) 
g2 = lambda x,y: -1*TE*x*y / ((x**2+y**2)**(3/2))
kappa = lambda x,y: TE / (2*np.sqrt(x**2+y**2))

coords = np.linspace(-2,2,400)
X,Y = np.meshgrid(coords,coords)
g1out = g1(X,Y)
g2out = g2(X,Y)
kappaout = kappa(X,Y)
for i in range(len(coords)):
    for j in range(len(coords)):
        if np.sqrt(coords[i]**2+coords[j]**2) <= TE:
            g1out[i][j]=0
            g2out[i][j]=0

fig = plt.figure()
fig.subplots_adjust(wspace=0,hspace=0)

# subplot number 1
ax1 = fig.add_subplot(1,2,1,aspect='equal',xlim=[-2,2],ylim=[-2,2])
plt.title(r"$\gamma_{1}$",fontsize="18")
plt.xlabel(r"x ($\theta_{E}$)",fontsize="15")
plt.ylabel(r"y ($\theta_{E}$)",rotation='horizontal',fontsize="15")
plt.xticks([-2.0,-1.5,-1.0,-0.5,0,0.5,1.0,1.5])
plt.xticks([-2.0,-1.5,-1.0,-0.5,0,0.5,1.0,1.5])
plt.imshow(g1out,extent=(-2,2,-2,2))
plt.axhline(y=0,linewidth=2,color='k',linestyle="--")
plt.axvline(x=0,linewidth=2,color='k',linestyle="--")
e1 = patches.Ellipse((0,0),2,2,color='white')
ax1.add_patch(e1)

# subplot number 2
ax2 = fig.add_subplot(1,2,2,sharey=ax1,xlim=[-2,2],ylim=[-2,2])
plt.title(r"$\gamma_{2}$",fontsize="18")
plt.xlabel(r"x ($\theta_{E}$)",fontsize="15")
ax2.yaxis.set_major_formatter( NullFormatter() )
plt.axhline(y=0,linewidth=2,color='k',linestyle="--")
plt.axvline(x=0,linewidth=2,color='k',linestyle="--")
plt.imshow(g2out,extent=(-2,2,-2,2))
e2 = patches.Ellipse((0,0),2,2,color='white')
ax2.add_patch(e2)

# subplot for colorbar
ax3 = fig.add_subplot(1,1,1)
ax3.axis('off')
cbar = plt.colorbar(ax=ax2)

plt.show()

当前回答

matplotlib 3.4.0新增功能

共享色条现在可以使用子图实现:

新的图。子图和图。Add_subfigure allow…只属于每个子图形的本地化图形艺术家(例如,色条和标题)。

matplotlib库包括如何绘制子图的演示。

下面是一个有2个子图的最小示例,每个子图都有一个共享的色条:

fig = plt.figure(constrained_layout=True)
(subfig_l, subfig_r) = fig.subfigures(nrows=1, ncols=2)

axes_l = subfig_l.subplots(nrows=1, ncols=2, sharey=True)
for ax in axes_l:
    im = ax.imshow(np.random.random((10, 10)), vmin=0, vmax=1)

# shared colorbar for left subfigure
subfig_l.colorbar(im, ax=axes_l, location='bottom')

axes_r = subfig_r.subplots(nrows=3, ncols=1, sharex=True)
for ax in axes_r:
    mesh = ax.pcolormesh(np.random.randn(30, 30), vmin=-2.5, vmax=2.5)

# shared colorbar for right subfigure
subfig_r.colorbar(mesh, ax=axes_r)

其他回答

上面的答案很好,但大多数都使用fig.colobar()方法应用于fig对象。这个例子展示了如何使用plt.colobar()函数,直接应用到pyplot:

def shared_colorbar_example():
    fig, axs = plt.subplots(nrows=3, ncols=3)
    for ax in axs.flat:
        plt.sca(ax)
        color = np.random.random((10))
        plt.scatter(range(10), range(10), c=color, cmap='viridis', vmin=0, vmax=1)
    plt.colorbar(ax=axs.ravel().tolist(), shrink=0.6)
    plt.show()

shared_colorbar_example()

因为上面的大多数答案都演示了在2D矩阵上的使用,所以我使用了一个简单的散点图。收缩关键字是可选的,可以调整颜色条的大小。

如果没有指定vmin和vmax,该方法将自动分析所有子图,以获得颜色条上使用的最小值和最大值。当使用fig.colorbar(im)时,上述方法只扫描作为颜色条最小和最大值参数的图像。

结果:

abevieiramota使用坐标轴列表的解决方案非常有效,直到你只使用一行图像,正如评论中指出的那样。使用一个合理的长宽比来显示图像大小是有帮助的,但还远远不够完美。例如:

import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(9.75, 3))
for ax in axes.flat:
    im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)

fig.colorbar(im, ax=axes.ravel().tolist())

plt.show()

colorbar函数提供了收缩参数,这是一个颜色条轴大小的缩放因子。这确实需要一些手工试验和错误。例如:

fig.colorbar(im, ax=axes.ravel().tolist(), shrink=0.75)

共享色图和色条

这是针对更复杂的情况,其中值不只是在0和1之间;cmap需要共享,而不是仅仅使用最后一个cmap。

import numpy as np
from matplotlib.colors import Normalize
import matplotlib.pyplot as plt
import matplotlib.cm as cm
fig, axes = plt.subplots(nrows=2, ncols=2)
cmap=cm.get_cmap('viridis')
normalizer=Normalize(0,4)
im=cm.ScalarMappable(norm=normalizer)
for i,ax in enumerate(axes.flat):
    ax.imshow(i+np.random.random((10,10)),cmap=cmap,norm=normalizer)
    ax.set_title(str(i))
fig.colorbar(im, ax=axes.ravel().tolist())
plt.show()

使用make_axes甚至更简单,并且可以得到更好的结果。它还提供了自定义颜色条位置的可能性。 还要注意共享x轴和y轴的子图选项。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

fig, axes = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True)
for ax in axes.flat:
    im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)

cax,kw = mpl.colorbar.make_axes([ax for ax in axes.flat])
plt.colorbar(im, cax=cax, **kw)

plt.show()

As pointed out in other answers, the idea is usually to define an axes for the colorbar to reside in. There are various ways of doing so; one that hasn't been mentionned yet would be to directly specify the colorbar axes at subplot creation with plt.subplots(). The advantage is that the axes position does not need to be manually set and in all cases with automatic aspect the colorbar will be exactly the same height as the subplots. Even in many cases where images are used the result will be satisfying as shown below.

当使用plt.subplots()时,使用gridspec_kw参数可以使色条轴比其他轴小得多。

fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(5.5,3), 
                  gridspec_kw={"width_ratios":[1,1, 0.05]})

例子:

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

fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(5.5,3), 
                  gridspec_kw={"width_ratios":[1,1, 0.05]})
fig.subplots_adjust(wspace=0.3)
im  = ax.imshow(np.random.rand(11,8), vmin=0, vmax=1)
im2 = ax2.imshow(np.random.rand(11,8), vmin=0, vmax=1)
ax.set_ylabel("y label")

fig.colorbar(im, cax=cax)

plt.show()

如果图的方面是自动缩放的,或者图像由于它们在宽度方向上的方面而收缩(如上所示),那么这种方法工作得很好。然而,如果图像是宽的,然后是高的,结果将是如下所示,这可能是不希望看到的。

将颜色条高度固定到子图高度的解决方案是使用mpl_toolkit .axes_grid1.inset_locator。InsetPosition用于设置相对于图像子图轴的色条轴。

import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
from mpl_toolkits.axes_grid1.inset_locator import InsetPosition

fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(7,3), 
                  gridspec_kw={"width_ratios":[1,1, 0.05]})
fig.subplots_adjust(wspace=0.3)
im  = ax.imshow(np.random.rand(11,16), vmin=0, vmax=1)
im2 = ax2.imshow(np.random.rand(11,16), vmin=0, vmax=1)
ax.set_ylabel("y label")

ip = InsetPosition(ax2, [1.05,0,0.05,1]) 
cax.set_axes_locator(ip)

fig.colorbar(im, cax=cax, ax=[ax,ax2])

plt.show()