Claude Codeで色覚に配慮したカラーマップを選ぶ — jetからviridisへ置換して検証
matplotlibの図でjetをviridisなどに置き換え、グレースケール印刷と色覚シミュレーションで判別できるかをClaude Codeに測らせる手順です。
論文やダッシュボードの図は、カラーマップを jet から viridis のような明度が単調に変わるものへ替え、グレースケールと色覚シミュレーションで読めるかを測れば、色覚多様性への対応として筋が通ります。Claude Codeには、既存スクリプトの cmap 指定を洗い出させる作業と、明度を数値で測る検証スクリプトの実行を任せられます。
見た目の良し悪しをClaudeに判断させると、同じ図でも答えがぶれます。そこで、判断は数値に寄せます。この記事では、matplotlibの公式解説が示す基準を検証コードに落とし、置換から確認までを一続きの手順にします。
何を直すのか — jetが読みにくい理由を数値で見る
matplotlibの公式解説は、多くの用途で知覚的に均一なカラーマップが最適だと説明しています。人の脳はデータの変化を、色相よりも明度の変化として受け取りやすいため、明度が単調に増えていくカラーマップのほうが正しく読まれるからです。
この基準は、手元のカラーマップで数値にできます。公式解説がグレースケール変換の節で使っているのと同じ、CAM02-UCS色空間の明度L*を計算します。以下は、Claude Codeに書かせて実行した確認用スクリプトです。
python -m venv .venv && . .venv/bin/activate
pip install matplotlib colorspacious# cmap_check.py — カラーマップの明度(L*)を調べる
# 使い方: python cmap_check.py sequential viridis cividis
# python cmap_check.py diverging coolwarm
import sys
import numpy as np
import matplotlib as mpl
from colorspacious import cspace_convert
kind, names = sys.argv[1], sys.argv[2:]
x = np.linspace(0, 1, 256)
for name in names:
rgb = mpl.colormaps[name](x)[:, :3]
L = cspace_convert(rgb, "sRGB1", "CAM02-UCS")[:, 0]
if kind == "sequential":
ok = bool((np.diff(L) >= -0.5).all()) # 明度が一方向に増える
else:
# 両端の明度が近く、最大が中央にある
ok = bool(abs(L[0] - L[-1]) < 5 and 0.4 < x[L.argmax()] < 0.6)
print(f"{name:9s} L* {L[0]:5.1f} -> {L[-1]:5.1f} "
f"min {L.min():5.1f} max {L.max():5.1f} {kind}_ok={ok}")連続型(Sequential)と発散型(Diverging)では、合格の条件が違います。連続型は明度が一方向に増えること、発散型は両端の明度がほぼ等しく、中央で最大になることです。matplotlib 3.11.2で python cmap_check.py sequential jet turbo viridis cividis gray と python cmap_check.py diverging coolwarm を実行した出力は次のとおりです。
jet L* 14.3 -> 29.8 min 14.3 max 96.1 sequential_ok=False
turbo L* 15.6 -> 28.8 min 15.6 max 90.8 sequential_ok=False
viridis L* 18.6 -> 92.4 min 18.6 max 92.4 sequential_ok=True
cividis L* 16.0 -> 92.7 min 16.0 max 92.7 sequential_ok=True
gray L* 0.0 -> 100.0 min 0.0 max 100.0 sequential_ok=True
coolwarm L* 38.7 -> 43.1 min 38.7 max 89.6 diverging_ok=Truejet と turbo は、始点と終点がどちらも暗い色で、途中で明るくなってまた暗くなります。連続量に使うと、値の大小がそのまま明るさの大小にならないので、白黒で刷ると高い値と低い値が同じ灰色に化けます。viridis と cividis はL*が90前後まで一方向に伸びます。
数値で見る置換の根拠
jetとturboは明度が途中で折り返し、連続型としてはsequential_ok=Falseになるviridisとcividisは明度が単調に増え、グレースケールでも順序が残るcoolwarmは両端の明度がほぼ同じ(38.7と43.1)で、灰色にすると青側と赤側の区別がつかない。中央だけが明るくなる
coolwarm のような発散型は、中央を基準に両側へ振れるデータのために作られています。中央で明るく両端で暗くなるので、連続型の判定では必ず単調でなくなります。発散型で見るのは、両端の明度が近いことと、最大が中央にあることです。coolwarm は中央で89.6まで上がりますが、両端は38.7と43.1で、明度の振れ幅が小さめです。公式解説も、これを印刷すると詳細が失われると述べています。発散型を使うなら、等高線にラベルを重ねて、色以外の手がかりを足します。
データの性質でカラーマップの種類を決める
viridis に統一すれば済むわけではありません。公式解説は、カラーマップを役割で4つに分けています。
データの性質とカラーマップの種類
順序のある量
温度や密度のように大小がある値には、明度が段階的に変わる連続型(Sequential)を使います。
viridisはここに入ります。中央に意味のある量
地形の標高やゼロ付近で振れる差分には、2色が中央の無彩色で交わる発散型(Diverging)を使います。
端がつながる量
位相角、風向、時刻のように終点が始点に戻る値には、循環型(Cyclic)の
twilightが候補です。hsvは明度の変動が大きく、知覚的な用途には向かないと公式解説は述べています。順序のないカテゴリ
質的型(Qualitative)は知覚的な連続性を目的にしておらず、
Accentのように明度が行き来します。連続量の表現には使えません。
洗い出しの表に「この図のデータはどの種類か」の列を足して、Claudeに埋めさせてから置換させると、風向の図に viridis を当てて端の不連続を作るような取り違えを防げます。
Claude Codeに洗い出させる — cmapの指定箇所を一覧にする
置換の前に、リポジトリのどこで色が決まっているかを把握します。cmap= の直接指定だけでなく、rcParams["image.cmap"] や、plt.cm.jet のような属性参照、スタイルファイルの設定も対象です。
次のように依頼します。
リポジトリ内で matplotlib のカラーマップを指定している箇所を全部洗い出して。
cmap=、plt.cm.*、image.cmap、スタイルファイルを対象にして、
ファイルパス・行番号・カラーマップ名・何のデータに使っているかを表にして。
まだ編集はしないで。表ができたら、各行を用途で3つに分けます。
| 用途 | 例 | 置換の方針 |
|---|---|---|
| 連続量(温度・密度・強度) | 例ヒートマップ、等高線図 | 置換の方針明度が単調な viridis などへ |
| 中央を基準に両側へ振れる量 | 例差分、残差 | 置換の方針発散型を使い、ラベルや等高線を併用 |
| 離散カテゴリ | 例系列ごとの折れ線、棒の色分け | 置換の方針色だけに頼らず、線種やマーカーを併用 |
カテゴリの色分けは、連続カラーマップへ替えても解決しません。色相で系列を見分けさせる図では、線種やマーカー、直接ラベルのように色以外の区別も足したほうが確実です。この3分類を先に決めておくと、Claudeが一律に viridis へ置換して離散データの図を壊す事態を避けられます。
規約をCLAUDE.mdに書いて検証コマンドを固定する
置換のたびに基準を言い直さずに済むよう、プロジェクトの CLAUDE.md に規約を置きます。Claudeは CLAUDE.md を強制される設定ではなくコンテキストとして読むため、検証できる書き方にしておくほうが守られやすくなります。
## 図のカラーマップ規約
- 連続量は viridis / cividis など明度が単調に増えるものを使う。jet / turbo / hsv / rainbow は使わない
- 赤と緑を同じ図で対比させない(赤緑の色覚特性で区別できなくなる)
- 図を変更したら `python tools/cmap_check.py sequential <連続型のcmap名>` を実行し、
sequential_ok=True を確認してから完了とする
- 発散型は `python tools/cmap_check.py diverging <cmap名>` で、
diverging_ok=True(両端の明度が近く、中央が最大)を確認する
- 発散型(coolwarm 等)を使う図は、等高線ラベルか数値注記を必ず付ける赤緑を避ける規約は、公式解説の記述に沿っています。色覚特異の最も一般的な型は赤と緑の区別に関わるため、赤と緑の両方を含むカラーマップを避けると、多くの問題を防げます。
置換を実行させる — jetからviridisへ
洗い出しと規約がそろったら、置換を依頼します。
連続量の図だけ、jet を viridis に置換して。
離散カテゴリの図と発散型の図は触らない。
置換したら cmap_check.py を sequential で実行して、結果を貼って。差分は小さくなります。
-im = ax.imshow(field, cmap="jet")
+im = ax.imshow(field, cmap="viridis")
fig.colorbar(im, ax=ax, label="temperature [K]")カラーバーのラベルは変えません。置換後に図の見え方が変わるので、論文なら本文中の「赤い領域」「青い領域」といった色で指す表現も読み直す必要があります。この確認はClaudeに別途頼みます。「本文と図のキャプションで色名を使っている箇所を挙げて」と依頼すれば、書き直す候補を拾えます。
グレースケールで判別できるかを測る
公式解説は、カラー図が白黒プリンターで刷られる場合があるため、グレースケール変換に注意が必要だと述べています。変換の方法は複数あり、知覚に合わせた重みづけをした線形結合や、L*を使う非線形の方法が挙げられています。
目視ではなく、実際にグレースケール画像として書き出して確かめます。
# gray_preview.py — 図をグレースケールにして保存する
import sys
from PIL import Image
src, dst = sys.argv[1], sys.argv[2]
Image.open(src).convert("L").save(dst)python gray_preview.py fig_before.png fig_before_gray.png
python gray_preview.py fig_after.png fig_after_gray.pngClaudeは画像ファイルを読めるので、「2枚のグレースケール画像を見比べて、値の高い領域と低い領域が区別できるか答えて」と依頼できます。ただし、これは補助の確認です。判別できるかの根拠は、前節のL*のほうに置いてください。画像を読んだ感想は、同じ図でも答えが変わりえます。
公式解説は、autumn、spring、summer、winter はグレースケールでの変化が小さいと述べています。名前から暖色や寒色の連続的なカラーマップに見えても、明度の幅は別の話です。
色覚シミュレーションで確認する
グレースケールは明度だけを見るので、色相の混同は別に確かめます。colorspaciousライブラリには、色覚特異を模擬する変換が用意されています。deuteranomaly(最も一般的な赤緑型で、一部の男性に見られる)、protanomaly、tritanomalyを指定でき、severityは0から100で、100が完全な二色型色覚に当たります。
# cvd_preview.py — 色覚シミュレーション後の画像を保存する
import sys
import numpy as np
from PIL import Image
from colorspacious import cspace_convert
src, dst, kind = sys.argv[1], sys.argv[2], sys.argv[3]
rgb = np.asarray(Image.open(src).convert("RGB")) / 255.0
space = {"name": "sRGB1+CVD", "cvd_type": kind, "severity": 100}
out = np.clip(cspace_convert(rgb, space, "sRGB1"), 0, 1)
Image.fromarray((out * 255).astype(np.uint8)).save(dst)for k in deuteranomaly protanomaly tritanomaly; do
python cvd_preview.py fig_after.png "fig_after_$k.png" "$k"
done変換は「CVD空間から標準sRGB空間へ」という向きで書きます。ある色覚で見たときに同じに見える色を、通常の色覚でも同じに見えるRGBへ写す、という考え方です。
シミュレーション画像は、カラーバーと本体を並べてClaudeに読ませ、「カラーバーの両端と中間の色が互いに区別できるか」を答えさせます。この判断も参考値です。確証が要る図は、色覚特異のある同僚や共著者に実際に見てもらってください。
検証をスキルにまとめて使い回す
図を作るたびに3つのスクリプトを呼ぶ手間は、スキルにまとめると減ります。Claude Codeのスキルは、.claude/skills/<スキル名>/SKILL.md に置くと、そのリポジトリのセッションで使えます。個人用なら ~/.claude/skills/<スキル名>/SKILL.md で、全プロジェクトから使えます。frontmatterの description は、Claudeがスキルを自動で読み込むかの判断材料になります。
---
description: matplotlib の図のカラーマップを検証する。図を作成・変更したとき、またはカラーマップの色覚対応を聞かれたときに使う。
---
## 手順
1. 変更した図で使っているカラーマップ名を列挙する
2. 連続型は `python tools/cmap_check.py sequential <名前...>`、発散型は `diverging` で実行し、`_ok=False` のものを報告する(循環型・質的型は対象外)
3. 図のPNGに対して gray_preview.py と cvd_preview.py を実行する
4. 判別できない箇所があれば、cmap の変更案を示す(編集は確認を取ってから)スキル機構そのものはClaude Codeのdatavizスキルの解説で扱っています。そちらは配色の検証スクリプトを含むバンドルスキルの話で、この記事のスキルは、matplotlibに絞った自作の検証手順です。
検証でつまずきやすい点
- カラーバーの端が切れている:
vminとvmaxを狭く指定すると、カラーマップの片端しか使われません。L*を調べても、実際に使う範囲で足りているかは別問題です。 - 離散データに連続カラーマップを当てる: 系列の数だけ等間隔に色を取り出す書き方でも、隣り合う系列の差は小さくなります。線種やマーカーも併用します。
- 画像の埋め込み時の色変換: 投稿先によっては、PDF化の段階でカラープロファイルが変わります。投稿規定に合わせた出力設定は論文図のdpiとフォントを揃える手順に任せ、色の検証は、最終形式の画像でもう一度行います。
- シミュレーションを過信する: シミュレーションは典型的な型の近似です。個人差があるので、通過したからといって誰にでも読めるとは言えません。
Claude Code自体の端末表示の配色は、themeコマンドの色覚多様性対応テーマで別に設定します。図の色とは設定の場所が違います。
まとめ
色覚への対応は、カラーマップの好みの問題として扱うと決着がつきません。L*の単調性、グレースケール変換、色覚シミュレーションの3つを、数値と画像の出力にして残すと、置換が妥当だったことを後から示せます。
Claude Codeに頼むのは、指定箇所の洗い出し、用途別の置換、そして検証スクリプトの実行と結果の報告です。どのカラーマップを選ぶかの前提(連続量か、中央を基準にするか、離散か)は、図の意味を知っている人が先に決めておくと、置換の手戻りが減ります。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。