工場裏のアーカイブス

素人によるiPhoneアプリ開発の学習記 あと機械学習とかM5Stackとか

matplotlibを用いたグラフの書き方(備忘録)

色々とPythonを使ってデータ処理を行う機会が増える(かもしれない)ので、matplotlibを用いたグラフの書き方についていくつかメモします。個人的な備忘録の意味合いが強く、他記事と書き方が異なっており、説明なども少なめです。今後は予告なく内容を追加するかもしれません。

※環境はJupyter Notebookの想定です。

 

基本的なグラフ

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-np.pi, np.pi, 100)
y1 = np.sin(x)
y2 = np.cos(x)

plt.plot(x, y1, label="sin")
plt.plot(x, y2, label="cos")

plt.title("Graph")
plt.xlabel("x", size=12)
plt.ylabel("y", size=12)

plt.legend()
plt.show()

f:id:fleron:20200413231441p:plain
 

凡例(legend)の調整

legendは配置やフォントサイズを調整出来る。 bbox_to_anchorはアンカーポイントであり、左下が(0, 0)、右上が(1, 1)。 locでアンカーポイントに対する凡例の枠の位置を指定する。borderaxespadは凡例の枠に対して、余白をどの程度設けるかの設定。

locで可能な指定は以下の通りとなる。

‘best’, ‘upper right’, ‘upper left’, ‘lower left’, ‘lower right’, ‘right’,
‘center left’, ‘center right’, ‘lower center’, ‘upper center’, ‘center’

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-np.pi, np.pi, 100)
y1 = np.sin(x)
y2 = np.cos(x)

plt.title("Graph")
plt.xlabel("x", size=12)
plt.ylabel("y", size=12)
plt.plot(x, y1, label="sin")
plt.plot(x, y2, label="cos")

plt.legend(bbox_to_anchor = (1.0, 1.0), loc = "upper right", borderaxespad=1, fontsize = 15)
plt.show()

f:id:fleron:20200413232925p:plain
 

axesを用いる方法

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-np.pi, np.pi, 100)
y1 = np.sin(x)
y2 = np.cos(x)

fig = plt.figure(figsize=(5, 5))
ax = fig.add_subplot(111) 
ax.plot(x, y1, label="sin") 
ax.plot(x, y2, label="cos") 

ax.set_title("Graph")
ax.set_xlabel("x", size=12)
ax.set_ylabel("y", size=12)

plt.legend(bbox_to_anchor = (0.0, 1.0), loc = "upper left", borderaxespad=1, fontsize = 12)
plt.show()

f:id:fleron:20200413233353p:plain
 

グラフの表示範囲を変更する

plt.xlim(a, b), plt.ylim(a, b)を使用する(axesの場合はax.set_xlim(a, b)といった形にする)。
↓最初の「基本的なグラフ」に対して、plt.xlim(-1, 1), plt.ylim(-2, 2)を設定した場合。
f:id:fleron:20200413234319p:plain
 

複数のグラフを並べる(1)

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(2, 2, figsize=(12, 6))
ax1, ax2, ax3, ax4 = axes.flatten()

#以下のように1行で書くことも出来る
#fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 6))

#あるいは以下のように、1つ1つ追加することも出来る。subplotは(行数、列数、番号)
#subplot(2, 2, 1)ではなくsubplot(221)のように書くことも可能
#fig = plt.figure(figsize=(12, 6))
#ax1 = fig.add_subplot(2, 2, 1)
#ax2 = fig.add_subplot(2, 2, 2)
#ax3 = fig.add_subplot(2, 2, 3)
#ax4 = fig.add_subplot(2, 2, 4)

x = np.linspace(-3*np.pi, 3*np.pi, 300)
y0 = np.sin(x)
y1 = np.cos(x) 
y2 = np.abs(np.sin(x))
y3 = np.abs(np.cos(x))

#凡例にLaTeXコマンドを使用する(raw文字列にする必要あり)
ax1.plot(x, y0, color = "red", label = r"$\sin x$")
ax2.plot(x, y1, color = "blue", label = r"$\cos x$")
ax3.plot(x, y2, color = "green", label = r"$|\sin x$|")
ax4.plot(x, y3, color = "yellow", label = r"$|\cos x$|")

ax1.set_title("Graph 1")
ax2.set_title("Graph 2")
ax3.set_title("Graph 3")
ax4.set_title("Graph 4")

ax1.set_xlabel("x")
ax2.set_xlabel("x")
ax3.set_xlabel("x")
ax4.set_xlabel("x")

ax2.set_ylabel("y")
ax2.set_ylabel("y")
ax2.set_ylabel("y")
ax2.set_ylabel("y")

#locは指定しないと、各グラフで凡例の位置がバラける
ax1.legend(loc = "upper right") 
ax2.legend(loc = "upper right") 
ax3.legend(loc = "upper right") 
ax4.legend(loc = "upper right") 

#並んだグラフのタイトルやラベルが、重なってしまうことを防ぐ
fig.tight_layout()             
plt.show()

f:id:fleron:20200413235338p:plain
 

複数のグラフを並べる(2)

forループを用いて短く記述。出力されるグラフは(1)と同じ。

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(2, 2, figsize=(12, 6))
faxes = axes.flatten()

x = np.linspace(-3*np.pi, 3*np.pi, 300)
y0 = np.sin(x)
y1 = np.cos(x) 
y2 = np.abs(np.sin(x))
y3 = np.abs(np.cos(x))
y = [y0, y1, y2, y3]

colorlist = ["red", "green", "blue", "yellow"]      # 各プロットの色
labellist = [r"$\sin$",r"$\cos x$",r"$|\sin x|$",r"$|\cos x|$"]   # 各ラベル

for i, ax in enumerate(faxes):
    ax.plot(x, y[i], color=colorlist[i], label=labellist[i])
    ax.set_title(f"Graph {i+1}")
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.legend(loc = "upper right")

fig.tight_layout()              
plt.show()

 

複数のグラフを並べる(3)

グラフ1内に別のグラフ2を、任意の位置・サイズで描画する。left, bottomはグラフ2の左下位置、width, heightはグラフ2の幅、高さであり、それぞれグラフ1(figure)の範囲に対する相対値で指定する。

#グラフ1を上書きする形になるので、きちんと使うなら調整が必要。
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-np.pi, np.pi, 100)
y1 = np.sin(x)
y2 = np.cos(x)

plt.figure(figsize=(8, 5))
plt.plot(x, y1, label="sin")
plt.legend(bbox_to_anchor = (0.0, 1.0), loc = "upper left", borderaxespad=1, fontsize = 12)
ax2 = plt.axes([0.2, 0.2, 0.4, 0.4]) #[left, bottom, width, height]
ax2.plot(x, y2, label="cos")

plt.legend(bbox_to_anchor = (0.0, 1.0), loc = "upper left", borderaxespad=1, fontsize = 12)
plt.show()

f:id:fleron:20210905220817p:plain
 

グラフを点線にする

plot()関数の、linestyle引数(lsと略記も可)で指定する。linewidth引数で線の太さも変更できる。 linestyleとして可能な指定は下表の通り。
※散布図(scatte)の場合、linewidthではマーカーの輪郭線の太さの指定となる。

":" 点線
"-." 一点鎖線
"--" 破線
"-" 実線

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

fig, axes = plt.subplots(2, 2, figsize=(10, 6))
faxes = axes.flatten()

x = np.linspace(-np.pi, np.pi, 100)
y = np.sin(x)
stylelist = [":", "-.", "--", "-"]

for i, ax in enumerate(faxes):
    ax.plot(x, y, color = "blue", ls = stylelist[i], linewidth = 2)
    ax.set_title(f"linstyle \"{stylelist[i]}\"")
    
fig.tight_layout()
plt.show()

f:id:fleron:20200414235633p:plain
 

散布図を描く

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-np.pi, np.pi, 50)
y = np.sin(x)

plt.plot(x, y, color=(0.2, 0.8, 0.2)) 

#ノイズを加える
y += 0.2 * np.random.randn(len(x))

plt.scatter(x, y)
plt.show()

f:id:fleron:20200414235939p:plain
 

棒グラフを描く

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(0)
x = [0, 1, 2, 3, 4]
y = np.random.rand(5)
label = ["A", "B", "C", "D", "E"]

plt.bar(x=x, height=y, tick_label=label)
plt.xlabel("Sample")
plt.ylabel("Value")
plt.show()

f:id:fleron:20210905223045p:plain
 

imshowによる2次元配列の描画

cmap引数でカラーマップを指定する。カラーマップの一覧は以下の公式リファレンスを参照。

https://matplotlib.org/examples/color/colormaps_reference.htmlmatplotlib.org

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

plt.imshow(np.random.randn(50, 50), cmap = "magma")
plt.title("50 x 50”)

f:id:fleron:20200415000806p:plain
 

多次元配列を用いた、2変数関数のimshowによる描画

Numpyの関数であるmgridを使用して、格子状の多次元配列を手軽に生成出来る。

mgrid[(x始点) : (x終点) : (x刻み幅), (y始点) : (y終点) : (y刻み幅)] または
mgrid[(x始点) : (x終点) : (x分割数)j, (y始点) : (y終点) : (y分割数)j] という形で使用する。
刻み幅の所に”j”を付けると、始点〜終点の区間を何分割するかという指定に変わる。

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

#それぞれ-2〜2の区間を400分割する
y, x = np.mgrid[-2:2:400j, -2:2:400j]
z = 10*np.sin(x**2 + y**2)
plt.imshow(z)

f:id:fleron:20200415002750p:plain
 

様々なカラーマップを用いてのimshowによる描画

先述のカラーマップ一覧のうち、いくつかをピックアップして、適用したグラフを並べてみる。

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

y, x = np.mgrid[-np.pi:np.pi:300j, -np.pi:np.pi:300j]
z = 10 * np.cos(x**2 + y**2)

fig, axes = plt.subplots(6, 4, figsize=(12, 18))
faxes = axes.flatten()

#公式リファレンスのカラーマップ一覧よりピックアップ
cmaplist = ["viridis", "plasma", "inferno", "magma", 
           "Greys", "Reds", "GnBu", "YlOrBr",
           "bone", "spring", "winter", "hot",
           "PiYG", "PuOr", "RdYlBu", "coolwarm",
           "Pastel1", "Paired", "Set1", "tab20",
           "flag", "prism", "terrain", "gist_rainbow"]

for i, ax in enumerate(faxes):
    ax.imshow(z, cmap = cmaplist[i])
    ax.set_title(f"{i+1} : {cmaplist[i]}")

fig.tight_layout() 
plt.show()

f:id:fleron:20200415002847p:plain
 

3次元グラフ(サーフェス)を描く

Numpyの関数であるmeshgridを使用して、別の方法で格子状の多次元配列を生成する。そしてAxes3Dモジュールを用いて、add_subplot()の引数でprojection=‘3d’と指定することで、3次元グラフを扱うことが可能となる。

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

x1 = np.linspace(0, 1, 100)
x2 = np.linspace(0, 1, 100)
x1, x2 = np.meshgrid(x1, x2)
y = 0.3 * x1**2 + 0.7 * x2**2

fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, projection="3d") #3次元版
ax.set_xlabel(r"$x_1$", fontsize=16)
ax.set_ylabel(r"$x_2$", fontsize=16)
ax.set_zlabel(r"$y$", fontsize=16)
surf = ax.plot_surface(x1, x2, y, rstride=5, cstride=5, cmap="plasma", alpha=0.8)
plt.colorbar(surf, ax=ax, shrink=0.75)
plt.show()

f:id:fleron:20210905225331p:plain
 

3次元グラフ(ワイヤフレーム)を描く

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

x1 = np.linspace(0, 1, 100)
x2 = np.linspace(0, 1, 100)
x1, x2 = np.meshgrid(x1, x2)
y = 0.3 * x1**2 + 0.7 * x2**2

fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, projection="3d") #3次元版
ax.set_xlabel(r"$x_1$", fontsize=16)
ax.set_ylabel(r"$x_2$", fontsize=16)
ax.set_zlabel(r"$y$", fontsize=16)
ax.plot_wireframe(x1, x2, y, linewidth=0.5)
plt.show()

f:id:fleron:20210905231758p:plain
 

3次元のバーを用いて、曲面を描く

bar3Dは本来、ヒストグラムや棒グラフを描くためのものであるが、これで強引に曲面を描いてみる。

bar3Dにおける各バーの座標は1次元配列で与える必要があるため、ravel関数で変換を行う。またバーの高さ(この場合はyの値)に応じて、カラーマップを割り当てるような仕組みは自前で実装する必要がある。

mport numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cm
%matplotlib inline

x1 = np.linspace(0, 1, 20)
x2 = np.linspace(0, 1, 20) 
x1, x2 = np.meshgrid(x1, x2)
xr1 = x1.ravel()
xr2 = x2.ravel()
y = 0.3 * xr1**2 + 0.7 * xr2**2 

def ColorMap(z, cmap):
    cmap = cm.get_cmap(cmap)
    norm = colors.Normalize(vmin = z.min(), vmax = z.max())
    return cmap(norm(z))

fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel(r"$x_1$", fontsize=16)
ax.set_ylabel(r"$x_2$", fontsize=16)
ax.set_zlabel(r"$y$", fontsize=16)
ax.bar3d(xr1, xr2, 0, 0.05, 0.05, y, color=ColorMap(y, 'plasma'))
plt.show()

f:id:fleron:20210905232116p:plain
 

【参考】公式による、様々なグラフの描画例

matplotlib.org