2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

matplotlibでカラーバーを自分で作って横に表示させる

Last updated at Posted at 2022-01-05

#はじめに

最近グラフを作るのにpythonのmatplotlibを使用しています。
グラフ内の物理量変化を表すために、plt.fillという関数を使用しています。

さらに値の変化を色のグラデーションで表したく、cm.jetを使用して領域ごとに計算して色を決めています。

塗った結果がこちらです。

Figure 2022-01-04 200740.png

ここにカラーバーをつけたいのですが、手動で色を決めて塗ったため、fig.colorbar(im)などはできません。
そこでうまくカラーバーを自分で作成して横に表示させて以下のようにする方法を考えました。

image.png

#ベースとなるコード

上のようなコードは複雑なので、以下のコードをベースに作っていきます。

base.py
import matplotlib.pyplot as plt
import matplotlib.cm as cm

#cross_point_x,cross_point_yは座標を格納した行列
#M_areaはその場所での今回色で表したい物理量を格納した行列

n=9

cross_point_x= [["nan" for j in range(n+2)] for i in range(n+2)]
cross_point_y= [["nan" for j in range(n+2)] for i in range(n+2)]
M_area = [["nan" for j in range(n+2)] for i in range(n+2)]

for i in range(1,n+2):
    for j in range(1,i+1):
        cross_point_x[i][j] = i*j
        cross_point_y[i][j] = i*(n+2-j)
        M_area[i][j] = i*j
        

color_Mmax = 100
color_Mmin=0


for i in range(2,n+2):
    for j in range(2,i):
        testx = [cross_point_x[i-1][j-1], cross_point_x[i-1][j], cross_point_x[i][j], cross_point_x[i][j-1]]
        testy = [cross_point_y[i-1][j-1], cross_point_y[i-1][j], cross_point_y[i][j], cross_point_y[i][j-1]]
        plt.fill(testx,testy,color=cm.jet((M_area[i][j]-color_Mmin)/(color_Mmax-color_Mmin)))
       
plt.show()

これを実行すると、以下のグラフが描けます。

image.png

#案1 枠外に散布図を描く

image.png

###メリット
きれいに描ける

###デメリット
手動でいろいろ調整しなければいけない

###解説
matplotlibでは散布図を描くと、fig.colorbar(im)を使用すると自動的にカラーバーを作ってくれます。
なので、ほしいグラフの描写範囲外に最大値と最小値をとる点をプロットして、カラーバーを作ります。
プロットした結果がこちら

image.png

いらない点が2点描写されているので、xlimylimを使用して枠外になるようにします。

image.png

このコードは以下のplan1.1pyです。

plan1.1.py
import matplotlib.pyplot as plt
import matplotlib.cm as cm

#cross_point_x,cross_point_yは座標を格納した行列
#M_areaはその場所での今回色で表したい物理量を格納した行列

n=9

cross_point_x= [["nan" for j in range(n+2)] for i in range(n+2)]
cross_point_y= [["nan" for j in range(n+2)] for i in range(n+2)]
M_area = [["nan" for j in range(n+2)] for i in range(n+2)]

for i in range(1,n+2):
    for j in range(1,i+1):
        cross_point_x[i][j] = i*j
        cross_point_y[i][j] = i*(n+2-j)
        M_area[i][j] = i*j
        
color_Mmax = 100
color_Mmin=0

for i in range(2,n+2):
    for j in range(2,i):
        testx = [cross_point_x[i-1][j-1], cross_point_x[i-1][j], cross_point_x[i][j], cross_point_x[i][j-1]]
        testy = [cross_point_y[i-1][j-1], cross_point_y[i-1][j], cross_point_y[i][j], cross_point_y[i][j-1]]
        plt.fill(testx,testy,color=cm.jet((M_area[i][j]-color_Mmin)/(color_Mmax-color_Mmin)))


#---------------------------------------------
#追加分
#カラーバーを無理やり表示外にかく
#(-10,-10)に値color_Mmin、(-20,-20)に値color_Mmaxの値を持つ
a=[-10,-20]
b=[-10,-20]
c=[color_Mmin,color_Mmax]

#散布図を上から描写
img = plt.scatter(a,b,c=c,cmap=plt.cm.jet,alpha=1)
#カラーバーを自動で作成(plt.scatterを使ったときのみ使用可能)
plt.colorbar(img)

#描写範囲を主導で設定して追加した散布図を範囲外にする
plt.xlim(0,100)
plt.ylim(10,100)

#---------------------------------------------

plt.show()

xlimylimは自分で調整しなければいけないので、ここがめんどくさいポイントです。

#案2 subplotを使用して横に別の図として描写する

image.png

###メリット
描写範囲を手動で設定する必要がない

###デメリット
コードがちょっと複雑

###解説
matplotlibには、subpotといって図を複数並べて描写する関数があります。
さらに、カラーバーのみを図としてプロットする関数もあります。

これらを組み合わせて描写した結果が以下です。

image.png

コードはこちら

plan2.1.py
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib import gridspec
import matplotlib as mpl

#cross_point_x,cross_point_yは座標を格納した行列
#M_areaはその場所での今回色で表したい物理量を格納した行列

n=9

cross_point_x= [["nan" for j in range(n+2)] for i in range(n+2)]
cross_point_y= [["nan" for j in range(n+2)] for i in range(n+2)]
M_area = [["nan" for j in range(n+2)] for i in range(n+2)]

for i in range(1,n+2):
    for j in range(1,i+1):
        cross_point_x[i][j] = i*j
        cross_point_y[i][j] = i*(n+2-j)
        M_area[i][j] = i*j
        

color_Mmax = 100
color_Mmin=0

#---------------------------------------
#図を二つ作成
fig = plt.figure()
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
#---------------------------------------

for i in range(2,n+2):
    for j in range(2,i):
        testx = [cross_point_x[i-1][j-1], cross_point_x[i-1][j], cross_point_x[i][j], cross_point_x[i][j-1]]
        testy = [cross_point_y[i-1][j-1], cross_point_y[i-1][j], cross_point_y[i][j], cross_point_y[i][j-1]]
        ax1.fill(testx,testy,color=cm.jet((M_area[i][j]-color_Mmin)/(color_Mmax-color_Mmin)))
#ax1.fillというようにサブプロットに変更


#--------------------------------------------------------------
#カラーバーの手動作成
norm = mpl.colors.Normalize(vmin=color_Mmin, vmax=color_Mmax)
cbar = mpl.colorbar.ColorbarBase(
    ax=ax2,
    cmap=plt.cm.jet,
    norm=norm,
    orientation="vertical",
    label="Mach number",
)
#--------------------------------------------------------------

plt.show()

##図の比率を変更する

plan2.1.pyのコードだと、カラーバーが太すぎるという問題があります。
そこで、カラーバーの比率を変えます。

図:カラーバー=50:1とした結果がこちらです。

image.png

plan2.2.py
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib import gridspec
import matplotlib as mpl


#cross_point_x,cross_point_yは座標を格納した行列
#M_areaはその場所での今回色で表したい物理量を格納した行列

n=9

cross_point_x= [["nan" for j in range(n+2)] for i in range(n+2)]
cross_point_y= [["nan" for j in range(n+2)] for i in range(n+2)]
M_area = [["nan" for j in range(n+2)] for i in range(n+2)]

for i in range(1,n+2):
    for j in range(1,i+1):
        cross_point_x[i][j] = i*j
        cross_point_y[i][j] = i*(n+2-j)
        M_area[i][j] = i*j
        

color_Mmax = 100
color_Mmin=0


#----------------------------------------------------
fig = plt.figure()
#ここ追加、width_ratiosで比率を変える
spec = gridspec.GridSpec(ncols=2, nrows=1,
                         width_ratios=[50, 1])

ax1 = fig.add_subplot(spec[0])
ax2 = fig.add_subplot(spec[1])
#----------------------------------------------------




for i in range(2,n+2):
    for j in range(2,i):
        testx = [cross_point_x[i-1][j-1], cross_point_x[i-1][j], cross_point_x[i][j], cross_point_x[i][j-1]]
        testy = [cross_point_y[i-1][j-1], cross_point_y[i-1][j], cross_point_y[i][j], cross_point_y[i][j-1]]
        ax1.fill(testx,testy,color=cm.jet((M_area[i][j]-color_Mmin)/(color_Mmax-color_Mmin)))



norm = mpl.colors.Normalize(vmin=color_Mmin, vmax=color_Mmax)
cbar = mpl.colorbar.ColorbarBase(
    ax=ax2,
    cmap=plt.cm.jet,
    norm=norm,
    orientation="vertical",
    label="Mach number",
)


plt.show()

#まとめ

異なる2つの方法を紹介しました。
それぞれに特徴があるので、使いやすい方法を使ってみてください。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?