【Python】matplotlibによるグラフ描画時のColormapのカスタマイズ

[Adventar](http://www.adventar.org)の[Python Advent Calendar 2015](http://www.adventar.org/calendars/846) 21日目の記事です。

Pythonでグラフを描く時、[Matplotlib](http://matplotlib.org)を使うと思います。また最近は[Seaborn](http://stanford.edu/~mwaskom/software/seaborn/)というグラフを綺麗にしてくれるライブラリがあり、自分はそれを愛用しています。ただ、色をもっと自由に選びたい、設定したいという時に＋αでColormapのカスタマイズをすると便利です。今回はこれを紹介します。

#準備

まずはいつもの一式インポートです。大体Anacondaにあるものですが、足りないものは `pip install ＜入れたいライブラリ名＞`で入れることができます。

```py 
import numpy as np
import pandas as pd
from sklearn import datasets

import matplotlib.pyplot as plt
import matplotlib.cm as cm
%matplotlib inline
import seaborn as sns

sns.set(style="darkgrid", palette="muted", color_codes=True) 
```

お試しに使うのは、いつものirisデータセットです。

```py 
# irisデータのロード
iris = datasets.load_iris()
```

そのまま素直に、irisの種別ごとに色分けして散布図を描くとこんな感じで、白黒になってしまいます・・・。

```py 
# 散布図の表示（色が白黒になってしまう・・・）
plt.figure(figsize=(10,7))
plt.scatter(iris.data[:,0], iris.data[:,1], linewidths=0, alpha=1,
            c=iris.target   # iris.targetには種別を表す[0, 1, 2]が入っているのでそれで色分け
           )
plt.show()
```

![plot_01.png](https://qiita-image-store.s3.amazonaws.com/0/50670/31992ff7-bbd3-2e16-6ed4-3f18c0b52159.png)

引数`c`に個別に色の名前を指定すると色を指定することが出来ます。でもあんまりスマートじゃない感じがします。

```py 
# 散布図の表示（ひとつずつ色を指定する）
def set_color(l):
    if l == 0:
        return "b"  # blue
    elif l == 1:
        return "g"  # green
    else:
        return "r"  # red
    
color_list = map(set_color, iris.target)

plt.figure(figsize=(10,7))
plt.scatter(iris.data[:,0], iris.data[:,1], linewidths=0, alpha=1,
            c=iris.target   # iris.targetには種別を表す[0, 1, 2]が入っているのでそれで色分け
           )
plt.show()
```

![plot_02.png](https://qiita-image-store.s3.amazonaws.com/0/50670/eba68ae2-1cad-cbb4-0dfb-9a324751ca26.png)

種別ひとつずつに色を指定するのも大変なので、元々Matplotlibで定義されているカラーマップを使うこともできます。カラーマップについては[ここ](http://matplotlib.org/examples/color/colormaps_reference.html)を参考にすると様々なカラーマップの定義を見ることができます。

```py 
# 定義されているカラーマップを適用する
# 参考： http://matplotlib.org/examples/color/colormaps_reference.html

fig = plt.figure(figsize=(13,7))
im = plt.scatter(iris.data[:,0], iris.data[:,1], c=iris.target, linewidths=0, alpha=1, 
                 cmap=cm.Accent # ここでカラーマップを指定
                )
fig.colorbar(im)
plt.show()
```

でもちょっと欲しい色にバッチリ合わせるのが難しいですね・・・。
![plot_03.png](https://qiita-image-store.s3.amazonaws.com/0/50670/fcb1090d-1cf5-a83c-cd4c-7fd059df60e4.png)

# Colormapのカスタマイズ

そこで、自分でこのカラーマップをカスタマイズして定義してみます。
色名もしくは16進のカラーコードをリストで指定すると、間を滑らかに線形補完しながらカラーマップを作ってくれます。日本語でうまく説明できないので、使用例を見てみましょう。

```py 
# colormapをカスタマイズする
from matplotlib.colors import LinearSegmentedColormap

def generate_cmap(colors):
    """自分で定義したカラーマップを返す"""
    values = range(len(colors))
    
    vmax = np.ceil(np.max(values))
    color_list = []
    for v, c in zip(values, colors):
        color_list.append( ( v/ vmax, c) )
    return LinearSegmentedColormap.from_list('custom_cmap', color_list)
```

irisデータは3種類なので、3つの色を指定します。使える色名称については、[ここ](http://matplotlib.org/examples/color/named_colors.html)を参考にすると一覧で見ることができます。

```py 
# 色をカスタマイズ, その1:色名称で指定
# 参考： http://matplotlib.org/examples/color/named_colors.html
unique_value = set(iris.target)
print unique_value
# --> [0, 1, 2]

cm = generate_cmap(['mediumblue', 'limegreen', 'orangered'])

fig = plt.figure(figsize=(13,7))
im = plt.scatter(iris.data[:,0], iris.data[:,1], c=iris.target, linewidths=0, alpha=.8, cmap=cm)
fig.colorbar(im)
plt.show()
```
![plot_04.png](https://qiita-image-store.s3.amazonaws.com/0/50670/0131a5a2-ceef-e3a2-27bc-601c627c645e.png)


色の名称ではなく、16進表現で指定することもできます。16進表現のカラーコードは[こちら](http://www5.plala.or.jp/vaio0630/hp/c_code.htm)が参考になりました

```py 
# 色をカスタマイズ, その2：16進で指定
# http://www5.plala.or.jp/vaio0630/hp/c_code.htm

cm = generate_cmap(['#87CEEB', '#2E8B57', '#F4A460'])

fig = plt.figure(figsize=(13,7))
im = plt.scatter(iris.data[:,0], iris.data[:,1], c=iris.target, linewidths=0, alpha=.8, cmap=cm)
fig.colorbar(im)
plt.show()
```
![plot_04-2.png](https://qiita-image-store.s3.amazonaws.com/0/50670/8313008d-8a9c-31bd-75c6-79202aa6cbc4.png)


淡い色を指定した時は、背景がグレーよりも白の方が見やすいです。seabornで変更できるので背景を白にしてみます。

```py 
# 色をカスタマイズ, その3：背景を白くする
sns.set(style="whitegrid", palette="muted", color_codes=True)

cm = generate_cmap(['#87CEEB', '#2E8B57', '#F4A460'])

fig = plt.figure(figsize=(13,7))
im = plt.scatter(iris.data[:,0], iris.data[:,1], c=iris.target, linewidths=0, alpha=.8, cmap=cm)
fig.colorbar(im)
plt.show()
```

![plot_05.png](https://qiita-image-store.s3.amazonaws.com/0/50670/29f9e891-fe9f-b843-87ee-53a63a6e3834.png)

次に、平面座標(2つの変数)を引数に取る関数の値を色で表現してみます。カラーマップのカスタマイズはこういったケースでとても有効ですね。

```py 
# 滑らかな塗りつぶし
n = 501
X, Y = np.meshgrid(np.linspace(0, 1, n), np.linspace(0, 1, n))
Z = np.sin(X*30) + np.cos(Y*30)
print np.min(Z), np.max(Z)

cm = generate_cmap(['#00008B', '#aaaaab', '#FFFFFF', '#F4D793', '#F4A460'])

fig =plt.figure(figsize=(10,8))
im = plt.pcolor(X, Y, Z, cmap=cm)

fig.colorbar(im)
plt.xlim(0, 1)
plt.ylim(0, 1)
plt.show()
```

![plot_06.png](https://qiita-image-store.s3.amazonaws.com/0/50670/9775a1f6-c85e-bdad-c464-6647a0b7737a.png)

最後に、等高線の高さを色で表現してみる例です。こちらもカラーマップでグラデーション含め指定できることがとても有効です。

```py 
# 等高線
n = 201
X, Y = np.meshgrid(np.linspace(0, 1, n), np.linspace(0, 1, n))
Z = np.sin(X*20) * np.cos(Y*20)

cm = generate_cmap(['indigo', 'white', 'salmon'])

fig =plt.figure(figsize=(10,8))
interval = [i/10. -1 for i in range(20)]
im = plt.contour(X, Y, Z, interval, alpha=0.5, cmap=cm)

fig.colorbar(im)
plt.xlim(0, 1)
plt.ylim(0, 1)
plt.show()
```

![plot_07.png](https://qiita-image-store.s3.amazonaws.com/0/50670/541e2c62-0831-55c4-2aa0-ab3f0aee7d25.png)



## 本記事のコード
GitHubにコードを掲載しています。
https://github.com/matsuken92/Qiita_Contents/blob/master/General/Matplotlib_color_settings.ipynb


#参考
Matplotlib colormaps reference
　http://matplotlib.org/examples/color/colormaps_reference.html
Making a custom colormap using matplotlib in python (stackoverflow)
　http://stackoverflow.com/questions/24997926/making-a-custom-colormap-using-matplotlib-in-python
名前が定義されている色一覧
　http://matplotlib.org/examples/color/named_colors.html
16進カラーコード
　http://www5.plala.or.jp/vaio0630/hp/c_code.htm