LoginSignup
30
47

More than 3 years have passed since last update.

matplotlibでグラフを描く

Posted at

はじめに

データ解析とかをしていると、グラフ表現で可視化をすることはよくある。
Pythonではmatplotlibというライブラリを使うが、設定や書式など覚えきれずに度々調べてしまうので、よく使う機能・設定について備忘録的にまとめておく。
なので、「matplotlibとは?」や「pythonの使い方」などについての解説は特にしない。

基本的な描き方

とりあえず描く

一番基本的なプロットをやってみる。

単一データ
from matplotlib import pyplot as plt
from random import randint

# データの定義(サンプルなのでテキトー)
x = list(range(10))
y = [randint() for _ in x]

# グラフの描画
plt.plot(x, y)
plt.show()

出力↓
image.png

とりあえず描画するだけならば非常にシンプルで、上記サンプルのようにmatplotlibライブラリからpyplotモジュールをインポートし、リスト等で用意したデータをplt.plot()に渡すだけでよい。
plt.show()で出力例のようなウィンドウが開いて中にグラフが表示される。(注:サンプルのデータはランダムなのでグラフ自体は異なる)
ちなみにpyplotモジュールはpltという名称に置換するのが慣例らしい。

プロットしたいデータが複数ある場合は、plt.plot()を描画したいデータ分だけ重ねればよい。

複数のデータ
from matplotlib import pyplot as plt
from random import randint

# データの定義(サンプルなのでテキトー)
x = list(range(10))
y1 = [randint(0, 100) for _ in x]
y2 = [randint(0, 100) for _ in x]

# グラフの描画
plt.plot(x, y1)
plt.plot(x, y2)
plt.show()

出力↓
image.png

こんな感じに勝手に色分けされて描画される。

複数のグラフを1枚に描く

異なるデータを描画する際、重ねるのではなく別々に描画したいこともある。

複数グラフ
from matplotlib import pyplot as plt
from random import randint, random

# データの定義(サンプルなのでテキトー)
x = list(range(10))
y1 = [randint(0, 100) for _ in x]
y2 = [randint(0, 100) for _ in x]

# グラフの描画
fig = plt.figure()
ax = fig.add_subplot(2, 1, 1)
ax.plot(x, y1)
ax = fig.add_subplot(2, 1, 2)
ax.plot(x, y2)
plt.show()

出力↓
image.png

少し複雑になった。
最初に、fig = plt.figure()でグラフのインスタンスを取得し、ax = fig.add_subplot()でプロットエリアのインスタンスをaxに取得している。axに対してplotを実行することで異なるプロットエリアに描画している。
add_subplot()に渡す引数は左から、縦のプロットエリア数,横のプロットエリア数,割り当てるプロットエリア番号になっている。プロットエリア番号は左上から横⇒下の順で割りつく。

2x2の場合の例
image.png

なので、サンプルのサブプロット部分の指定を下記のようにすると、横並びにもできる。

横並び
ax = fig.add_subplot(1, 2, 1)
ax.plot(x, y1)
ax = fig.add_subplot(1, 2, 2)
ax.plot(x, y2)

image.png

ちなみに、サンプルではfigaxに明示的にプロットエリアのインスタンスを取得してからプロットしているが、下記のようにも書ける。

少しラフな書き方
# グラフの描画
plt.subplot(2, 1, 1)
plt.plot(x, y1)
plt.subplot(2, 1, 2)
plt.plot(x, y2)
plt.show()

好みの問題かもしれないが、この記法で書いても劇的に短縮されるわけではないのでメリットは薄い。(気がする)

なお、subplotでは強制的にグリッドレイアウトになるが、変則的なレイアウトにする手段もある。
ただし、大抵subplotで事足りる気がするのでこの記事には含まない。もし、必要になったらここの記事が参考になる。

よく使うグラフの種類

ここまでの例ではすべて折れ線グラフを使っていたが、いろいろな種類のグラフを書くことができる。

折れ線グラフ:plot

plt.plot(x, y)
image.png

散布図:scatter

plt.scatter(x, y)
image.png

棒グラフ:bar

plt.bar(x, y)
image.png

ヒストグラム:hist

plt.hist(y, 30)
image.png

ヒストグラム以外の種類は呼び出す関数を変えるだけで、x,yの指定通り描画できる。散布図はplot関数でlinewidthmarkerオプションを設定することでも作成できるが、scatter関数の場合は、関数名変えるだけなのでこちらのほうが楽。(scatterは厳密にはバブルチャート作成用らしい)
ヒストグラムに関してはランダムに存在するデータを第一引数に渡し、第二引数で分割数を指定する。上記のサンプルでは正規分布の10000件のデータをyに入れて、適当に30分割して表示してみた。
他にも円グラフやヒートマップなど色々あるが、ここではよく使うものだけまとめておく。ほかのグラフは公式ドキュメントに沢山の例があるので、描きたいグラフに近いものを参照すればよい。

グラフの保存

ここまでの例ではplt.show()でグラフの表示をしてきた。これを使うと、pythonコード実行時にグラフ用のウィンドウが開いて中にグラフが表示される形になる。一応、このウィンドウから保存もできるが、連続で数枚のグラフを作る時や、画像サイズにこだわる時は一々表示はせずにグラフ画像の保存用関数plt.savefig("ファイル名.png")で保存できる。
ファイル形式は保存するファイル名末尾につけた形式で自動判別される。
test.png
上の図は適当な散布図をpng形式で保存したもの。
ファイル名にはパスを指定するので、imgディレクトリ等を先に作って、その中に連続で画像を保存とかもできる。

保存先ディレクトリ自動作成を含んだコード例
#!/usr/bin/env python
# coding: utf-8
from matplotlib import pyplot as plt
import numpy as np
from pathlib import WindowsPath as Path

# データの定義(サンプルなのでテキトー)
x = range(100)
y = np.random.normal(100, 20, 100)

# 画像の保存先ディレクトリを作成
img_path = Path("./img")
img_path.mkdir(exist_ok=True)

# グラフの描画
plt.figure(figsize=(4, 2))
plt.plot(x, y, lw=0, marker='.')
plt.savefig("./img/test.png")

jupyterでの表示

jupyterのセルごとに実行結果をoutセルに表示できる。
%matplotlib inlineとノートブックの先頭などに書いておくだけで、他は通常通り書けばよい。
※いつからか不明だが、何も書かなくてもインライン表示できました。とりあえず実行して、表示できないときは上記を書くと解決するかもしれません。
image.png

markdown-preview-enhancedのコードチャンクでの表示

実験結果をレポートとしてまとめる時などに、markdown-preview-enhancedを使う場合、コードチャンクとして実行コードを埋め込むことができる。
通常、コードチャンクとしてpythonのコードを埋め込む時は、

```python {cmd}
~
```

のようにpython指定のコードブロックの後ろに␣{cmd}をつける。
このままでmatplotlibの描画コードを実行すると、plt.show()で別ウィンドウが開いてしまう。
こんな感じ↓
image.png

そこで、コードブロックの後ろを␣{cmd matplotlib}に変更すると、ドキュメント中に出力がインライン表示されるようになる。↓
image.png

hideも指定して、␣{cmd hide matplotlib}とすると、描画用のコードを隠してグラフだけ表示することもできる。

※コードチャンク自体デフォルトではセキュリティ上offになっているので、公式ドキュメントの通り有効にしたときのみ使用できる。公式ドキュメントでも下記の注意書きがあるように、悪意あるコードを実行できてしまう危険があるので、機能の有効化は自己責任で。

⚠️ Script execution is off by default and needs to be explicitly enabled in Atom >package / VSCode extension preferences

Please use this feature with caution because it may put your security at risk! Your >machine can get hacked if someone makes you open a markdown with malicious code while >script execution is enabled.

Option name: enableScriptExecution

見た目の調整

ここまではデータをプロットするだけだったが、レポートに載せる時などは当然体裁を整えたくなる。

よく使うオプション

軸、目盛り関連

軸範囲の調整:xlim, ylim

表示する軸範囲をを設定するオプション。
x軸はplt.xlim(min, max)、y軸はplt.ylim(min, max)にそれぞれ上下限値を指定する。
なお、axesを使う場合はax.set_xlim(min, max)ax.set_ylim(min, max)と若干名称が変化する。

例えば、下記コードは散布図を描画するが、基本0~100のデータ分布中に10000の値が紛れさせている。

#!/usr/bin/env python
# coding: utf-8
from matplotlib import pyplot as plt
import random

# データの定義(サンプルなのでテキトー)
x = range(100)
y = [random.randint(0, 100) for _ in range(40)]
y.append(10000)
y.extend([random.randint(0, 100) for _ in range(59)])

# グラフの描画
plt.scatter(x, y)
plt.show()

これを描画するとこうなる。
image.png

こんな外れデータを無視して分布が集中しているところを見たくなったりするときに、データから外れ値を除く処理を掛けなくても、y軸の描画範囲を絞るだけで見れるようになる。

y軸描画範囲を0~120に制限
#!/usr/bin/env python
# coding: utf-8
from matplotlib import pyplot as plt
import random

# データの定義(サンプルなのでテキトー)
x = range(100)
y = [random.randint(0, 100) for _ in range(40)]
y.append(10000)
y.extend([random.randint(0, 100) for _ in range(59)])

# グラフの描画
plt.scatter(x, y)
plt.ylim(0, 120)
plt.show()

image.png

数値でない軸目盛りを使う

x軸には時折数値でない値を使いたいこともある。

文字列をそのまま軸目盛りに使いたい場合

項目ごとに正規化した値を比較するようなグラフとかの場合は、x軸に項目名の文字列リストを指定するだけで自動的に項目名を軸にしてくれる。

#!/usr/bin/env python
# coding: utf-8
from matplotlib import pyplot as plt
import random

# データの定義(サンプルなのでテキトー)
x = ['a', 'b', 'c', 'd', "hoge"]
y = [random.randint(0, 100) for _ in x]

# グラフの描画
plt.bar(x, y)
plt.show()

出力↓
image.png

プロットは値そのままで、描画上文字列に置換したい場合

任意の文字列を目盛りに振りたい時は、x軸はplt.xticks(x, ticks)、y軸はplt.yticks(y, ticks)を使う。x,yは目盛り値のリスト、ticksには目盛りに対応するラベル(文字列とか)のリストを渡す。ticksを省略すると目盛りの値がそのままラベルになる。
なお、axesに設定する場合はset_xticks(),set_yticks()になる。

x軸の値ラベルを適当な文字列で置換する例
#!/usr/bin/env python
# coding: utf-8
from matplotlib import pyplot as plt
import random

# データの定義(サンプルなのでテキトー)
x = range(10)
xticks = ["id{}".format(x) for x in random.sample(range(100), len(x))]
y = [random.randint(0, 100) for _ in x]

# グラフの描画
plt.plot(x, y)
plt.xticks(x, xticks)
plt.show()

出力↓
image.png

また、xticks,yticksは軸の目盛りを調整するものになるので、デフォルトの目盛り間隔が広すぎる/狭すぎるといったときに、任意の間隔に調整するのにも使う。

目盛り間隔による違い
#!/usr/bin/env python
# coding: utf-8
from matplotlib import pyplot as plt
import random

# データの定義(サンプルなのでテキトー)
x = range(10)
xticks = ["id{}".format(x) for x in random.sample(range(100), len(x))]
y = [random.randint(0, 100) for _ in x]

# グラフの描画
fig = plt.figure()
ax = fig.add_subplot(3, 1, 1)
ax.plot(x, y)
ax.set_title("自動")
ax = fig.add_subplot(3, 1, 2)
ax.plot(x, y)
ax.set_xticks(x)
ax.set_title("全部")
ax = fig.add_subplot(3, 1, 3)
ax.plot(x, y)
ax.set_xticks([i for i in x[::3]])
ax.set_title("3つ飛ばし")
plt.tight_layout()
plt.show()

出力↓
image.png

軸目盛を表示したくない場合

plt.xticks([])のように空のリストを渡せば軸目盛を消せる。
なお、axesに設定する場合はset_xticks(),set_yticks()になる。
image.png

グリッドの表示/非表示

グラフによってはグリッド線のガイドがあると見やすくなるものがある。グリッドはplt.grid()で表示できる。

グリッド線表示例
#!/usr/bin/env python
# coding: utf-8
from matplotlib import pyplot as plt
import random

# データの定義(サンプルなのでテキトー)
x = range(10)
y = [random.randint(0, 100) for _ in x]

# グラフの描画
plt.plot(x, y)
plt.grid()
plt.show()

出力↓
image.png

オプション引数でaxis="x""y"を指定すると、指定した側だけグリッド線を表示できる。
image.png

データの表示関連

線の幅を調整:linewidth

折れ線で接続する線の幅を調整するオプション。
plt.plot(x, y, linewidth=xx)または、plt.plot(x, y, lw=xx)と指定する。(xxは任意の値)

表示例↓
image.png

線の種類:linestyle

折れ線で接続する線の種類を調整するオプション。
plt.plot(x, y, linestyle=xx)または、plt.plot(x, y, ls=xx)と指定する。(xxは任意の指定子)

指定子 種類
"-" 実線
"--" 破線
"-." 一点鎖線
":" 点線

表示例↓
image.png

点の種類:marker

プロットする点の種類を調整するオプション。
plt.plot(x, y, marker=xx)と指定する(xxは任意の指定子)
指定子は大量にあるが、主に使うものをピックアップしたのが下表。全種確認したい時は公式ドキュメントを参照。

指定子 種類
"o"
"."
"s" 四角
"D" ダイヤ
"+" プラス

表示例↓
image.png

線、点の色:color

描画する線及び点の色を調整するオプション。
plt.plot(x, y, color=xx)またはplt.plot(x, y, c=xx)と指定する(xxは任意の色名)
指定する色名の内、下表の色は短縮指定子を使える。
下表に無い色でもかなりの色がプリセットとして色名指定できるようになっている。詳しくは公式ドキュメント参照。

指定子 フルネーム
'r' "red"
'g' "green"
'b' "blue"
'c' シアン "cyan"
'm' マゼンタ "magenta"
'y' "yellow"
'k' "black"
'w' "white"

表示例↓
image.png

その他

凡例の表示

プロットしたデータの凡例を表示する機能。

表示例↓
image.png

表示例のコード
#!/usr/bin/env python
# coding: utf-8
from matplotlib import pyplot as plt
import random


# データの定義(サンプルなのでテキトー)
x = range(10)

fig, ax = plt.subplots(1, 1)
for i in range(3):
    lower = 100 * i
    upper = lower + 100
    y = [random.randint(lower, upper) for _ in x]
    ax.scatter(x, y, label="random:{}-{}".format(lower, upper))
plt.legend()
plt.tight_layout()
plt.show()

なお、凡例の表示位置をplt.legend(loc=xx)で指定できる。(xxは任意のパターンの指定子)
なお、axesの場合も同じメソッド名でよい。
指定パターンは下表の通り。

指定子 位置
"best" 自動(標準)
"upper left" 左上
"center left" 左中央
"lower left" 左下
"upper center" 中央上
"center" 中央
"lower center" 中央下
"upper right" 右上
"center right" 右中央
"lower right" 右下

表示例↓
image.png

表示例のコード
#!/usr/bin/env python
# coding: utf-8
from matplotlib import pyplot as plt
import random


# データの定義(サンプルなのでテキトー)
x = range(10)

# グラフの描画
fig = plt.figure()
locs = [
    "{} {}".format(a, b)
    for b in ["left", "center", "right"]
    for a in ["upper", "center", "lower"]
    if not (a == "center" and b == "center")
]
locs.insert(len(locs) // 2, "center")
for j, loc in enumerate(locs, 1):
    for i in range(3):
        lower = 100 * i
        upper = lower + 100
        y = [random.randint(lower, upper) for _ in x]
        ax = fig.add_subplot(3, 3, j)
        ax.scatter(x, y, label="{}-{}".format(lower, upper))
        ax.set_title("\"{}\"".format(loc))
        ax.legend(loc=loc)
plt.tight_layout()
plt.show()

グラフタイトルの表示

グラフに任意のタイトルを書く機能。
plt.title(xx)で指定する。(xxは表示したい任意の文字列)なお、axesの場合はax.set_title(xx)となる。

表示例↓
image.png

表示例のコード
#!/usr/bin/env python
# coding: utf-8
from matplotlib import pyplot as plt
import random


# データの定義(サンプルなのでテキトー)
x = range(10)
y = [random.randint(0, 100) for _ in x]

# グラフの描画
plt.plot(x, y)
plt.title("タイトル")
plt.show()

任意のテキストの表示

axes内の任意座標を指定して、指定したテキストを描画する機能。
plt.text(x, y, s)で指定する。

表示例↓
image.png

表示例のコード
#!/usr/bin/env python
# coding: utf-8
from matplotlib import pyplot as plt
import random


# データの定義(サンプルなのでテキトー)
x = range(10)
y = [random.randint(0, 100) for _ in x]

# グラフの描画
plt.plot(x, y)
plt.text(5, 50, "真ん中")
plt.grid()
plt.show()

終わりに

普段よく使う機能をまとめてみたが、かなりのボリュームになってしまった…
matplotlibにはまだまだ使ったことない機能も沢山あるが、次はアニメーションをまとめたいと思います。

参考

30
47
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
30
47