matplotlibで棒グラフと折れ線グラフを重ねて表示する

## はじめに

　Pythonの**matplotlib**ライブラリの**pyplot**を使って同一キャンパス上に棒グラフと折れ線グラフを描く方法の覚え書きです。あくまでも個人の忘備録レベルの覚書です。勘違いやおかしな記述があるかもしれませんが、ご容赦くださいますようお願いいたします。

## ゴール

　以下のようなグラフを描画します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/124810/dc389aca-2303-769c-2691-986a5100c87b.png)

このグラフは **Google Colaboratory** の以下のページにてご確認いただくことができます。

[データ登録費用推移.ipynb @ Google Colaboratory](https://colab.research.google.com/drive/11q-aGtjXaUNBJnQDzrBvEEpNIhLUt0QC?usp=sharing)

![スクリーンショット 2021-01-03 23.53.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/124810/3ce3ca01-610f-d850-9fc2-584a7a9da7f0.png)

　実行ボタン(▶️)を押すと上記のようなグラフが描画されます。データは架空のものですので内容は適当です💦


## 日本語表示するために

　pipで**japanize-matplotlib**ライブラリをインストールします。

```bash:Colaboratoryセル
!pip install japanize-matplotlib
```

* 実行結果

```
Collecting japanize-matplotlib
  Downloading https://files.pythonhosted.org/packages/aa/85/08a4b7fe8987582d99d9bb7ad0ff1ec75439359a7f9690a0dbf2dbf98b15/japanize-matplotlib-1.1.3.tar.gz (4.1MB)
     |████████████████████████████████| 4.1MB 9.1MB/s 
Requirement already satisfied: matplotlib in /usr/local/lib/python3.6/dist-packages (from japanize-matplotlib) (3.2.2)
Requirement already satisfied: numpy>=1.11 in /usr/local/lib/python3.6/dist-packages (from matplotlib->japanize-matplotlib) (1.18.5)
Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->japanize-matplotlib) (2.4.7)
Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->japanize-matplotlib) (2.8.1)
Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->japanize-matplotlib) (1.3.1)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.6/dist-packages (from matplotlib->japanize-matplotlib) (0.10.0)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.6/dist-packages (from python-dateutil>=2.1->matplotlib->japanize-matplotlib) (1.15.0)
Building wheels for collected packages: japanize-matplotlib
  Building wheel for japanize-matplotlib (setup.py) ... done
  Created wheel for japanize-matplotlib: filename=japanize_matplotlib-1.1.3-cp36-none-any.whl size=4120275 sha256=8b8504f6d315e1a263355f065bbb0d37e887c72094b9064f89be586d8d31300c
  Stored in directory: /root/.cache/pip/wheels/b7/d9/a2/f907d50b32a2d2008ce5d691d30fb6569c2c93eefcfde55202
Successfully built japanize-matplotlib
Installing collected packages: japanize-matplotlib
Successfully installed japanize-matplotlib-1.1.3
```

## 描画の順序
1. キャンバスを描画する
2. タイトルを描画する
3. 棒グラフを描画する
4. 折れ線グラフを描画する

## キャンバスを描画する

　**キャンバスを描画する()**関数で、グラフを描画するキャンパス上に棒グラフオブジェクトと折れ線グラフオブジェクトを生成します。

　**matplotlib**ライブラリの**pyplot**オブジェクトの**subplots（）**メソッドを使います。引数に**facecolor**と**subplot_kw**を指定します。引数**facecolor**は棒グラフの表面の色を指定します。結果はキャンバスオブジェクトと棒グラフオブジェクトのタプルで返されます

```python:タプル(キャンバスオブジェクト,棒グラフオブジェクト)を取得
( キャンバスオブジェクト, 棒グラフオブジェクト ) = plt.subplots( facecolor=表面色, subplot_kw=dict(facecolor=背景色) )
```

 折れ線グラフオブジェクトを棒グラフオブジェクトの**twinx()**メソッドで取得します。**twinx()**メソッドにより、x軸が棒グラフと共通でy軸が異なる折れ線グラフを描くことができます。

```python:折れ線グラフオブジェクトを取得
折れ線グラフオブジェクトグラフ = 棒グラフオブジェクトグラフ.twinx()
```

 キャンバスオブジェクトの横幅と縦幅を**set_size_inches()**メソッドで指定します

```python:キャンバスオブジェクトの横幅と縦幅を指定
キャンバスオブジェクト.set_size_inches( キャンバスの横幅, キャンバスの縦幅 )
```

## タイトルを描画する

　**title()**メソッドで指定します

```python:タイトルを描画する（）関数
plt.title( タイトル文字列, フォントサイズ )
```

## 棒グラフを描画する

　棒グラフオブジェクトの**bar()メソッド**で棒グラフを描画します。引数にx軸データ配列, y軸データ配列, 表面色, 輪郭色, 輪郭幅を指定します。

```python:棒グラフを描画する（）関数
棒グラフ.bar( x軸データ配列, y軸データ配列, color=表面色, edgecolor=輪郭色, linewidth=輪郭幅 )
```

　棒グラフオブジェクトの**set_xlabel()メソッド**と**set_ylabel()メソッド**で棒グラフのx軸とy軸の軸ラベルを設定します。引数にx軸ラベル文字列またはy軸ラベル文字列、y軸文字色、x軸文字サイズまたはy軸文字サイズを指定します。

　ここで、x軸は棒グラフと折れ線グラフで共通ですのでx軸文字色は指定しません。指定しないと、デフォルトの黒色になります。

```python:棒グラフのx軸とy軸の軸ラベルを設定する
棒グラフオブジェクト.set_xlabel( x軸ラベル文字列, fontsize=x軸文字サイズ )
棒グラフオブジェクト.set_ylabel( y軸ラベル文字列, color=y軸文字色, fontsize=y軸文字サイズ )
```

## 折れ線グラフを描画する

　折れ線グラフオブジェクトの **plot()メソッド** で折れ線グラフを描画します。引数にx軸データ配列, y軸データ配列, 表面色, 折れ線の太さを指定します。

```python:折れ線グラフを描画する(）関数
折れ線グラフ.plot( x軸データ配列, y軸データ配列, color=表面色, linewidth=折れ線の太さ )
```

　折れ線グラフオブジェクトの **set_ylabel()メソッド** で折れ線グラフのy軸の軸ラベルを設定します。引数にx軸ラベル文字列またはy軸ラベル文字列、y軸文字色、y軸文字サイズを指定します。ここで、x軸の軸ラベルは棒グラフと同じなので設定しません。

```python:折れ線グラフのy軸の軸ラベルを設定する
折れ線グラフ.set_ylabel( y軸ラベル文字列, color=ç文字色, fontsize=y軸文字サイズ )
```
　グリッド線を表示するには**grid()**メソッドの引数に**True**を指定します

```python:グリッド線を表示する
折れ線グラフ.grid( True )
```

### 棒グラフと折れ線グラフを重ねてを描画する

　まず、キャンバスを描画する()関数より、タプル **( 棒グラフオブジェクト, 折れ線グラフオブジェクト )** を得ます。次に **タイトルを描画する()** 関数で、タイトルを表示します。さらに、 **棒グラフを描画する()** 関数と **折れ線グラフを描画する() 関数に対してそれぞれ、棒グラフオブジェクト, 折れ線グラフオブジェクトを引数に渡して実行します。

```python:グラフを描画する()関数：棒グラフと折れ線グラフを重ねてを描画する
( 棒グラフオブジェクト, 折れ線グラフオブジェクト ) = キャンバスを描画する()
タイトルを描画する()
棒グラフを描画する( 棒グラフオブジェクト )
折れ線グラフを描画する( 折れ線グラフオブジェクト )
```

## 実装コード

　下記のコード例では棒グラフが費用グラフで、折れ線グラフが件数グラフとなっています。

```python:データ登録費用推移.ipynb
import numpy as np
import pandas as pd
from matplotlib import rcParams
import matplotlib.pyplot as plt
import japanize_matplotlib

期間data = np.array(
    ['2016下半期', '2017上半期', '2017下半期', '2018上半期', '2018下半期', '201上半期', '2019下半期', '2020上半期']
)
費用data = np.array(
    [402240, 534960, 435120, 499120, 358880, 351440, 293280, 322320]
)
件数data = np.array(
    [105, 143, 126, 134, 128, 126, 122, 124]
)

キャンバス = {
    '色': {
          '背景': '#cccccc'
        , '描画領域': '#999999'
    }
    ,
    '長さ': {
          '横': 18
        , '縦': 13
    }
    ,
    'タイトル': {
          '文字列': 'データ登録費用推移'
        , 'サイズ': 30
    }
    ,
    '文字': {
        '標準サイズ': 18
    }
    ,
    '線': {
        '標準太さ': 4
    }
}
棒グラフ = {
    'COLOR': '#ff7d92'
}
折れ線グラフ = {
    'COLOR': '#5b70ff'
}

def キャンバスを描画する():
  ( fig, 費用グラフ ) = plt.subplots(
                  facecolor=キャンバス['色']['背景'],
                  subplot_kw=dict( facecolor=キャンバス['色']['描画領域'] )
              )
  件数グラフ = 費用グラフ.twinx()

  fig.set_size_inches( キャンバス['長さ']['横'], キャンバス['長さ']['縦'] )

  return ( 費用グラフ, 件数グラフ )

def タイトルを描画する():
  plt.title( キャンバス['タイトル']['文字列'], fontsize=キャンバス['タイトル']['サイズ'] )

def 棒グラフを描画する( 費用グラフ ):
  費用グラフ.bar( 期間data, 費用data, color=棒グラフ['COLOR'], edgecolor="#CC4959", linewidth=キャンバス['線']['標準太さ'] )
  費用グラフ.tick_params( labelsize=キャンバス['文字']['標準サイズ'] )
  費用グラフ.set_xlabel( "年度", fontsize=キャンバス['文字']['標準サイズ'] )
  費用グラフ.set_ylabel( "費用 (円)", color=棒グラフ['COLOR'], fontsize=キャンバス['文字']['標準サイズ'] )
  費用グラフ.spines['left'].set_color( 棒グラフ['COLOR'] )
  費用グラフ.tick_params( axis = 'y', colors =棒グラフ['COLOR'] )

def 折れ線グラフを描画する( 件数グラフ ):
  件数グラフ.plot( 期間data, 件数data, color=折れ線グラフ['COLOR'], linewidth=キャンバス['文字']['標準サイズ'] )
  件数グラフ.patch.set_alpha( 0 )
  件数グラフ.tick_params( labelsize=キャンバス['文字']['標準サイズ'] )
  件数グラフ.set_ylabel( "登録数 (件数)", color=折れ線グラフ['COLOR'], fontsize=キャンバス['文字']['標準サイズ'] )
  件数グラフ.spines['right'].set_color( 折れ線グラフ['COLOR'] )
  件数グラフ.tick_params( axis = 'y', colors=折れ線グラフ['COLOR'] )
  件数グラフ.grid( True )


def グラフを描画する():
  ( 費用グラフ, 件数グラフ ) = キャンバスを描画する()
  タイトルを描画する()
  棒グラフを描画する( 費用グラフ )
  折れ線グラフを描画する( 件数グラフ )

グラフを描画する()
```