Matplotlibのデフォルト配色

Pythonのグラフ描画パッケージであるmatplotlibは便利なツールである。 僕は最近、論文やプレゼン用のグラフ作成はもっぱらmatplotlibで行っている。

ただ、matplotlibのデフォルトの配色はちょっと気に食わない。

import matplotlib.pyplot as plt
for i in range(15):
   plt.plot( [0,3] , [-i,-i] , lw=4 , label=str(i) )
plt.xlim(0,10)
plt.legend( ncol=2 , fontsize=16 )
plt.show()

でプロットした結果は以下のような具合である。

これについて思うこととして、

  1. 色が全体的に優しすぎる。学術研究のグラフで最も重要なのは結果を明瞭に伝えることなので、もっと強い色を使いたい。
  2. 好みの問題だが、赤と青は最初に使いたい。もっとも、原色である必要はない。微妙な中間色の方が今風でかっこいい気がする。
  3. 色がやや薄い。白色背景のグラフで視認性を高めるため、もっと濃い色を使いたい。

といった不満が挙げられる。

配色を変更する一つの手段として、plt.plot()などによる線や点の描画の都度、 引数colorで色を指定することも可能だが、 デフォルトの配色を上の不満を解消したものにしておけばいちいち指定する手間が省ける。 そこで僕は、以下のような手順で変更したデフォルト配色を使っている。

まず、matplotlibのデフォルト配色は、matplotlibの各種デフォルト値を記録したplt.rcParamsの中の

plt.rcParams['axes.prop_cycle']

に記録されており、デフォルト値は、

>>> import matplotlib.pyplot as plt
>>> plt.rcParams['axes.prop_cycle']
cycler('color', ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf'])

となっている。

この変数を変更すればデフォルト配色が変更できる。 例として、

import matplotlib.pyplot as plt
from cycler import cycler

plt.rcParams['axes.prop_cycle'] = cycler('color' , ['red','green','blue'])
for i in range(15):
   plt.plot( [0,3] , [-i,-i] , lw=4 , label=str(i) )
plt.xlim(0,10)
plt.legend( ncol=2 , fontsize=16 )
plt.show()

とすれば以下のように赤、緑、青が順番に使われるようになる。

これで好きな色が指定できるのだが、このようにして色を何種類も指定するのは骨が折れるし、 色の素人が配色を決めてもろくなことにならないので、何らかの既存の配色を使いたい。 そこで、そのような配色としてmatplotlibの中に入っているカラーマップなるものを拝借することにする。 カラーマップは matplotlib.org あたりに一覧があるので眺めてみると、 僕の場合はSet1というやつが最も好みに近い。 ちなみに、デフォルト配色はこの中のtab10というやつである。

カラーマップやcyclerの使い方はなかなかに難しいのだが、

plt.rcParams['axes.prop_cycle'] = cycler( color=[plt.get_cmap('Set1')(i) for i in range(9)])

とすればSet1の配色全9色をmatplotlibのデフォルトに指定できる。

import matplotlib.pyplot as plt
from cycler import cycler

plt.rcParams['axes.prop_cycle'] = cycler( color=[plt.get_cmap('Set1')(i) for i in range(9)])
for i in range(15):
   plt.plot( [0,3] , [-i,-i] , lw=4 , label=str(i) )
plt.xlim(0,10)
plt.legend( ncol=2 , fontsize=16 )
plt.show()

の結果が、以下のようなものである。

これを見ると、6色目以降の黄色やピンクは視認性が悪くていまいちだが、 最初の5色の配色は僕のイメージにかなり近い。 しかし残念ながら、これらの5色も微妙な中間色でやや薄いので、この配色で論文用のグラフを作ってみると全体的に視認性が低いことに気がつく。 従って、もう少し濃くてはっきりした色が使えればそれに越したことはない。

その一方でSet1の配色自体は悪くないので、ここではSet1をベースにして、 この配色を全体的に濃くすることにする。 そんなことができるのかというと、以下で説明するようにいとも簡単にできてしまうのがPythonの素晴らしいところである。

まず、「色を濃くする」という操作を行うには、RGBではなくHSV (色相、彩度、明度)で色を取得して、 明度(V)の値を調整すればよい。 一方、カラーマップの値はRGBで与えられているので、やるべきこととしては、

  1. カラーマップSet1の各要素のRGB値をHSVに変換して、
  2. 明度(と彩度)を適宜調整したうえで
  3. その色を再びRGBに戻す、
  4. という操作を行ったcyclerなリストを作成し、
  5. このリストをplt.rcParams['axes.prop_cycle']に代入する、

ということになる。 こう書くと一見複雑だが、この中のRGBとHSVの交互の変換という操作が colorsysというパッケージの関数colorsys.rgb_to_hsv()colorsys.hsv_to_rgb()を使うことで それぞれ一行に実現できるあたりがさすがPythonである。

以上の操作を行っているのが以下のコードである。

import colorsys
clist = []
for i in range(8):
    c = colorsys.rgb_to_hsv( *plt.get_cmap('Set1')(i)[:3] )
    c = colorsys.hsv_to_rgb( c[0] , 1.-(1.-c[1])*.2 , c[2]*.85 )
    clist.append( c )
plt.rcParams['axes.prop_cycle'] = cycler( color=clist )

matplotlibによるプロット操作の前にこのコードを挿入しておけばデフォルト配色が変更される。 こうして作った配色によるプロットが以下である。

なお、上の調整ではHSVの第2引数である彩度を上げて(1に近づけて)、 第3引数の明度を0.85倍に小さくしてあるのだが、このあたりはいろいろ試して決めたものである。

この設定で、僕としては5色目までは全く不満がなくなった。 6色目以降はやや不満だが、一つのグラフで6種類以上の色を使うことはまずないし、 そんな場合にはそれこそ配色を真面目に検討して個別にcolorを指定するべきなので、 デフォルト配色としてはこれで十分である。

なお、カラーサイクルを最初の5色のみで回すという選択肢も考えられ、 その場合は上のコードの3行目の'range(8)'を'range(5)'にすれば良い。

この配色を使ったグラフの作例は、

inspirehep.net

のFigs. 6, 8, 10-13などである。

僕は、matplotlibの各種設定をまとめて1つのpyファイルに書いておいて グラフ描画の際に読み込むことにしており、上の配色設定もこのファイルに書いてある。

僕の場合、これでmatplotlibの配色に関する不満はほぼ完全に解消された。