Matplotlib 2个子图,1个颜色条


235

我花了太多的时间研究如何在Matplotlib中使用两个颜色共享的单个颜色条来使两个子图共享相同的y轴。

发生的是,当我colorbar()subplot1或中调用函数时subplot2,它将自动缩放绘图,以使颜色栏和绘图可以放入“子图”边界框内,从而导致两个并排的绘图有两个不同大小。

为了解决这个问题,我尝试创建了第三个子图,然后黑客入侵了它,仅用一个颜色条就不渲染任何图。唯一的问题是,现在两个图的高度和宽度是不均匀的,我不知道如何使它看起来还不错。

这是我的代码:

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

Answers:


319

只需将颜色条放置在其自身的轴上并用于为其留subplots_adjust出空间。

作为一个简单的例子:

import numpy as np
import matplotlib.pyplot as plt

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

fig.subplots_adjust(right=0.8)
cbar_ax = fig.add_axes([0.85, 0.15, 0.05, 0.7])
fig.colorbar(im, cax=cbar_ax)

plt.show()

在此处输入图片说明

请注意,im即使值范围由vmin和设置,颜色范围也将由最后绘制的图像(产生)设置vmax。例如,如果另一个图具有更高的最大值,则具有比max的最大值更高的值的点im将以统一的颜色显示。


4
ImageGrid对于此确切目的也非常有用。
菲利普·

5
如果需要使用tight_layout(),则需要在tight_layout之后执行subplots_adjust之后的所有操作,然后手动调整subplots_adjust和add_axes的坐标。
user1748155 2013年

2
如何为已有的两个不同散点图使用单个颜色条?我在上面尝试过,但是我不知道如何用适当的变量替换“ im”。假设我的散点图是plot1 = pylib.scatter(x,y,z)和plot2 = pylib.scatter(a,b,c)
Rotail 2014年

46
这对于其他人可能已经很明显了,但是我想指出,为了使色标确实在所有图中均能正确表示颜色,所以vminvmax参数至关重要。它们控制每个子图的颜色范围。如果您有真实数据,则可能需要经过此过程才能首先找到最小值和最大值。
James Owers 2015年

2
如果绘图值的范围不同,则颜色栏范围将仅显示最后一个绘图的范围,对吗?有什么建议?
卢卡斯

132

您可以使用带有轴列表的ax参数来简化Joe Kington的代码figure.colorbar()。从文档中

斧头

无| 父轴对象,新色条轴的空间将从中被窃取。如果给出了轴列表,则将全部调整它们的大小,以便为色条轴腾出空间。

import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=2, ncols=2)
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()

1个


4
此解决方案在这里效果很好,并且似乎是最简单的解决方案。
Kknd

8
如果将nrows更改为1,则两个图都比colorbar亮。那么,如何解决这个问题呢?
2016年

6
可惜它不能与tight_layout一起使用,但是仍然是一个好的解决方案。
2016年

1
只是要记住...我喜欢这个解决方案!Tinha que ser cearense!
iury simoes-sousa,

1
这个答案的关键部分是fig.colorbar(im, ax=axes.ravel().tolist())。如果省略ax=axes.ravel().tolist(),则颜色栏将放置在一个子图中。
nyanpasu64

55

此解决方案不需要手动调整轴位置或颜色栏大小,适用于多行单行布局,并且适用于tight_layout()。它是从一个适应画廊例如,使用ImageGrid从matplotlib的AxesGrid工具箱

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid

# Set up figure and image grid
fig = plt.figure(figsize=(9.75, 3))

grid = ImageGrid(fig, 111,          # as in plt.subplot(111)
                 nrows_ncols=(1,3),
                 axes_pad=0.15,
                 share_all=True,
                 cbar_location="right",
                 cbar_mode="single",
                 cbar_size="7%",
                 cbar_pad=0.15,
                 )

# Add data to image grid
for ax in grid:
    im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)

# Colorbar
ax.cax.colorbar(im)
ax.cax.toggle_label(True)

#plt.tight_layout()    # Works, but may still require rect paramater to keep colorbar labels visible
plt.show()

图像网格


双+1,这是一个很棒的方法
Brett

确实可以使用tight_layout,但是我不知道如何向该颜色条添加标签。它不接受kws标签,标题,文本……任何东西!而且文档没有太大帮助。
TomCho

3
@TomCho要设置标签,您可以在实例化颜色条时使用它的手柄,如下所示:thecb = ax.cax.colorbar(im)。然后,您可以thecb.set_label_text("foo")
2002年

1
如何更改颜色图?
Sigur

1
@Sigur我确定您现在已经弄清楚了,但是对于其他人,可以在声明im时更改cmap:im = ax.imshow(data,vmin = 0,vmax = 1,cmap ='your_cmap_here')
Shaun Lowis

38

使用起来make_axes更容易,并且效果更好。它还提供了自定义颜色条位置的可能性。还请注意subplots共享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()


7
当子图不是正方形时,此方法不起作用。如果更改nrows=1,则颜色栏将再次变得大于子图。
韦斯利·坦西

您的matplotlib默认设置是什么?看起来不错!
rafaelvalle

18

作为偶然接触此线程的初学者,我想添加一个针对abevieiramota的非常简洁答案的python-for- dummies改编(因为我处于必须查找'ravel'的水平才能弄清楚什么)他们的代码正在执行):

import numpy as np
import matplotlib.pyplot as plt

fig, ((ax1,ax2,ax3),(ax4,ax5,ax6)) = plt.subplots(2,3)

axlist = [ax1,ax2,ax3,ax4,ax5,ax6]

first = ax1.imshow(np.random.random((10,10)), vmin=0, vmax=1)
third = ax3.imshow(np.random.random((12,12)), vmin=0, vmax=1)

fig.colorbar(first, ax=axlist)

plt.show()

更少的pythonic,对于像我这样的菜鸟来说更容易看到这里的实际情况。


17

正如在其他答案中指出的那样,通常是为色条定义一个驻留的轴。尚未提及的一种方法是使用创建子图时直接指定颜色条轴plt.subplots()。优点是不需要手动设置轴位置,并且在所有情况下都具有自动外观,颜色栏将与子图高度完全相同。甚至在许多使用图像的情况下,结果也会令人满意,如下所示。

当使用时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_toolkits.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()

在此处输入图片说明


我不确定是否可以在这里问这个问题,但是有没有办法ax = fig.add_subplot()代替使用该解决方案呢?我问是因为我不知道如何在底图中使用它。
lanadaquenada

1
@lanadaquenada是的,这是可能的,但你需要提供GridSpecadd_subplot()在这种情况下。
重要性

10

如注释中指出的那样,使用abevieiramota使用轴列表的解决方案非常有效,直到仅使用一行图像为止。使用合理的宽高比来提供figsize帮助,但还远远不够完美。例如:

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

1 x 3图像阵列

彩条的功能提供了shrink这对于颜色条轴的尺寸的比例因子的参数。它确实需要一些手动试验和错误。例如:

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

1 x 3带有缩小色条的图像阵列


4

要添加到@abevieiramota的出色答案中,您可以将constrained_layout与tight_layout等效。如果您使用imshow而不是,则仍然会产生较大的水平间隙,这是pcolormesh因为施加的1:1长宽比imshow

import numpy as np
import matplotlib.pyplot as plt

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

fig.colorbar(im, ax=axes.flat)
plt.show()

在此处输入图片说明


1

我注意到几乎所有发布的解决方案都涉及,ax.imshow(im, ...)并且没有规范显示在多个子图的颜色栏上的颜色。该im可映射从最后一个实例采取,但如果多个值im-s有什么不同?(我假设这些可映射对象的处理方式与轮廓集和表面集的处理方式相同。)我有一个示例,使用下面的3d表面图为2x2子图创建两个颜色条(每行一个颜色条) )。尽管该问题明确要求采用其他安排,但我认为该示例有助于阐明某些内容。plt.subplots(...)不幸的是,由于3D轴,我还没有找到使用此方法的方法。

样例图

如果我能以更好的方式放置颜色条...(可能有更好的方法,但是至少应该不太难遵循。)

import matplotlib
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

cmap = 'plasma'
ncontours = 5

def get_data(row, col):
    """ get X, Y, Z, and plot number of subplot
        Z > 0 for top row, Z < 0 for bottom row """
    if row == 0:
        x = np.linspace(1, 10, 10, dtype=int)
        X, Y = np.meshgrid(x, x)
        Z = np.sqrt(X**2 + Y**2)
        if col == 0:
            pnum = 1
        else:
            pnum = 2
    elif row == 1:
        x = np.linspace(1, 10, 10, dtype=int)
        X, Y = np.meshgrid(x, x)
        Z = -np.sqrt(X**2 + Y**2)
        if col == 0:
            pnum = 3
        else:
            pnum = 4
    print("\nPNUM: {}, Zmin = {}, Zmax = {}\n".format(pnum, np.min(Z), np.max(Z)))
    return X, Y, Z, pnum

fig = plt.figure()
nrows, ncols = 2, 2
zz = []
axes = []
for row in range(nrows):
    for col in range(ncols):
        X, Y, Z, pnum = get_data(row, col)
        ax = fig.add_subplot(nrows, ncols, pnum, projection='3d')
        ax.set_title('row = {}, col = {}'.format(row, col))
        fhandle = ax.plot_surface(X, Y, Z, cmap=cmap)
        zz.append(Z)
        axes.append(ax)

## get full range of Z data as flat list for top and bottom rows
zz_top = zz[0].reshape(-1).tolist() + zz[1].reshape(-1).tolist()
zz_btm = zz[2].reshape(-1).tolist() + zz[3].reshape(-1).tolist()
## get top and bottom axes
ax_top = [axes[0], axes[1]]
ax_btm = [axes[2], axes[3]]
## normalize colors to minimum and maximum values of dataset
norm_top = matplotlib.colors.Normalize(vmin=min(zz_top), vmax=max(zz_top))
norm_btm = matplotlib.colors.Normalize(vmin=min(zz_btm), vmax=max(zz_btm))
cmap = cm.get_cmap(cmap, ncontours) # number of colors on colorbar
mtop = cm.ScalarMappable(cmap=cmap, norm=norm_top)
mbtm = cm.ScalarMappable(cmap=cmap, norm=norm_btm)
for m in (mtop, mbtm):
    m.set_array([])

# ## create cax to draw colorbar in
# cax_top = fig.add_axes([0.9, 0.55, 0.05, 0.4])
# cax_btm = fig.add_axes([0.9, 0.05, 0.05, 0.4])
cbar_top = fig.colorbar(mtop, ax=ax_top, orientation='vertical', shrink=0.75, pad=0.2) #, cax=cax_top)
cbar_top.set_ticks(np.linspace(min(zz_top), max(zz_top), ncontours))
cbar_btm = fig.colorbar(mbtm, ax=ax_btm, orientation='vertical', shrink=0.75, pad=0.2) #, cax=cax_btm)
cbar_btm.set_ticks(np.linspace(min(zz_btm), max(zz_btm), ncontours))

plt.show()
plt.close(fig)
## orientation of colorbar = 'horizontal' if done by column

如果从多个值ims为不同的,他们应该不会使用相同的彩条,使原来的问题就不会真正适用
升速

0

这个主题涵盖了很多,但是我仍然想以稍微不同的哲学提出另一种方法。

设置起来有点复杂,但是(我认为)它允许更多的灵活性。例如,一个人可以使用每个子图/颜色条的比例:

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

# Define number of rows and columns you want in your figure
nrow = 2
ncol = 3

# Make a new figure
fig = plt.figure(constrained_layout=True)

# Design your figure properties
widths = [3,4,5,1]
gs = GridSpec(nrow, ncol + 1, figure=fig, width_ratios=widths)

# Fill your figure with desired plots
axes = []
for i in range(nrow):
    for j in range(ncol):
        axes.append(fig.add_subplot(gs[i, j]))
        im = axes[-1].pcolormesh(np.random.random((10,10)))

# Shared colorbar    
axes.append(fig.add_subplot(gs[:, ncol]))
fig.colorbar(im, cax=axes[-1])

plt.show()

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.