私はMatplotlibで2つのサブプロットを同じy軸を共有させ、2つのサブプロットを同じy軸を共有させる方法を研究するのに、非常に長い時間を費やしました。
私がsubplot1
またはsubplot2
でcolorbar()
関数を呼び出したとき、カラーバーとプロットが 'subplot'境界ボックスの内側に収まるようにプロットをオートスケールし、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()
カラーバーを独自の軸に配置し、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()
軸のリストとともにfigure.colorbar()
のax
パラメーターを使用してJoe Kingtonのコードを単純化できます。から ドキュメント :
斧
なし新しいカラーバーAxesのスペースを取得する親Axesオブジェクト。軸のリストが与えられた場合、それらはすべてカラーバー軸のためのスペースを空けるためにリサイズされます。
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行レイアウトで機能し、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()
make_axes
を使うのはさらに簡単で、より良い結果を与えます。カラーバーの位置をカスタマイズすることも可能です。 x軸とy軸を共有するためのsubplots
のオプションにも注意してください。
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()
他の答えで指摘されているように、考えは通常カラーバーが存在するための軸を定義することです。そうするための様々な方法があります。まだ言及されていないものは、サブプロット作成時に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()
このスレッドに出会った初心者として、私は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がはるかに少ないので、私のような誰もが実際にここで何が起こっているのかを簡単に確認できます。
コメントで指摘されているように、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()
カラーバー関数 は、カラーバー軸のサイズの倍率であるshrink
パラメータを提供します。手動の試行錯誤が必要です。例えば:
fig.colorbar(im, ax=axes.ravel().tolist(), shrink=0.75)
@ abevieiramotaの優れた答えに追加するには、constrained_layoutを使用して、tight_layoutと同等のものを取得できます。 imshow
のアスペクト比が1:1であるため、pcolormesh
の代わりに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()
私は、投稿されたほとんどすべての解決策がax.imshow(im, ...)
を含み、複数のサブ設定に対してカラーバーに表示される色を正規化していないことに気付きました。 im
マッピング可能ファイルは最後のインスタンスから取得されますが、複数のim
の値が異なる場合はどうなりますか? (これらのマッピング可能物は、等高線セットと曲面セットが扱われるのと同じ方法で扱われると仮定しています。)2x2サブプロットに対して2つのカラーバーを作成する、下の3d曲面プロットを使った例があります。 )質問は別の取り決めを明示的に要求しますが、私は例がいくつかのことを明確にするのを助けると思います。残念ながら3D軸のため、plt.subplots(...)
を使用してこれを実行する方法はまだ見つかりません。
私がカラーバーをより良い方法で配置できるのであれば...(これを行うにはおそらくもっと良い方法がありますが、少なくとも従うのはそれほど難しくないはずです)。
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