Matplotlibでデータ可視化

# はじめに
データの分布や分析結果をグラフとして可視化することは、それらを理解する大きな助けになります。この記事では、データの可視化のために用いられるMatplotlibライブラリの基本的なところについて、備忘録として書いておきます。

# Matplotlibについて
Matplotlibは、グラフ描画用のPythonライブラリです。オブジェクト指向で、可視化するもの（プロット・軸・ラベル・凡例など）を細かく調整できます。

 - 公式ドキュメント：https://matplotlib.org/

公式ドキュメントには、さまざまなグラフとそれを描画するためのサンプルコードが見られるギャラリーがあり、データ可視化の参考になります。

 - Gallery - Matplotlib documentation: https://matplotlib.org/gallery/index.html

# データを可視化する方法
この記事では、以下の可視化方法を取り上げます。

 - ヒストグラム（histメソッド）：データの分布を可視化
 - 散布図（scatterメソッド）：データ間の相関関係を可視化
 - 折れ線グラフ（plotメソッド）：データの推移を可視化

また、複数のグラフを描画する方法についても触れます。

ここでは、UCI Machine Learning Repository (http://archive.ics.uci.edu/ml/index.php) で公開されている二つのデータセットを使います。
一つ目は、ボストン市郊外の地域別住宅価格（https://archive.ics.uci.edu/ml/machine-learning-databases/housing/ ）です。以下のコードでは、scikit-learnライブラリに付属のデータセットを読み込み、PandasのDataFrameに変換しています。（以降のコードの動作環境は、Python 3.7.3, pandas 0.24.2, matplotlib 3.0.3, scikit-learn 0.20.3 です。）

```python
from sklearn.datasets import load_boston
boston = load_boston() # データセットの読み込み

import pandas as pd
boston_df = pd.DataFrame(boston.data, columns = boston.feature_names) # 説明変数(boston.data)をDataFrameに保存
boston_df['MEDV'] = boston.target # 目的変数(boston.target)もDataFrameに追加
```
データを覗いてみると、こんな感じです。

```python
boston_df.head()
```

<img width="514" alt="boston.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/258694/7be59646-a3bb-5516-9b48-3cb4d2409351.jpeg">

各変数（データ項目）の説明は以下の通りです。

| 変数    | 説明                                 |
|:--------|:------------------------------------|
| CRIM    | 犯罪発生率                           |
| ZN      | 25,000平方フィート以上の住宅区画の割合 |
| INDUS   | 非小売業種の土地面積の割合            |
| CHAS    | チャールズ川沿いかを表すダミー変数     |
| NOX     | 窒素酸化物の濃度                     |
| RM      | 平均部屋数                           | 
| AGE     | 1940年より前に建てられた建物の割合     |
| DIS     | 5つのボストンの雇用施設への重み付き距離 |
| RAD     | 高速道路へのアクセスのしやすさ         |
| TAX     | 10,000ドルあたりの不動産税率          |
| PTRATIO | 生徒と教師の割合                     |
| B       | 黒人の割合                          |
| LSTAT   | 低所得者の割合                       |
| MEDV    | 住宅価格の中央値（1,000単位）         |

二つ目は、ガスセンサーの時系列データ（https://archive.ics.uci.edu/ml/machine-learning-databases/00309/ ）です。以下のコードでは、CSVファイルとしてダウンロードしたデータをPandasのDataFrameとして読み込んでいます。

```python
import pandas as pd
sensor_df = pd.read_csv('000_Et_H_CO_n.csv') # データセットの読み込み
sensor_df.columns = ['Time', 'Temperature', 'Relative Humidity', 'Sensor1', 'Sensor2', 'Sensor3', 'Sensor4', 'Sensor5', 'Sensor6', 'Sensor7', 'Sensor8'] # 列名を付与
```

データを覗いてみると、こんな感じです。

```python
sensor_df.head()
```
<img width="562" alt="gas_sensor.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/258694/5bbe1060-182d-5f8d-4004-5f5560a9fdaf.jpeg">

各変数（データ項目）の説明は以下の通りです。

| 変数              | 説明                     |
|:------------------|:------------------------|
| Time              | 時間（秒）               |
| Temperature       | 温度（℃）               |
| Relative Humidity | 湿度（%）                |
| Sensor1           | ガスセンサー1で取得した値 |
| Sensor2           | ガスセンサー2で取得した値 |
| Sensor3           | ガスセンサー3で取得した値 | 
| Sensor4           | ガスセンサー4で取得した値 |
| Sensor5           | ガスセンサー5で取得した値 |
| Sensor6           | ガスセンサー6で取得した値 |
| Sensor7           | ガスセンサー7で取得した値 |
| Sensor8           | ガスセンサー8で取得した値 |

## ヒストグラム：データの分布を可視化
ヒストグラムは、一つの変数（データ項目）の分布を可視化するのに役立つグラフです。matplotlibライブラリのhist()メソッドを使うことで、ヒストグラムを描画することができます。

例えば、以下のコードでは、低所得者の割合（LSTAT）の分布を描画します。hist()メソッドの引数xに描画対象とするデータを指定し、図のタイトルやx軸とy軸のラベルを指定したのち、show()メソッドを呼び出すことでヒストグラムを描画できます。

```python
import matplotlib.pyplot as plt
plt.hist(x=boston_df['LSTAT'])                         # ヒストグラムをプロット

plt.title('Distribution of LSTAT')                     # 図のタイトル
plt.xlabel('% lower status of the population [LSTAT]') # x軸のラベル
plt.ylabel('Frequency')                                # y軸のラベル

plt.show()                                             # 図の表示
```
図を見ると、低所得者の割合はおよそ0~40%の範囲にあり、10%程度である割合がもっとも大きい、などのデータの分布に関する情報が読み取れます。

![hist_plot.jpg](https://qiita-image-store.s3.amazonaws.com/0/258694/14b78de7-0c59-d2d5-da71-15ea3bb79191.jpeg)

hist()メソッドは、分布を可視化したいデータx以外の引数を指定することで描画方法を調整することができます。たとえば、

```python
plt.hist(x=boston_df['LSTAT'], bins=20)
```

とすることでヒストグラムのビンの数を20個にしたり、

```python
plt.hist(x=boston_df['LSTAT'], range=(10, 20))
```

とすることで表示する横軸の範囲を10~20にしたり、

```python
plt.hist(x=boston_df['LSTAT'], cumulative=True)
```

とすることで累積ヒストグラムを表示したりすることができます。

## 散布図：データの相関関係を可視化
散布図は、二つの変数（データ項目）の相関関係を可視化するのに役立つグラフです。matplotlibライブラリのscatter()メソッドを使うことで、散布図を描画することができます。

例えば、以下のコードでは、低所得者の割合（LSTAT）と住宅価格の中央値（MEDV）の散布図を描画します。scatter()メソッドの引数に描画対象とする二つのデータx, yを指定し、図のタイトルやx軸とy軸のラベルを指定したのち、show()メソッドを呼び出すことで散布図を描画できます。grid()メソッドは、グリッド線を表示するためのメソッドです。

```python
import matplotlib.pyplot as plt
plt.scatter(x=boston_df['LSTAT'], y=boston_df['MEDV']) # 散布図をプロット

plt.title('Scatter Plot of LSTAT vs MEDV')             # 図のタイトル
plt.xlabel('% lower status of the population [LSTAT]') # x軸のラベル
plt.ylabel('Prices in $1000\'s [MEDV]')                # y軸のラベル
plt.grid(True)                                         # グリッド線を表示

plt.show()                                             # 図の表示
```
図を見ると、大雑把な傾向として、低所得者の割合が大きいほど住宅価格は安い、などのデータ間の相関関係が見て取れます。

![scatter_plot.jpg](https://qiita-image-store.s3.amazonaws.com/0/258694/8f840b48-bb9d-8a42-695f-896003b9f354.jpeg)


## 折れ線グラフ：データの推移を可視化
折れ線グラフは、データの推移を可視化するのに役立つグラフです。matplotlibライブラリのplot()メソッドを使うことで、折れ線グラフを描画することができます。

例えば、以下のコードでは、ガスセンサー1で取得した値の折れ線グラフを描画します。plot()メソッドの引数に描画対象とする変数を指定し、図のタイトルやx軸とy軸のラベルを指定したのち、show()メソッドを呼び出すことで折れ線グラフを描画できます。

```python
import matplotlib.pyplot as plt
plt.plot(sensor_df['Time'], sensor_df['Sensor1']) # 折れ線グラフをプロット

plt.title('Transition of Sensor1 value')          # 図のタイトル
plt.xlabel('Time (s)')                            # x軸のラベル
plt.ylabel('Value')                               # y軸のラベル

plt.show()                                        # 図の表示
```

図を見ると、ガスセンサーで取得した値が時間の経過とともに変化していく様子が見て取れます。

![timeseries_plot.jpg](https://qiita-image-store.s3.amazonaws.com/0/258694/0ab8b6ef-79de-fce0-b12f-7be083de5e9e.jpeg)

## 複数のグラフ・図のプロット
show()メソッドを呼び出す前に、プロットするためのメソッドを複数回呼び出すことで、一つの図に複数のグラフを描画できます。

例えば、以下のコードでは、ガスセンサー1～4で取得した値の折れ線グラフ（計4本）を描画します。plot()メソッドを4回呼び出し、それぞれガスセンサー1～4の値を描画対象に指定しています。また、それぞれの折れ線グラフを区別するために、線の色（color）、点のマーカー（marker）、線の種類（linetype）、凡例表示のためのラベル（label）が互いに異なるものになるように引数を指定しています。図のタイトルやx軸とy軸のラベルを指定し、さらにlegend()メソッドで凡例を表示し、xlim, ylimメソッドで表示範囲を指定します。これらの設定をすべて完了したのち、show()メソッドを呼び出すことで4本の折れ線グラフを描画しています。

```python
import matplotlib.pyplot as plt
# 複数の折れ線グラフをプロット：線の色（color）・点のマーカー（marker）・線の種類（linetype）・凡例表示のためのラベル（label）でグラフを区別
plt.plot(sensor_df['Time'], sensor_df['Sensor1'], color = 'red', marker = 'o', linestyle = '-', label = 'Sensor1')
plt.plot(sensor_df['Time'], sensor_df['Sensor2'], color = 'green', marker = 'x', linestyle = '--', label = 'Sensor2')
plt.plot(sensor_df['Time'], sensor_df['Sensor3'], color = 'blue', marker = 's', linestyle = ':', label = 'Sensor3')
plt.plot(sensor_df['Time'], sensor_df['Sensor4'], color = 'orange', marker = 'v', linestyle = '-.', label = 'Sensor4')

plt.title('Transition of Sensor value') # 図のタイトル
plt.xlabel('Time (s)')                  # x軸のラベル
plt.ylabel('Value')                     # y軸のラベル
plt.legend(loc = 'upper right')         # 凡例を右上（upper right）に表示
plt.xlim([50, 51])                      # x軸方向の表示範囲を指定
plt.ylim([250, 800])                    # y軸方向の表示範囲を指定

plt.show()                              # 図の表示
```

![multi_plot.jpg](https://qiita-image-store.s3.amazonaws.com/0/258694/c05b0687-c521-dcd3-17c2-26074b2ad381.jpeg)

複数の図にグラフを描画するには、subplots()メソッドを使います。

例えば、以下のコードでは、subplots()メソッドで図を2行2列に分割し、plot()メソッドで各領域に一つずつ折れ線グラフを描画しています。ここでは、subplots()メソッドの返り値をFigureクラスのインスタンスfigとAxesクラスのインスタンスaxeとして受け取っています。（Figureクラスは図全体を保持し、AxesクラスはFigureクラスのもとで個々のグラフを保持する領域というイメージです。）こうすることで、分割された各領域は行列axeの要素として参照することができます。各領域axe[i, j]に対してplotメソッドを呼び出すことで折れ線グラフをプロットでき、グラフのタイトルや軸のラベルも指定できます。注意点としては、タイトルや軸のラベルの指定には、set_title()メソッド、set_xlabel()メソッド, set_ylabel()メソッドを使います。tight_layout()メソッドは、複数のグラフ間でタイトルやラベルが被らないように配置するためのメソッドです。

```python
import matplotlib.pyplot as plt
fig, axe = plt.subplots(2, 2) # 図を2行2列に分割

# 各領域で折れ線グラフをプロット
axe[0, 0].plot(sensor_df['Time'], sensor_df['Sensor1'], color = 'red', marker = 'o', linestyle = '-', label = 'Sensor1')
axe[0, 1].plot(sensor_df['Time'], sensor_df['Sensor2'], color = 'green', marker = 'x', linestyle = '--', label = 'Sensor2')
axe[1, 0].plot(sensor_df['Time'], sensor_df['Sensor3'], color = 'blue', marker = 's', linestyle = ':', label = 'Sensor3')
axe[1, 1].plot(sensor_df['Time'], sensor_df['Sensor4'], color = 'orange', marker = 'v', linestyle = '-.', label = 'Sensor4')

# 各グラフのタイトル・x軸のラベル・y軸のラベルを設定
axe[0, 0].set_title('Transition of Sensor1 value')
axe[0, 0].set_xlabel('Time (s)')
axe[0, 0].set_ylabel('Value')

axe[0, 1].set_title('Transition of Sensor2 value')
axe[0, 1].set_xlabel('Time (s)')
axe[0, 1].set_ylabel('Value')

axe[1, 0].set_title('Transition of Sensor3 value')
axe[1, 0].set_xlabel('Time (s)')
axe[1, 0].set_ylabel('Value')

axe[1, 1].set_title('Transition of Sensor4 value')
axe[1, 1].set_xlabel('Time (s)')
axe[1, 1].set_ylabel('Value')

plt.tight_layout() # ラベル・タイトルが被らないように配置
plt.show()         # 図の表示
```

![subplots.jpg](https://qiita-image-store.s3.amazonaws.com/0/258694/8c4d4bed-e69d-e821-18d0-9b69847c9191.jpeg)

また、以下のコードでも同様のグラフを描画することができます。ここでは、subplot()メソッドを用いており、subplot()の引数に行数、列数、グラフの番号を指定しています。

```python
import matplotlib.pyplot as plt

plt.subplot(2, 2, 1) # 2行2列の1番目のグラフ
plt.plot(sensor_df['Time'], sensor_df['Sensor1'], color = 'red', marker = 'o', linestyle = '-', label = 'Sensor1')
plt.title('Transition of Sensor1 value')
plt.xlabel('Time (s)')
plt.ylabel('Value')

plt.subplot(2, 2, 2) # 2行2列の2番目のグラフ
plt.plot(sensor_df['Time'], sensor_df['Sensor2'], color = 'green', marker = 'x', linestyle = '--', label = 'Sensor2')
plt.title('Transition of Sensor2 value')
plt.xlabel('Time (s)')
plt.ylabel('Value')

plt.subplot(2, 2, 3) # 2行2列の3番目のグラフ
plt.plot(sensor_df['Time'], sensor_df['Sensor3'], color = 'blue', marker = 's', linestyle = ':', label = 'Sensor3')
plt.title('Transition of Sensor3 value')
plt.xlabel('Time (s)')
plt.ylabel('Value')

plt.subplot(2, 2, 4) # 2行2列の4番目のグラフ
plt.plot(sensor_df['Time'], sensor_df['Sensor4'], color = 'orange', marker = 'v', linestyle = '-.', label = 'Sensor4')
plt.title('Transition of Sensor4 value')
plt.xlabel('Time (s)')
plt.ylabel('Value')

plt.tight_layout()
plt.show()
```
さらに、以下のコードでも同様のグラフを描画することができます。ここでは、add_subplot()メソッドを用いてAxeクラスのインスタンスを順次追加することで複数のグラフを描画しています。

```python
import matplotlib.pyplot as plt

fig = plt.figure()
ax1 = fig.add_subplot(2, 2, 1)
ax1.plot(sensor_df['Time'], sensor_df['Sensor1'], color = 'red', marker = 'o', linestyle = '-', label = 'Sensor1')
ax1.set_title('Transition of Sensor1 value')
ax1.set_xlabel('Time (s)')
ax1.set_ylabel('Value')

ax2 = fig.add_subplot(2, 2, 2)
ax2.plot(sensor_df['Time'], sensor_df['Sensor2'], color = 'green', marker = 'x', linestyle = '--', label = 'Sensor2')
ax2.set_title('Transition of Sensor2 value')
ax2.set_xlabel('Time (s)')
ax2.set_ylabel('Value')

ax3 = fig.add_subplot(2, 2, 3)
ax3.plot(sensor_df['Time'], sensor_df['Sensor3'], color = 'blue', marker = 's', linestyle = ':', label = 'Sensor3')
ax3.set_title('Transition of Sensor3 value')
ax3.set_xlabel('Time (s)')
ax3.set_ylabel('Value')

ax4 = fig.add_subplot(2, 2, 4)
ax4.plot(sensor_df['Time'], sensor_df['Sensor4'], color = 'orange', marker = 'v', linestyle = '-.', label = 'Sensor4')
ax4.set_title('Transition of Sensor4 value')
ax4.set_xlabel('Time (s)')
ax4.set_ylabel('Value')

plt.tight_layout()
plt.show()
```

複数のグラフを描画する方法を三つ紹介しましたが、理解しやすいものを利用すればよいと思います。

# おわりに
この記事では、データの可視化のために用いられるMatplotlibライブラリの基本的なところについて、簡単に触れました。

# 更新履歴
- （2019/10/1）hist()メソッドの主な引数についての説明の追記