Python 

Matplotlib-2軸グラフの書き方

こんにちは! 新宿の昼ごはんがお財布に響いている、インターンの菅野です。

今回は、matplotlibの2軸を使ってグラフを書く方法について説明していきたいと思います。
下の図を目標に進めていきます。

matplotlibについて

matplotlibには2つの流派が存在します。
1つ目は、 Pyplot流派
2つ目は、オブジェクト指向流派になります。
今回の記事では、オブジェクト志向流派を使用しています。

使用するデータ

データは、「とあるWebサービスのPV数(ページビュー)と新規登録者の数」と想定します。
なんと最近ある記事がバスりました。いつもと比べてどのくらい数値が変わったのかを確認したいと思います

 PV新規登録者
2018-03-011005616327
2018-03-021038596309
2018-03-031311877690
2018-03-041313847511
2018-03-05993816364
2018-03-06989456569
2018-03-07997476311
2018-03-081018836652
2018-03-091019676722
2018-03-101307787457

 

描画

2つのグラフをまとめて描画・1軸

では、さっそくグラフを書いてみましょう。 

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

fig = plt.figure()
ax1 = fig.add_subplot(111)

# 1つのaxesオブジェクトのlines属性に2つのLine2Dオブジェクトを追加
ax1.plot(frame.index.to_pydatetime(), frame["pageview"],
         color=cm.Set1.colors[1], label="PV数")
ax1.plot(frame.index.to_pydatetime(), frame["register"],
         color=cm.Set1.colors[0], label="新規登録者数")

#凡例をつける
ax1.legend()

すると、なんだかバズった割にはどうも新規登録者数が少なめに見えます。
このグラフは問題ない、、、ということはなく、「異なる単位のデータを同じ軸で表示している」という致命的な欠点を抱えています。PV数と新規登録者数は、単位が違います。

その結果、新規登録者数のグラフが潰れてしまっています。
2つの数値を分けて描画してみましょう。

 

PV数と新規登録者のグラフを別々に描画

2つのグラフを別々に描画します。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

# figureオブジェクトを作成
fig = plt.figure()
# figureオブジェクトに属するaxesオブジェクトを生成
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)

# それぞれのaxesオブジェクトのlines属性にLine2Dオブジェクトを追加
ax1.plot(frame.index.to_pydatetime(), frame["pageview"],
         color=cm.Set1.colors[1], label="PV数")
ax2.plot(frame.index.to_pydatetime(), frame["register"],
         color=cm.Set1.colors[0], label="新規登録者数")

#凡例をつける
ax1.legend()
ax2.legend()

新規登録者数を潰さずに描画することができました。

 

PV数と新規登録者のグラフをまとめて描画・1軸

上記の2つ別々に描画するのではなく、1つのグラフで描画してみましょう。

fig, ax1 = plt.subplots()

# ax1とax2を関連させる
ax2 = ax1.twinx()

# それぞれのaxesオブジェクトのlines属性にLine2Dオブジェクトを追加
ax1.bar(frame.index.to_pydatetime(), frame["pageview"], color=cm.Set1.colors[1],
        label="PV数")
ax2.plot(frame.index.to_pydatetime(), frame["register"],
         color=cm.Set1.colors[0], label="新規登録者数")

# 凡例
# グラフの本体設定時に、ラベルを手動で設定する必要があるのは、barplotのみ。plotは自動で設定される>
handler1, label1 = ax1.get_legend_handles_labels()
handler2, label2 = ax2.get_legend_handles_labels()
# 凡例をまとめて出力する
ax1.legend(handler1 + handler2, label1 + label2, loc=2, borderaxespad=0.)

pageview_max = 3 * max(frame["pageview"])
register_max = 1.2 * max(frame["register"])

ax1.set_ylim([0, pageview_max])
ax2.set_ylim([0, register_max])

ここで登場しているのが、twinx()関数です。
この関数で、左右に異なる軸を持つことができるようになります。

 

おまけ: 2軸グラフを書く際に注意すべきこと

2軸グラフは使い方によっては、わかりにくくなり誤解を招くことがございます。
以下のような工夫をし、理解しやすいグラフを目指しましょう。

  1. 重要な数値を左軸にする
  2. なるべく違うタイプのグラフを用いる。
      例:棒グラフと線グラフの組み合わせ
  3. 着色する

上記に注意し、グラフを修正すると以下のようになります。

以下、ソースコードです。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
import matplotlib.ticker as ticker

# styleを変更する
# plt.style.use('ggplot')

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()

# styleを適用している場合はgrid線を片方消す
ax1.grid(True)
ax2.grid(False)

# グラフのグリッドをグラフの本体の下にずらす
ax1.set_axisbelow(True)

# 色の設定
color_1 = cm.Set1.colors[1]
color_2 = cm.Set1.colors[0]

# グラフの本体設定
ax1.bar(frame.index.to_pydatetime(), frame["pageview"], color=color_1,
        label="PV数")
ax2.plot(frame.index.to_pydatetime(), frame["register"], color=color_2,
         label="新規登録者数")

# 軸の目盛りの最大値をしている
# axesオブジェクトに属するYaxisオブジェクトの値を変更
ax1.yaxis.set_major_locator(MaxNLocator(nbins=5))
ax2.yaxis.set_major_locator(MaxNLocator(nbins=5))

# 軸の縦線の色を変更している
# axesオブジェクトに属するSpineオブジェクトの値を変更
# 図を重ねてる関係で、ax2のみいじる。
ax2.spines['left'].set_color(color_1)
ax2.spines['right'].set_color(color_2)

# 軸の縦線の色を変更している
ax1.tick_params(axis='y', colors=color_1)
ax2.tick_params(axis='y', colors=color_2)

#  軸の目盛りの単位を変更する
ax1.yaxis.set_major_formatter(ticker.FormatStrFormatter("%d人"))
ax2.yaxis.set_major_formatter(ticker.FormatStrFormatter("%d件"))

# 凡例
# グラフの本体設定時に、ラベルを手動で設定する必要があるのは、barplotのみ。plotは自動で設定される>
handler1, label1 = ax1.get_legend_handles_labels()
handler2, label2 = ax2.get_legend_handles_labels()
# 凡例をまとめて出力する
ax1.legend(handler1 + handler2, label1 + label2, loc=2, borderaxespad=0.)

# グラフの範囲を決める
pageview_max = 3 *max(frame["pageview"])
register_max = 1.2 * max(frame["register"])

ax1.set_ylim([0, pageview_max])
ax2.set_ylim([0,  register_max])

 

いかがだったでしょうか?  
matplotlibで2軸グラフを描く方法をご紹介いたしました。
意外と奥が深いmatplotlib、いろいろ調べてみると新たな発見があるかもしれません。

DATUM STUDIOでは様々なAI/機械学習のプロジェクトを行っております。
詳細につきましてはこちら

詳細/サービスについてのお問い合わせはこちら



DATUM STUDIOは、クライアントの事業成長と経営課題解決を最適な形でサポートする、データ・ビジネスパートナーです。
データ分析の分野でお客様に最適なソリューションをご提供します。まずはご相談ください。

このページをシェアする: