※ この記事を書く際に利用したコードに関連するライブラリのバージョンは以下になります.
Python: 3.9.7
matplotlib: 3.4.2
numpy: 1.20.3
要点だけまとめ
色分けで使いたい色の数に応じた対応:
- 3~5色程度で色分けしたい: 手動でラベルから色に対応する配列を作る.
- ~20色:
matplotlib.tab10
やmatplotlib.tab20
などを使う. - 20色以上:
matplotlib.colors.CSS4_COLORS
を使う.
クラスタやカテゴリごとに色分けしたい
使う場面
以下のように,散布図の点たちをクラスタごとに(もしくは,クラスタに限らず,何らかの条件で)色分けしたいようなケースを考えます.
クラスタ数が3など小さい場合は,label2color = ['r', 'g', 'b']
のように設定して,以下のような関数を使えばよいです.
def plot_points(d, label2color):
fig, ax = plt.subplots(figsize=(8, 8))
for i in range(len(d)):
# d[i]: クラスタiに属する点[x,y]のリスト
ax.scatter(d[i][:, 0], d[i][:, 1], s=6,
c=label2color[i], label=i) # ここの引数cで色を設定している
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.legend(fontsize=12, prop={'size':24},
loc='center left', bbox_to_anchor=(1,0.5),
markerscale=10)
plt.show()
しかし,クラスタ数が多くなってくると,手動でlabel2color
のような配列を設定するのは骨が折れます.
この記事で説明すること
この記事では,任意の数の色で色分けする方法について説明します.
結論を述べると,10色~20色ほど使いたい場合,matplotlib.cm.tab10
やmatplotlib.cm.tab20
などのカラーマップが用意されているのでそれを使えばいいです.
また,それ以上の数の異なる色を使いたい場合は,matplotlib.colors.CSS4_COLORS
を使うと良いかと思います.
~20色程度の色を使いたい場合
この場合は,matplotlib.cm.tab10
やmatplotlib.cm.tab20
を使うとよいです.
例えば,上で示したコード中のax.scatter()
において,引数c=matplotlib.cm.tab20(i)
(i
はクラスタの番号)のように設定します.
すると,さきほど手動で3色分定義していた,label2color
のような配列を定義しなくても自動で色分けしてくれます.
def plot_points_tab(d):
fig, ax = plt.subplots(figsize=(8, 8))
for i in range(len(d)):
ax.scatter(d[i][:, 0], d[i][:, 1], s=6,
c=matplotlib.cm.tab20(i), label=i)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.legend(fontsize=12, prop={'size':24},
loc='center left', bbox_to_anchor=(1,0.5),
markerscale=10)
plt.show()
クラスタ数=20として点列を生成して,上の関数を利用してプロットすると以下のようになります.
20個のクラスタが異なる色で色分けされていることがわかります(結構似ている色もありますが).
20色以上程度の色を使いたい場合
先ほど使っていたmatplotlib.cm.tab20
は20色しか用意されていないので,それ以上の数の色を使いたい場合は違う方法を取らないといけません.
matplotlib.colors.CSS4_COLORS
について
そこで,今回は matplotlib.colors.CSS4_COLORS
を使う方法を紹介します.
matplotlib.colors.CSS4_COLORS
では148色の色が定義されています.
print(type(matplotlib.colors.CSS4_COLORS)) # >> <class 'dict'>
print(len(matplotlib.colors.CSS4_COLORS)) # >> 148
つまり,matplotlib.colors.CSS4_COLORS
は辞書型で148個の要素からなることがわかります.
実際の中身は,以下のように,キー = 利用できる色名,値 = 対応するカラーコード として格納されています.
{'aliceblue': '#F0F8FF', 'antiquewhite': '#FAEBD7', 'aqua': '#00FFFF', 'aquamarine': '#7FFFD4', 'azure': '#F0FFFF', 'beige': '#F5F5DC', 'bisque': '#FFE4C4', 'black': '#000000', ...
ランダムに任意の数のカラーコードを取得してリストにする
今回は,必要な数だけカラーコードを取得するために,この辞書からランダムに抽出を行います.
それを行う関数を以下に示します.
def choose_colors(num_colors):
# matplotlib.colors.CSS4_COLORSの値だけをリストにする
tmp = list(matplotlib.colors.CSS4_COLORS.values())
# リストにしたものをランダムにシャッフルする
random.shuffle(tmp)
# 必要な数だけ先頭から取り出してreturnする
label2color = tmp[:num_colors]
return label2color
この関数 choose_colors()
を使えば,自動で各ラベルに対応する色のリストを作ることができます.
つまり,冒頭で示したような,ラベルと色の対応を表すリストlabel2color
が,任意のサイズで作れることになります.
実際に,この関数 choose_colors()
と冒頭のプロット用の関数 plot_points()
を使って,クラスタ数=30でプロットしてみると以下のようになりました.
ほとんど見分けがつかないが微妙に違う色(25番と28番など)がありますが,この辺を解決するのは難しそう...
[追記]
クラスタの数が多い場合,凡例を横に出しておくと見づらいです.
以下のように,クラスタの中心にラベルを記載しておくことで多少はマシになると思います(コメント頂いた方ありがとうございました).
def plot_points_annotated(d, label2color):
fig, ax = plt.subplots(figsize=(8, 8))
for i in range(len(d)):
ax.scatter(d[i][:, 0], d[i][:, 1], s=6,
c=label2color[i], label=i)
ax.set_xlabel('x')
ax.set_ylabel('y')
# 各クラスタの中心にラベルを記載
ax.annotate(i,
xy=tuple(np.mean(d[i], axis=0)), fontsize=10, color='k',
bbox={'facecolor':label2color[i], 'edgecolor':'k', 'alpha':0.8})
plt.show()
ちなみに148色以上使いたい場合(そんなことなかなかなさそうですが)は,matplotlib.colors.XKCD_COLORS
というものがあり,こちらは949色使えるそうです.