Matplotlib 2サブプロット、1カラーバー


235

Matplotlibの2つのサブプロットが同じy軸を共有し、2つのサブプロットが2つのサブプロット間で共有されるようにする方法を調査するのに、あまりにも長い時間を費やしてきました。

起こっていたのは、またはのcolorbar()いずれsubplot1subplot2で関数を呼び出したときに、カラーバーとプロットが「サブプロット」の境界ボックス内に収まるようにプロットを自動スケーリングし、2つの横並びのプロットが2つの非常に異なるものになることでした。サイズ。

これを回避するために、3番目のサブプロットを作成しようとしましたが、これをハッキングして、カラーバーだけが存在するプロットをレンダリングしませんでした。唯一の問題は、2つのプロットの高さと幅が不均一になり、見栄えをよくする方法がわかりません。

これが私のコードです:

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

回答:


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によって値の範囲が設定されている場合でも、色の範囲は、最後にプロットされた(が発生した)画像によって設定されることに注意してください。たとえば、別のプロットの最大値が高い場合、最大値より高い値のポイントは均一な色で表示されます。vminvmaxim


4
ImageGridは、まさにこの目的のためにも非常に役立ちます。
Phillip Cloud

5
tight_layout()を使用する必要がある場合は、tight_layoutの後にsubplots_adjustの後にすべてを行い、次にsubplots_adjustとadd_axesの座標を手動で微調整します。
user1748155 2013

2
すでに持っている2つの異なる散布図に単一のカラーバーを設定するにはどうすればよいですか?上記を試しましたが、「im」を適切な変数に置き換える方法がわかりません。私の散布図がplot1 = pylib.scatter(x、y、z)およびplot2 = pylib.scatter(a、b、c)であるとしましょう
Rotail

46
これは他の人には明白だったかもしれませんが、カラーバーがすべてのプロットの色を正確に表すためには、vminおよびvmax引数が重要であることを指摘したいと思います。それらは各サブプロットのカラー範囲を制御します。実際のデータがある場合は、最初に最小値と最大値を見つけるためにこれを通過する必要がある場合があります。
James Owers、2015

2
プロットの値の範囲が異なる場合、カラーバーの範囲は最後のプロットの範囲のみを表示しますよね?助言がありますか?
Lukas

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 2015年

8
nrowsを1に変更すると、どちらのプロットもcolorbarよりもショットが多くなります。それで、この問題をどのように解決できますか?
Jin

6
残念ながら、tight_layoutでは機能しませんが、それでも優れたソリューションです。
マーク

1
覚えておいてください...私はこのソリューションが大好きです!Tinha que ser cearense!
iury simoes-sousa

1
この答えの重要な部分はfig.colorbar(im, ax=axes.ravel().tolist())です。を省略するax=axes.ravel().tolist()と、カラーバーは1つのサブプロット内に配置されます。
nyanpasu64

55

このソリューションでは、Axesの位置やカラーバーのサイズを手動で微調整する必要がなく、複数行および単一行のレイアウトで機能し、で機能しtight_layout()ます。これは、matplotlibのAxesGrid Toolboxを使用して、ギャラリーの例から適合させたものです。ImageGrid

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 2017

3
@TomChoラベルを設定するには、インスタンス化するときに次のようにカラーバーのハンドルを取得しますthecb = ax.cax.colorbar(im)。その後、次のことを実行できますthecb.set_label_text("foo")
スピンアップ

1
カラーマップを変更するには?
Sigur

1
@Sigur私はあなたが今までにそれを理解したと確信していますが、他の人のために、imを宣言するときにcmapを変更できます:im = ax.imshow(data、vmin = 0、vmax = 1、cmap = 'your_cmap_here')
Shaunロウイス

38

使用make_axesはさらに簡単で、より良い結果が得られます。また、カラーバーの位置をカスタマイズすることもできます。subplotsx軸と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と、カラーバーは再びサブプロットより大きくなります。
Wesley Tansey

あなたのmatplotlibのデフォルトは何ですか?いいね!
rafaelvalle 2017

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()。利点は、Axesの位置を手動で設定する必要がなく、自動アスペクトの場合はすべて、カラーバーがサブプロットとまったく同じ高さになることです。画像を使用した多くの場合でも、以下のように満足のいく結果が得られます。

を使用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()代わりに使用してこのソリューションを実装する方法はありますか?ベースマップでそれを使用する方法がわからないので、私は尋ねています。
ラナダケナダ

1
@lanadaquenadaはい可能ですが、供給する必要があることGridSpecadd_subplot()、その場合には。
ImportanceOfBeingErnest

10

コメントで指摘されているように、abevieiramotaによる軸のリストを使用するソリューションは、画像の1行のみを使用するまで非常にうまく機能します。妥当なアスペクト比を使用すると効果的です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の真の等価物を取得できます。によって課される1:1のアスペクト比imshowpcolormeshために、の代わりにを使用すると、依然として大きな水平方向のギャップが得られ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は異なっていますか?(これらのマップ可能要素は等高線セットと表面セットが処理されるのと同じ方法で処理されることを想定しています。)2x2サブプロットに2つのカラーバーを作成する以下の3D表面プロットを使用する例があります(1行に1つのカラーバー) )。この質問では別の配置を明示的に求めていますが、この例はいくつかのことを明確にするのに役立つと思います。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

複数imのの値が異なる場合、同じカラーバーを使用しないでください。そのため、元の質問は実際には適用されません
スピンアップ

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

ここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.