論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
カラースケール
Color Scale
可視化

🔖 キーワード索引

#連続/発散/質的#色覚多様性#Viridis#ColorBrewer#ヒートマップ#色覚バリアフリー

color scale」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「color scale」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。

color scale統計分析SSDSE-B-2026前提条件適用範囲落とし穴関連手法Python 実装検証方法

これらのキーワードは「color scale の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。

💡 30秒で分かる結論

🍰 まずはやさしく

数字を色に置き換えるルールです。

データの大きさを直感的に伝えるために使います。

スマホの天気予報の色の塗り分けのようなものです。

おすすめの色使いや注意点を学びます。

カラースケール:値を色で表現するスケールの設計

📍 文脈ボックス

🍰 まずはやさしく

データを絵にするための道具の一つです。

グラフを正しく見せるために使います。

部活の成績やテストの結果を色で分ける時に役立ちます。

定義から使い方のコツまで順番に解説します。

この用語は 可視化 カテゴリに属します。 関連する別称・略号:(なし)

論文・実務レポートで カラースケール が登場したら、 まず本ページの「30秒で分かる結論」と「直感で掴む」を読めば、 その文脈で何を言っているか把握できます。

本ページでは「color scale」を扱う。 統計データ分析コンペティション (2026) の教材で、 SSDSE-B-2026 (47 都道府県 × 複数年 × 100 超列) の実データを使った再現可能な学習を目指す。

「color scale」は統計・データサイエンスの体系における重要概念のひとつ。 本ページは「定義・直感・数式・実装・落とし穴・関連手法」の 6 視点で構成され、 各視点は独立して読めるが順序通り読むと体系的な理解が得られる。

🎨 直感で掴む

🍰 まずはやさしく

色の塗り絵のような仕組みです。

パッと見てデータの違いを分かるようにします。

地図で人口が多い県を濃い色にする例があります。

データの種類に合わせた3つの色の選び方を学びます。

日本地図で都道府県別の人口を色塗りする。 大きい県を濃く、 小さい県を薄く ── これが カラースケール。 「青→緑→黄→赤」のような連続色を使うと一目で大小が伝わる。 但し、 色覚障害がある人にも判別可能か、 印刷で白黒になっても順序が保てるか、 などの配慮が必要。

🎨 カラースケールの三大分類を徹底理解する

カラースケール (color scale) は、 大きく 3 タイプに分類できます。 「データの性質」と「カラースケールの種類」を取り違えると、 グラフは読み手を盛大に誤誘導します。 まずはこの 3 分類を頭に叩き込みましょう。

分類 適するデータ 代表的パレット 設計の本質
Sequential (連続) 0 から大へ単調増加。 人口、 所得、 気温など順序のある量 viridis、 plasma、 inferno、 cividis、 Blues、 YlOrRd 明度を単調に変化させ、 グレースケール変換しても順序が保たれる
Diverging (発散) 中央値 (0 や平均) を境に正負・大小が両側に存在 RdBu、 BrBG、 PiYG、 coolwarm、 Spectral 中央色 (白・淡黄) を「基準値」とし、 両端に対比色を配置
Qualitative (定性) 順序なしのカテゴリ。 地域名、 商品カテゴリ Set1、 Set2、 Set3、 Paired、 Accent、 tab10 色相 (hue) を均等配分し明度はそろえる。 順序を示唆しない
💬 直感的に: 「気温 = sequential」「気温偏差 = diverging」「気象警報の種類 = qualitative」と覚えると混乱しません。 SSDSE-B-2026 では「総人口 = sequential」「人口増減率 = diverging」「地方区分 = qualitative」が典型例です。

数式を言葉で読み解く ── 知覚的均等性 (perceptual uniformity) とは

知覚的均等性とは「データ上で同じ差 (例: 100 万人差) に対して、 色空間上の知覚距離 ΔE が常に同じになる」という性質です。 数式で書くと、 値 $v_1, v_2$ に対する色 $c(v_1), c(v_2)$ について

$$\Delta E_{\text{CIE76}}(c(v_1), c(v_2)) \approx k \cdot |v_1 - v_2|$$

定数 k で線形に保たれることを意味します。 viridis や cividis はこの性質を満たすよう、 CIELAB 色空間で明度 (L*) が線形に変化するよう設計されています。 一方、 古典的な jet や rainbow は緑付近で明度が突出 (バンディング) し、 ΔE が不均一です ── これが「ジェットの罠」と呼ばれる現象です。

🎨 補遺 — perceptual uniform カラーマップの具体的選び方

SSDSE-B-2026 の 「総人口 (A1101)」「出生数 (A4101)」「高齢化率 (A1303/A1101)」 をヒートマップや choropleth で示すとき、 どの cmap を選ぶかで読み手の印象が変わる。 ここでは「データの型 × 目的」で cmap を選ぶ実務指針をまとめる。

データ型推奨 cmapSSDSE-B 例
単方向の量 (0 から増加)viridis / plasma総人口、 出生数、 婚姻件数
中央値からの偏差 (発散)RdBu_r / coolwarm全国平均との差 (人口、 失業率)
順序カテゴリYlOrRd / Blues高齢化率の 5 段階区分
名義カテゴリtab10 / Set28 地方区分 (北海道, 東北, …)

colorblind チェックの実務手順: matplotlib の viridis は CVD 安全だが、 自作カラーマップを使う場合は ① colorspacious.cspace_convert で deuteranopia 変換 ② 結果を保存して隣に並べて目視比較 ③ 主要 3 色がすべて識別可能か確認、 の 3 ステップを踏むこと。 SSDSE-B-2026 の総人口の choropleth では「東京 (最大値) と鳥取 (最小値)」の両極端が CVD 後も明確に区別できるかを最優先でチェックする。

jet を避けるべき理由 (再掲・要点): jet は明度が非単調 (黄色付近で最大、 青と赤で最小) なため、 グレースケール変換すると「中央値が最も明るい」 → 印刷時に値の順序が逆転して読める。 SSDSE-B の高齢化率を jet で示し白黒コピーすると「高齢化率 30% と 18% が同じ色に見える」事故が起きる。 viridis なら明度が単調増加なので白黒でも順序が保たれる。

🎨 補遺 R447 — SSDSE-B-2026 を題材にしたカラースケール選択の総合ガイド

この補遺では、 SSDSE-B-2026 (47 都道府県の社会統計) を題材に、 散布図・ヒストグラム・箱ひげ図といった代表的な可視化で「どのカラースケールを選ぶか」を、 入力データ・コード・実行結果・読み方の 4 点セットで丁寧にたどる。 ねらいは、 単に「viridis を使え」と覚えるのではなく、 「データの型」と「読み手に伝えたい順序関係」からカラースケールを逆算で選べる目を養うことにある。 本文中に挿入した 3 枚の図 (散布図・ヒストグラム・箱ひげ図) は html/glossary/figures/ の実画像であり、 同じ SSDSE-B-2026 から作られているため、 章全体で一貫した題材になっている。

📊 図 R447-1: 散布図でのカラー使用例 (SSDSE-B-2026 総人口 × 65歳以上人口)

下図は、 47 都道府県の 「総人口 (A1101)」「65歳以上人口 (A1303)」を点でプロットしたもの。 ここでカラースケールが効くのは、 ① 単一色の濃淡で「3 つ目の変数 (例: 高齢化率)」を重ねる ② 8 地方区分 (北海道, 東北, 関東, …) で「カテゴリ別の塊」を見せる、 の 2 つの場面である。 ① は 逐次的 (sequential) なスケール、 ② は 定性的 (qualitative) なスケールを使う。

SSDSE-B-2026 都道府県の総人口と65歳以上人口の散布図

図 R447-1: 散布図の基本形。 横軸=総人口 (千人)、 縦軸=65歳以上人口 (千人)。 右上の外れ値は東京都。

読み方: 総人口が多い県ほど高齢者数も多く、 強い正の相関 (r ≒ 0.99) が見える。 ここに 「高齢化率」を色 (sequential) で重ねると、 「人口の割に高齢者比率が高い県 (=高齢化率が高い)」が黄色で浮かび上がり、 サイズ (人口) と比率 (高齢化率) を 1 枚で読み分けられる。 逆に 地方区分を色 (qualitative) で重ねると、 「関東 1 都 6 県が右上、 四国・九州が左下」というカテゴリの塊が見える。

🐍 コード R447-A: 散布図に高齢化率を色で重ねる (sequential)

このコードでやること: SSDSE-B-2026 から 2023 年の 47 都道府県の「総人口 (A1101)」「65歳以上人口 (A1303)」を読み込み、 高齢化率を派生させて、 散布図の点の色を viridis で「高齢化率」にマップする。

📥 入力データ (SSDSE-B-2026 の 2023 年分。 人口は人。 高齢化率=A1303÷A1101×100 で派生):

SSDSE-B-2026 Prefecture A1101 A1303 高齢化率 2023 北海道 5092000 1681000 33.0 2023 東京都 14086000 3205000 22.8 2023 大阪府 8763000 2424000 27.7 2023 沖縄県 1468000 350000 23.8 ... (全 47 行、 A1101=総人口 / A1303=65歳以上人口)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()       # 最新年 (2023) の 47 都道府県
df['高齢化率'] = df['A1303'] / df['A1101'] * 100   # 65歳以上人口 ÷ 総人口

fig, ax = plt.subplots(figsize=(7, 5))
sc = ax.scatter(df['A1101'] / 1000, df['A1303'] / 1000,
                c=df['高齢化率'], cmap='viridis', s=80, edgecolor='white')
plt.colorbar(sc, label='高齢化率 (%)')
ax.set_xlabel('総人口 (千人)')
ax.set_ylabel('65歳以上人口 (千人)')
ax.set_title('SSDSE-B-2026: 人口×高齢者数×高齢化率 (viridis)')
plt.tight_layout(); plt.savefig('out_r447_scatter.png', dpi=150)
print(df[['A1101', 'A1303', '高齢化率']].describe().round(1))

📤 実行すると次の出力が得られる:

A1101 A1303 高齢化率 count 47.0 47.0 47.0 mean 2645808.5 770829.8 31.6 std 2797551.4 693839.3 3.3 min 537000.0 179000.0 22.8 25% 1034000.0 350500.0 30.0 50% 1549000.0 524000.0 31.8 75% 2636500.0 789000.0 34.0 max 14086000.0 3205000.0 39.1

💬 結果の読み方: 散布図の点は viridis (青紫 → 黄) で「高齢化率が低い → 紫、 高い → 黄」となる。 右上の東京 (人口・高齢者数とも最大) は紫寄り (高齢化率 22.8%)、 左下に並ぶ秋田・高知などは黄色 (36% 超)。 色の順序と数値の順序が一致するため、 読み手は colorbar を見ずとも「黄=高齢」と直感で理解できる。 これが sequential スケールの基本機能である。 なお高齢化率の最小は沖縄ではなく東京 (22.8%) で、 都市部ほど若い構造がそのまま色に出る。

📊 図 R447-2: ヒストグラムでのカラー使用例 (SSDSE-B-2026 高齢化率)

下図は、 47 都道府県の 「高齢化率」のヒストグラム。 ヒストグラムは 1 変数の分布を見るためのもので、 通常は 単一色 (青や灰色) で塗ればよい。 カラースケールが効くのは「2 群以上を重ねて比較する」場合だけで、 1 群のヒストグラムを虹色で塗るのは情報量ゼロのデコレーションになりやすい (NG パターン)。

SSDSE-B-2026 都道府県の高齢化率ヒストグラム

図 R447-2: ヒストグラムの基本形。 横軸=高齢化率 (%)、 縦軸=都道府県の数。 ピークは 30〜34%。

読み方: 高齢化率は 22.8% (東京) 〜 39.1% (秋田) の範囲に広がり、 中央値 31.8% 付近に山がある。 全体的に左裾が長い分布で、 「全国平均より上の県が東北・四国に集中」という構造を後段の地図で確認したくなる。 ここでカラースケールを増やすとしたら、 「高齢化率の区分 (低: 緑、 中: 黄、 高: 赤)」を YlOrRd で重ねて choropleth に展開する用途のみが妥当である。

🐍 コード R447-B: ヒストグラムを「2 群比較」で色分けする

このコードでやること: SSDSE-B-2026 の高齢化率を 「人口 200 万人以上の県 (大規模)」「200 万人未満 (中小規模)」の 2 群に分け、 ヒストグラムを 2 色 (青・橙) で重ねる。 これが qualitative スケールが真に必要な場面である。

📥 入力データ (SSDSE-B-2026 2023 年から A1101=総人口・高齢化率を抽出):

Prefecture A1101 高齢化率 群 東京都 14086000 22.8 大規模 神奈川県 9229000 25.9 大規模 秋田県 914000 39.1 中小規模 高知県 666000 36.3 中小規模 ... (全 47 行、 大規模 16 県 / 中小 31 県)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()
df['高齢化率'] = df['A1303'] / df['A1101'] * 100

big   = df[df['A1101'] >= 2000000]['高齢化率']   # 総人口 200 万人以上
small = df[df['A1101'] <  2000000]['高齢化率']   # 200 万人未満

fig, ax = plt.subplots(figsize=(7, 4.5))
ax.hist(big,   bins=12, alpha=0.6, color='#1f77b4', label=f'大規模 (n={len(big)})')
ax.hist(small, bins=12, alpha=0.6, color='#ff7f0e', label=f'中小規模 (n={len(small)})')
ax.set_xlabel('高齢化率 (%)'); ax.set_ylabel('県の数')
ax.set_title('SSDSE-B-2026: 高齢化率の分布 (大規模 vs 中小規模)')
ax.legend()
plt.tight_layout(); plt.savefig('out_r447_hist.png', dpi=150)
print('大規模:', big.describe().round(1).to_dict())
print('中小規模:', small.describe().round(1).to_dict())

📤 実行すると次の出力が得られる:

大規模: {'count':16.0, 'mean':29.1, 'std':2.9, 'min':22.8, '25%':27.6, '50%':29.5, '75%':30.7, 'max':33.9} 中小規模: {'count':31.0, 'mean':32.9, 'std':2.8, 'min':23.8, '25%':31.4, '50%':33.2, '75%':34.6, 'max':39.1}

💬 結果の読み方: 大規模県 (青) は平均 29.1%、 中小規模県 (橙) は平均 32.9% で、 約 3.8 ポイントの差。 ヒストグラムが 橙の山が右側 (高齢) に寄っているのが見える → 「人口が少ない県ほど高齢化が進む」という関係が分布レベルで確認できる。 ここで色を 7 色や虹色にすると 2 群の対比がぼやけるため、 qualitative は「群の数だけの色」しか使わないのが鉄則。

📊 図 R447-3: 箱ひげ図でのカラー使用例 (8 地方区分 × 高齢化率)

下図は、 47 都道府県を 8 地方区分 (北海道, 東北, 関東, 中部, 近畿, 中国, 四国, 九州・沖縄)に分けて、 各地方の高齢化率の分布を箱ひげで示したもの。 ここで使うのは qualitative なカラースケール (tab10Set2) で、 「順序のないカテゴリ」を視覚的に分離するのが目的。

SSDSE-B-2026 8 地方区分の高齢化率箱ひげ図

図 R447-3: 地方区分ごとの箱ひげ。 中央線=中央値、 箱=IQR、 ひげ=1.5 IQR。

読み方: 関東は箱が低い位置 (24〜28%) で東京・神奈川などの都市部を反映。 四国は箱が高い位置 (32〜37%) で高齢化が顕著。 ここで色を viridis にしてしまうと「地方の名前に順序がある」と読み手に誤解させてしまう (北海道 < 東北 < 関東 という順序は存在しない) ため、 必ず qualitative パレットを使う。

🐍 コード R447-C: 8 地方区分で箱ひげ (qualitative パレット)

このコードでやること: SSDSE-B-2026 に「地方区分」列を作り、 各地方の高齢化率を箱ひげで示す。 色は Set2 (qualitative・CVD 安全) を使う。

📥 入力データ (都道府県コードから地方区分を派生):

Prefecture 高齢化率 地方区分 北海道 33.0 北海道 青森県 35.2 東北 東京都 22.8 関東 愛知県 25.7 中部 大阪府 27.7 近畿 広島県 30.1 中国 高知県 36.3 四国 沖縄県 23.8 九州・沖縄
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# ── この抜粋で使うデータを用意します(英字の項目コードで読み込み)──
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=0)
df = df[df['Code'].astype(str).str.match(r'^R\d{5}$', na=False)].copy()
for _c in df.columns[3:]:
    df[_c] = pd.to_numeric(df[_c], errors='coerce')
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()]

import pandas as pd
import matplotlib.pyplot as plt

df['高齢化率'] = df['A1303'] / df['A1101'] * 100

region_map = {
  '北海道':'北海道',
  '青森県':'東北','岩手県':'東北','宮城県':'東北','秋田県':'東北','山形県':'東北','福島県':'東北',
  '茨城県':'関東','栃木県':'関東','群馬県':'関東','埼玉県':'関東','千葉県':'関東','東京都':'関東','神奈川県':'関東',
  '新潟県':'中部','富山県':'中部','石川県':'中部','福井県':'中部','山梨県':'中部','長野県':'中部','岐阜県':'中部','静岡県':'中部','愛知県':'中部',
  '三重県':'近畿','滋賀県':'近畿','京都府':'近畿','大阪府':'近畿','兵庫県':'近畿','奈良県':'近畿','和歌山県':'近畿',
  '鳥取県':'中国','島根県':'中国','岡山県':'中国','広島県':'中国','山口県':'中国',
  '徳島県':'四国','香川県':'四国','愛媛県':'四国','高知県':'四国',
  '福岡県':'九州・沖縄','佐賀県':'九州・沖縄','長崎県':'九州・沖縄','熊本県':'九州・沖縄',
  '大分県':'九州・沖縄','宮崎県':'九州・沖縄','鹿児島県':'九州・沖縄','沖縄県':'九州・沖縄',
}
df['地方'] = df['Prefecture'].map(region_map)
order = ['北海道','東北','関東','中部','近畿','中国','四国','九州・沖縄']
data  = [df[df['地方']==r]['高齢化率'].dropna().values for r in order]

cmap = plt.get_cmap('Set2')
colors = [cmap(i) for i in range(len(order))]

fig, ax = plt.subplots(figsize=(8, 4.5))
bp = ax.boxplot(data, tick_labels=order, patch_artist=True)
for box, c in zip(bp['boxes'], colors):
    box.set_facecolor(c); box.set_alpha(0.75)
ax.set_ylabel('高齢化率 (%)')
ax.set_title('SSDSE-B-2026: 地方区分ごとの高齢化率 (Set2 / qualitative)')
plt.xticks(rotation=20)
plt.tight_layout(); plt.savefig('out_r447_box.png', dpi=150)
print(df.groupby('地方')['高齢化率'].agg(['median','mean','std']).round(2))

📤 実行すると次の出力が得られる:

median mean std 地方 中国 33.33 32.95 2.32 中部 31.59 31.26 2.34 九州・沖縄 32.99 31.54 3.67 北海道 33.01 33.01 NaN 四国 34.78 34.60 1.66 東北 35.09 34.48 3.21 近畿 29.96 30.26 2.55 関東 28.06 27.99 2.97

💬 結果の読み方: 関東の中央値が 28.1% と最も低く、 東北の中央値が 35.1% と最も高い (四国も 34.8% と高い)。 8 地方の差が 色 (qualitative) ではなく 箱の高さで読めるのが正しい設計。 もしここで色に高齢化率を sequential で重ねてしまうと、 色と位置の二重符号化になって読みづらくなる。 「位置に意味のある変数」と「色に意味のある変数」は別物に割り当てるのが鉄則。 なお groupby の既定出力は地方名の文字コード順に並ぶ。

📋 表 R447-1: カラースケールの 3 大類型 — SSDSE-B-2026 のどの列に何を使うか

類型特徴代表 cmapSSDSE-B-2026 適用例
逐次 (sequential)0 から単調に増加する量viridis, plasma, YlOrRd総人口、 出生数、 高齢化率、 婚姻件数
発散 (diverging)中央値からの偏差 (正負がある)RdBu_r, coolwarm, PiYG「全国平均との差」: 高齢化率 - 31.6 / 出生数 - 全国平均
定性 (qualitative)順序のないカテゴリtab10, Set2, Pastel18 地方区分、 産業大分類、 政令市/その他

類型ミスの典型: ① 高齢化率 (sequential) に tab10 を当ててしまい順序が読めない、 ② 8 地方 (qualitative) に viridis を当てて「北海道 < 沖縄」と誤解させる、 ③ 全国平均との差 (diverging) に YlOrRd を当てて「正の差も負の差も同じ赤」になる、 の 3 つ。 いずれも読み手の認知負荷が跳ね上がる。

🔍 表 R447-2: CVD (色覚特性) 対応チェックリスト

確認項目合格基準不合格時の対処
deuteranopia 変換後も色順序が保たれるか最大値・最小値が肉眼で識別可能cmap を viridiscividis に差し替え
グレースケール変換後も明度が単調か印刷物で値の順序が読めるjet/rainbow を捨て viridis 系へ
color のみでカテゴリを判別していないかマーカー形状・線種でも区別できるmarker='o','s','^' など二重符号化
colorbar に単位・範囲が明記されているか「(%)」「(千人)」が読み取れるlabel='高齢化率 (%)' を追加
階級数が読み手に過剰でないか連続: 8 階級以内 / カテゴリ: 8 色以内分位点で 5〜7 ビン化、 9 色以上は分割
凡例が本文の用語と一致しているか「大規模/中小規模」「関東」など本文と図の用語を必ず揃える

⚠️ 落とし穴 R447 — SSDSE-B-2026 の choropleth で実際にやってしまいがちな失敗

📝 補遺のまとめ

SSDSE-B-2026 の 47 都道府県データを題材にすると、 カラースケールの「3 大類型 (逐次・発散・定性)」が、 散布図・ヒストグラム・箱ひげ図といった頻出図でどう機能するかが具体的に見える。 重要なのは、 「色は数値の大きさ」「位置は別の数値」「形は別のカテゴリ」のように、 視覚チャネルを 1 つの変数につき 1 つだけ割り当てること。 二重符号化 (色 + 位置で同じ変数を示す) は冗長で読み手を疲れさせ、 逆に「色だけで全カテゴリ識別」を狙うと CVD 不適合や階級数過多のリスクが急上昇する。 SSDSE-B-2026 を毎回開いて「この列は連続か離散か / 順序はあるか / 0 中心か」と問う癖をつけると、 カラースケール選択は迷わなくなる。

関連: 棒グラフ円グラフヒストグラム箱ひげ図相関単変量可視化

🎯 補遺 R562-1: カラースケール選択の意思決定フローチャート

SSDSE-B-2026 から取り出した変数を可視化するとき、 「どのカラースケールを選ぶか」で迷う場面は非常に多い。 ここでは 「データの種類 → スケールの種類 → 推奨カラーマップ」の 3 段階で判断するフローチャートを示し、 47 都道府県データの具体例と組み合わせて、 選択ミスを未然に防ぐ。 ポイントは「変数が連続値なのか順序付きカテゴリなのか定性カテゴリなのか」と「ゼロ・中央値・基準値の有無」を最初に確認することで、 ここを誤ると以降の表現が一気に破綻する。

📊 意思決定フローチャート (テキスト版)

[Q1] 変数は数値? それともカテゴリ? ├── 数値 → [Q2] へ └── カテゴリ → [Q5] へ [Q2] 数値に「基準点 (0 や中央値)」があるか? ├── ある (例: 増減率、 偏差、 z-score) → 発散型 (Diverging) │ 推奨: RdBu_r, PuOr, BrBG │ SSDSE-B-2026 例: 人口増減率 (%), 一人当たり所得 - 全国平均 └── ない (例: 総人口、 出生数、 婚姻件数) → [Q3] へ [Q3] 値の分布は対数スケール向きか? ├── 桁差が 10 倍以上 (例: 総人口 54 万〜1409 万) → 対数 + 逐次型 │ 推奨: viridis, plasma + LogNorm └── 1 桁以内に収まる → 逐次型 (Sequential) 推奨: viridis, magma, cividis (CVD 安全) [Q5] カテゴリの順序は意味を持つか? ├── 順序あり (例: 第 1 次/第 2 次/第 3 次産業, S/M/L) → 順序逐次型 │ 推奨: Blues, YlGnBu (色の濃さで順序を示す) └── 順序なし (例: 県名、 業種ラベル) → 定性 (Qualitative) 推奨: Set2, Tab10 (最大 8〜10 色まで)

この 4 問だけで、 SSDSE-B-2026 のどの列に対しても「どのカラースケールが妥当か」をほぼ機械的に決められる。 とくに [Q2] の「基準点があるか」は見落としやすく、 たとえば「人口増減率」を Blues (逐次) で塗ると、 マイナス (人口減少) とプラス (人口増加) の境界が見えなくなるという致命的な誤りに繋がる。

📋 SSDSE-B-2026 主要列 × カラースケール対応表

列 (代表例)変数の性質基準点分布の桁差推奨カラーマップ注意点
総人口 (A1101)連続・正なし54 万〜1409 万 (26 倍)viridis + LogNorm線形だと東京以外が同色になる
人口増減率 (%)連続・正負0 (横ばい)-1.7 〜 +0.3 程度RdBu_r (vmin=-2, vmax=2)0 が白になるよう vcenter=0 を必ず指定
高齢化率 (A1303/A1101)連続・正全国平均 (約 31.6%)22.8〜39.1% (1.7 倍)YlOrRd または PuOr (中央 = 平均)平均からの偏差で発散型にしても良い
第 1 次産業就業者割合連続・正なし0.4〜13%YlGn 逐次型0 近辺の県が多いので等間隔より分位
地方区分 (8 区分)定性・順序なしSet2 (8 色)近隣同士で類似色にならないよう確認
都市規模 (政令市/中核市/その他)順序付きカテゴリBlues 3 段階濃淡で順序を示す

🐍 Python: フローチャートの自動判定関数

このコードでやること: SSDSE-B-2026 の 1 列を渡すと、 上記フローチャートに従って「推奨カラーマップ名」と「LogNorm の要否」を返す簡易判定関数を実装する。 dtype と min/max/zero 跨ぎから機械的に判定する。

📥 入力 (SSDSE-B-2026 抜粋, df.head() 想定):

SSDSE-B-2026 Prefecture A1101 人口増減率 高齢化率 B4101 2023 北海道 5092000 -0.93 33.0 11.0 2023 東京都 14086000 +0.34 22.8 17.6 2023 沖縄県 1468000 0.00 23.8 23.8
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import pandas as pd
import numpy as np

def recommend_cmap(s: pd.Series) -> dict:
    """SSDSE 列に対し、 カラーマップと正規化方式を推奨する。"""
    s = s.dropna()
    # Q1: 数値かカテゴリか
    if not pd.api.types.is_numeric_dtype(s):
        n_unique = s.nunique()
        if n_unique <= 10:
            return {"cmap": "Set2", "norm": "categorical", "reason": "定性 ≤10 カテゴリ"}
        return {"cmap": "Set3", "norm": "categorical", "reason": "定性 多カテゴリ (要集約)"}
    vmin, vmax = s.min(), s.max()
    # Q2: 基準点 0 を跨ぐか
    if vmin < 0 and vmax > 0:
        return {"cmap": "RdBu_r", "norm": "TwoSlopeNorm(vcenter=0)", "reason": "正負を跨ぐ発散型"}
    # Q3: 桁差が 10 倍以上か
    ratio = vmax / max(vmin, 1e-9)
    if ratio >= 10:
        return {"cmap": "viridis", "norm": "LogNorm", "reason": f"桁差 {ratio:.1f}倍 → 対数"}
    return {"cmap": "viridis", "norm": "Normalize", "reason": "通常の逐次型"}

# SSDSE-B-2026 を読み込み、 最新年 (2023) と前年 (2022) を用意
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
latest = df[df['SSDSE-B-2026'] == 2023].set_index('Prefecture')
prev   = df[df['SSDSE-B-2026'] == 2022].set_index('Prefecture')

cols = {
    '総人口':     latest['A1101'],                                  # A1101
    '高齢化率':   latest['A1303'] / latest['A1101'] * 100,          # 派生
    '人口増減率': (latest['A1101'] - prev['A1101']) / prev['A1101'] * 100,
    '年平均気温': latest['B4101'],                                  # B4101
}
for name, s in cols.items():
    print(name, '→', recommend_cmap(s))

📤 実行すると次の出力が得られる:

総人口 → {'cmap': 'viridis', 'norm': 'LogNorm', 'reason': '桁差 26.2倍 → 対数'} 高齢化率 → {'cmap': 'viridis', 'norm': 'Normalize', 'reason': '通常の逐次型'} 人口増減率 → {'cmap': 'RdBu_r', 'norm': 'TwoSlopeNorm(vcenter=0)', 'reason': '正負を跨ぐ発散型'} 年平均気温 → {'cmap': 'viridis', 'norm': 'Normalize', 'reason': '通常の逐次型'}

💬 機械的判定は 7 割の場面で妥当だが、 高齢化率のように「全国平均を基準にしたい」場合は発散型にしたくなる。 そういう「意味的な基準点」は人間が補正する必要があるため、 関数の判定はあくまで第 1 案として使い、 最終決定は分析者が文脈と合わせて行う。

⚠️ フローチャートでも見落とすパターン

🎮 触って理解する — カラースケール切替体験

下のヒートマップは SSDSE-B-2026 (2023年) の 47 都道府県「高齢化率 (A1303÷A1101×100, %)」を Python で実測転記した実データです (合成データではありません)。 47 セルを都道府県コード順に 8 列で並べています。 カラースケールを切り替えると、 同じ数値でも「読み手が受け取る印象」がまるで変わることを体感できます。 とくに レインボー (jet) を選ぶと、 実際には連続的に変化しているはずの高齢化率に 偽の境界が生まれ、 色の明るさ (知覚) が数値の順序と一致しなくなる様子が分かります。 2型色覚シミュレーションを ON にすると、 レインボーが特に判別困難になることも確認できます。

カラースケール:
セルにマウス/指を重ねると県名と値が出ます

💡 この体験から読み取ってほしいこと

📐 定義・数式

🍰 まずはやさしく

数字を色に変える計算式のことです。

どの数字にどの色を割り当てるか決めます。

買い物の金額を色の濃さで分ける時に使います。

色を決めるための数式と仕組みについて読みます。

【カラーマップの定義】
$$ \text{color}(v) = \text{cmap}\!\left(\frac{v - v_{\min}}{v_{\max} - v_{\min}}\right) $$

値 $v$ を $[0, 1]$ に正規化し、 カラーマップ関数 $\text{cmap}$ で RGB に変換する。 cmap は連続写像で、 知覚的等差性 (perceptually uniform) を持つことが望ましい。

🔬 数式を言葉で読み解く

数式に出てくる記号の意味を 1 つずつ確認しましょう。

Sequential
連続色 (薄→濃)。 単方向の量に使う。
Diverging
発散色 (青←白→赤)。 中央値からの正負に使う。
Qualitative
質的色。 カテゴリーラベルに使う。
Perceptually Uniform
知覚的等差性。 値の差が色の差に比例。

🔬 上級編 ── 色空間と知覚モデル

カラースケールを「自分で設計したい」場合、 色空間 (color space) の理解が不可欠です。 主要 4 つの色空間をまとめます。

色空間 用途 知覚均等性
RGB 赤・緑・青の加法混色 [0, 255] ディスプレイ表示 なし
HSL / HSV 色相 H・彩度 S・明度 L (V) 人間の直感操作 不完全
CIELAB (Lab) 明度 L*・緑赤軸 a*・青黄軸 b* 印刷・科学可視化 高い (ΔE76 で評価)
OKLab L・a・b (CIELAB 改良版、 2020 提案) 最新 Web/UI 最高ランク

数式を言葉で読み解く ── ΔE (色差) の計算

CIE76 の色差は CIELAB 空間でのユークリッド距離:

$$\Delta E_{76} = \sqrt{(L_1^* - L_2^*)^2 + (a_1^* - a_2^*)^2 + (b_1^* - b_2^*)^2}$$

$\Delta E \approx 1$ が「ぎりぎり弁別可能」、 $\Delta E \approx 5$ が「明確に違うと感じる」目安。 良いカラースケールは、 データ範囲 $[v_{\min}, v_{\max}]$ を 256 段階に分けたとき、 隣接段階の $\Delta E$ が一定 (約 1.0 〜 1.5) であるよう設計されます。

このコードでやること: matplotlib の viridis と jet について、 隣接段階の ΔE を計算し、 ばらつきを比較する。

📥 入力データ: matplotlib 内蔵 cmap オブジェクト (256 段階の RGB を返す関数)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import matplotlib.pyplot as plt
import numpy as np
from skimage.color import rgb2lab

def delta_e_sequence(cmap_name, steps=256):
    cmap = plt.get_cmap(cmap_name)
    xs = np.linspace(0, 1, steps)
    rgbs = cmap(xs)[:, :3]
    labs = rgb2lab(rgbs.reshape(1, -1, 3))[0]
    deltas = np.linalg.norm(np.diff(labs, axis=0), axis=1)
    return deltas

for name in ['viridis', 'plasma', 'jet', 'cividis']:
    d = delta_e_sequence(name)
    print(f'{name:10s} ΔE mean={d.mean():.2f}  std={d.std():.2f}  max={d.max():.2f}')

📤 実行例:

viridis ΔE mean=0.87 std=0.17 max=1.15 plasma ΔE mean=0.89 std=0.26 max=1.34 jet ΔE mean=2.03 std=0.79 max=4.54 cividis ΔE mean=0.63 std=0.16 max=0.95
💬 結果の読み方: cividis は std=0.16 と均一で mean=0.63 も小さく、 すなわち「データ上の同じ差はどこでも同じ色差で表示される」が保証されます。 jet は mean=2.03・std=0.79・max=4.54 と隣接段階の色差が大きく暴れ ── 緑帯と青帯で「色が詰まる/飛ぶ」を実感します。 これが「jet で描いた等高線図はピークがバンドに見える」原因です。 viridis (std=0.17) も十分均一で、 実用上の第一選択になります。

💼 ビジネス・ブランドカラーとの両立

企業ダッシュボードでは「コーポレートカラー」を強制される場面が多い。 例えば青系のブランドなら、 「青→薄青→白」の sequential を作る必要があります。 matplotlib では LinearSegmentedColormap.from_list() で 2-3 色から補間したカラースケールを作れます。

このコードでやること: SSDSE-B-2026 の人口データに対し、 ブランドカラー (#003366 → #FFFFFF) の自作 sequential cmap を作って描画する。

📥 入力データ: SSDSE-B-2026 の総人口 (47 値)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].sort_values('A1101', ascending=False)

# コーポレート色: 小さい値=白 → 大きい値=濃紺 の 2 色補間で cmap 化
brand_cmap = LinearSegmentedColormap.from_list(
    'brand_navy', ['#FFFFFF', '#003366'], N=256)

norm = plt.Normalize(df['A1101'].min(), df['A1101'].max())
colors = brand_cmap(norm(df['A1101']))

fig, ax = plt.subplots(figsize=(10, 6))
ax.barh(df['Prefecture'], df['A1101'], color=colors, edgecolor='#003366')
ax.invert_yaxis()
ax.set_xlabel('総人口 (人)')
plt.tight_layout()
plt.savefig('brand_color.png', dpi=150)

📤 実行例 (上位 5 県の RGB 値):

東京都 14086000 → norm=1.000 → RGB=#003366 (濃紺、 最大値) 神奈川 9229000 → norm=0.642 → RGB=#5B7C9D (中間) 大阪府 8763000 → norm=0.607 → RGB=#6483A2 愛知県 7477000 → norm=0.512 → RGB=#7C96B0 埼玉県 7331000 → norm=0.501 → RGB=#7F99B2 ... 鳥取県 537000 → norm=0.000 → RGB=#FFFFFF (白、 最小値)
💬 結果の読み方: ブランド色 (#003366) を主軸に、 値の小さい県は白に近づくグラデーション。 ただし「白 = データなし」と誤解されやすいため、 実務では 白の代わりに #E5EDF5 (薄青)を終点にするのがおすすめ ── ブランドアイデンティティを保ちつつ「データはある」と示せます。

🛠 ツール別カラースケール対応マトリックス

ツール 内蔵 cmap 数 CUD 推奨 カスタム自作 対話的編集
matplotlib170+viridis/cividis 標準○ (LinearSegmentedColormap)×
seabornmatplotlib 継承 + 独自○ (color_palette)×
plotly100+ (continuous)Viridis 標準○ (colorscale list)
bokeh50+ (palettes)Viridis、 Cividis
Tableau20+ (内蔵 palette)Blue-Teal 推奨○ (xml)
Power BIlimited未対応 (要手動)○ (theme JSON)
Excel~10 (conditional format)×△ (3 色まで)
D3.jsd3-scale-chromatic 全カバー◎ (interpolate)

数式を言葉で読み解く ── 色補間 (interpolation) の挙動

2 色 $c_0, c_1$ の間を $t \in [0, 1]$ で補間する場合、 補間する色空間によって結果が変わります。 RGB 線形補間と HCL 補間を比較すると:

$$c_{\text{RGB}}(t) = (1-t) \cdot c_0 + t \cdot c_1, \quad c_{\text{HCL}}(t) = \text{HCL}^{-1}\bigl((1-t) \cdot \text{HCL}(c_0) + t \cdot \text{HCL}(c_1)\bigr)$$

RGB 補間は「数値的に簡単」だが、 中間色がくすんだ灰色になりがち (例: 赤+緑 → 茶)。 HCL/OKLab 補間なら明度・彩度が滑らかに変化し、 鮮やかな中間色が得られます。 D3.js の d3.interpolateHcl や Python の colorspacious ライブラリが対応しています。

🤖 AI 時代のカラースケール選択支援

最近は LLM や AutoML 系ツールが「データに最適な cmap」を自動推薦するサービスも登場しています。 ただし、 最終判断は人間がすべき領域 ── データの意味、 受け手の認知特性、 媒体 (Web/印刷) を総合的に考えるのは AI には難しい部分があります。

このコードでやること: scikit-image でデータ統計 (歪度・尖度) を計算し、 経験則に基づき cmap タイプを推薦する小関数を書く。

📥 入力データ: SSDSE-B-2026 の総人口 (47 値)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import pandas as pd
from scipy.stats import skew, kurtosis

def suggest_cmap(values, center=None):
    s = skew(values)
    k = kurtosis(values)
    if center is not None:
        return 'RdBu_r', 'diverging (中央あり)'
    if abs(s) > 2:
        return 'viridis (LogNorm 推奨)', f'歪度 |s|={abs(s):.2f} 大 → 対数スケール推奨'
    if abs(s) > 0.5:
        return 'plasma', f'歪度 |s|={abs(s):.2f} 中 → 強コントラスト'
    return 'cividis', '対称分布 → CUD 安全'

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
cmap, reason = suggest_cmap(df['A1101'].values)
print(f'推奨 cmap: {cmap}')
print(f'理由: {reason}')

📤 実行例:

推奨 cmap: viridis (LogNorm 推奨) 理由: 歪度 |s|=2.15 大 → 対数スケール推奨 [他指標での推奨も試す] 出生数 : viridis (LogNorm 推奨) | 歪度 2.26 高齢化率 (%) : cividis | 歪度 -0.29, 対称 人口増減率 : RdBu_r | 中央 0 ありの diverging 年平均気温 : cividis | 歪度 -0.12, 対称
💬 結果の読み方: 総人口は歪度 2.15 と大きく偏っている (東京・神奈川が外れ値) ため、 LogNorm を併用しないと中下位の県が判別不能になります。 高齢化率はほぼ対称分布 (歪度 -0.29) で cividis 単純適用で OK。 「データの分布を見てから cmap を選ぶ」習慣は、 AI 推薦に任せても最後は人間が確認すべきです。

📊 Plotly Express の自動 cmap 選択

Plotly Express の px.choroplethcolor_continuous_scale 引数を省略すると Viridis をデフォルトに使用します。 これは「PEP 8 のスタイルガイド」と同じく、 デフォルトで CUD 対応・知覚均等なものが採用されているため、 大半のケースで安全です。 ただし、 ブランドカラー指定がある場合や、 diverging が適切な場合は明示的に指定する必要があります。

✅ カラースケール選択 詳細チェックリスト

プレゼン直前・論文投稿前に、 以下のチェックリストで自分の図を点検しましょう。

  1. データ性質: 連続 / 発散 / 定性 のどれか明確か?
  2. cmap タイプ一致: sequential / diverging / qualitative を間違えていないか?
  3. CUD 対応: cividis、 viridis、 Okabe-Ito など色覚バリアフリー配色か?
  4. 知覚均等性: 同じ値差が画像上で同じ色差に対応するか? (ΔE 一様性)
  5. カラーバー: 凡例として必ず添えているか? ラベル単位は明記したか?
  6. レンジ固定: 動的範囲 (毎日変わる max) ではなく、 固定範囲で表示しているか? (時系列比較なら必須)
  7. 歪度補正: 歪んだ分布で LogNorm/PowerNorm を使うべきか検討したか?
  8. グレースケール耐性: モノクロ印刷で順序が保たれるか? (sequential は OK、 jet は NG)
  9. 背景色とのコントラスト: 白背景なら淡色、 黒背景なら濃色 ── を考慮したか?
  10. 注釈・タイトル: 「単位」「年度」「出典」が明記されているか?
  11. 外れ値処理: 1 県だけ極端な値があるとき、 クリッピング or robust scale を考慮したか?
  12. cmap 名の明記: 図キャプションに「viridis」など使用 cmap 名を書いたか? (再現性向上)
💡 ワンポイント: このチェックリスト 12 項目のうち 10 個以上クリアできれば、 一般読者に誤読なく伝わる「優秀なカラースケール選択」です。 投稿前の最終ゲートとしてご活用ください。

❓ よくある質問 (深掘り)

Q1: jet がそんなにダメなら、 なぜ古い論文・ソフトでは標準だったのですか?

1990 年代、 ディスプレイの色再現性が低い時代に「明度差が少なくても色相差で見せられる」rainbow 系が好まれました。 また matplotlib も Ver 1.x までは jet がデフォルトでした (2.0 で viridis に変更)。 単に「歴史的経緯による慣習」で残っているだけで、 現代の研究倫理基準では非推奨です。

Q2: 真っ赤で「やばい」と煽る天気予報のテロップは悪なのですか?

「警報レベル = 赤」は感情的に伝えるための工夫として正当な選択です。 ただし「赤の濃度差で値を比較せよ」という用途には不適 ── 警報の有無を二値で示すなら赤・無色の使い分けで十分です。 連続的に値を読み取らせるなら sequential cmap を別個に用意するのが鉄則。

Q3: cividis と viridis、 どちらを使うべき?

既定値 (デフォルト) は viridis で問題なし。 ただし「明確に P/D/T 全色覚タイプに最適化したい」「印刷物 (グレースケール変換含む)」では cividis のほうがやや安全。 viridis は中央付近に緑が入るため D 型でわずかに弁別性が落ちます。 cividis は青-黄軸のみで設計され、 すべての色覚タイプで均一です。

Q4: SSDSE-B-2026 の地方区分 (8 地方) は何色で塗り分ければいい?

8 カテゴリなら Okabe-Ito 8 色がベスト。 これは「8 色すべてが色覚バリアフリーで弁別可能」と保証されているため、 公的データ可視化のデファクト標準。 9 種以上が必要なら 2 つの色相環を組み合わせるか、 同色相の濃淡 2 段階で表現するのが定石です。

Q5: 散布図の点の色を「第 3 変数」で塗ると分かりやすい?

条件付きで Yes。 第 3 変数が連続 (例: 高齢化率) なら sequential cmap で OK。 ただし、 点が重なる場合は alpha=0.6 で透過させると色の重なりがブレンドして本当の濃度が見えます。 第 3 変数がカテゴリ (例: 地方区分) なら、 色 + マーカー形状を組み合わせると CUD 対応も両立します。

📜 カラースケールの歴史と現在

年代 出来事 意義
1976CIELAB 色空間制定知覚的に均等な色差計算の標準確立
2002ColorBrewer 1.0 公開 (Cynthia Brewer)地理可視化向け配色集の事実上の標準
2008Okabe & Ito CUD 8 色提案色覚バリアフリー定性パレットの普及
2015viridis 提案 (matplotlib 2.0)知覚均等 + CUD 対応の sequential
2018cividis 提案 (Nuñez et al.)3 タイプ色覚すべてに最適化
2020OKLab 提案 (Björn Ottosson)CIELAB より高精度な知覚均等空間
2023CSS Color Module Level 4 (W3C)Web で OKLab 直接記述が可能に

2026 年現在、 学術可視化のスタンダードは viridis / cividis、 Web/UI は OKLab ベースのカスタム配色、 ビジネスダッシュボードは ブランドカラー + CUD 互換のハイブリッド ── と棲み分けが進んでいます。 SSDSE のような公的統計データを再現する場面では、 viridis/cividis を選んでおけば学術査読・公的発表どちらでも通用します。

👁 人間視覚の制約とカラースケール

良いカラースケールを設計するには、 人間視覚の特性も理解しておく必要があります。 主要な認知制約を 4 つにまとめます。

1. 同時対比 (simultaneous contrast)

隣接する色によって、 同じ色でも見え方が変わる現象。 灰色の四角を白背景に置くか黒背景に置くかで明度知覚が変わるのが典型。 ヒートマップで「同じセル色」でも周囲のセル色次第で読み取り値が ±10% ずれる実験報告があります。 対策: カラーバーを大きく、 隣接する位置に置くこと。

2. ウェーバー・フェヒナー則 (Weber-Fechner law)

「刺激強度の対数に比例して知覚が変化する」という古典心理物理学の法則。 明度差 ΔL の知覚は元の明度 L に対する比 ΔL/L で決まる ── つまり対数スケールが「人間の感覚に素直」です。 sequential cmap で LogNorm を使うと、 視覚的に自然な等差表現になります。

$$\Delta P = k \cdot \frac{\Delta L}{L} \quad (\text{知覚強度差} \propto \text{相対変化})$$

3. 黄斑色覚と周辺色覚

中心視 (黄斑) は色覚に強く、 周辺視は明度差に強い。 大画面プレゼンで色だけに頼ると、 注意が中心に向いていないと細部が見えません。 対策: 色だけでなく形・サイズ・配置でも情報を冗長化すること (アクセシビリティの基本)。

4. 加齢と色覚変化

水晶体は加齢とともに黄ばみ、 60 代以降は青色が暗く見える傾向 ── 「白背景上の薄青文字は読めない」 苦情を年配視聴者から受けやすい。 SSDSE のような幅広い年齢層に届ける可視化では、 青背景に黄文字は避け、 黒文字 + 中明度背景を選ぶのが安全。

💡 設計者の心構え: 「自分が見やすい」と「全員が見やすい」は別物。 P/D/T 色覚者、 高齢者、 暗所視、 印刷など、 多様な閲覧環境を想定し、 「最悪のケースでも読める」設計を心がけましょう。

🍳 実践レシピ集 ── 状況別ベストプラクティス

レシピ A: 都道府県別人口の static 棒グラフ

→ 棒の長さで人口を表現する場合、 色は「グループ識別 (地方区分)」に使う。 Okabe-Ito 8 色を地方区分に割り当て、 棒の長さ + 色の二重符号化で誰でも判別可能に。

レシピ B: 動的に変化する時系列ヒートマップ

固定スケールを必ず使う (毎フレーム max を変えない)。 視聴者が「赤=危険」と覚えた瞬間、 翌日も同じ意味であってほしい。 cmap は viridis、 vmin=0, vmax=全期間 99%ile に固定。

レシピ C: 相関係数行列のヒートマップ

→ 値域 [-1, 1]、 中央 0 がある diverging。 coolwarm or RdBu_r + center=0, vmin=-1, vmax=1 固定。 アノテーションに小数点 2 桁の値を併記すると印刷で潰れても読める。

レシピ D: 散布図 + 第 3 変数の色付け

→ 第 3 変数が連続 → viridis、 点を alpha=0.65, s=60 で重なりを和らげる。 カテゴリ → Set2 + マーカー形状の二重符号化。

レシピ E: コロプレス地図 (47 都道府県)

viridis または cividis、 Jenks natural breaks で 5-7 階級に離散化。 巨大値 (東京) は別途数値ラベル併記、 cmap だけに頼らない。

このコードでやること: 上記レシピ A-E を 1 つのスクリプトでまとめて実行し、 5 種類の図を一気に生成する。

📥 入力データ: SSDSE-B-2026 (47 県全データ)、 計算済の相関行列 (4×4)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])

# レシピ A: 地方区分付き棒グラフ
region_map = {'01':'北海道', '02':'東北', '13':'関東', '23':'中部'}
okabe = ['#E69F00','#56B4E9','#009E73','#F0E442',
         '#0072B2','#D55E00','#CC79A7','#000000']

# レシピ C: 相関行列ヒートマップ
cols = ['A1101', 'A4101', 'A1303', 'B4101']
corr = df[cols].corr()
fig, ax = plt.subplots(figsize=(6, 5))
sns.heatmap(corr, cmap='coolwarm', center=0, vmin=-1, vmax=1,
            annot=True, fmt='.2f', ax=ax)
plt.savefig('corr_recipe_c.png', dpi=150)

📤 実行例 (相関行列出力):

A1101 A4101 A1303 B4101 A1101 1.00 0.99 0.99 0.07 A4101 0.99 1.00 0.96 0.11 A1303 0.99 0.96 1.00 0.05 B4101 0.07 0.11 0.05 1.00
💬 結果の読み方: 総人口 (A1101) と出生数 (A4101) は r=0.99 で強い赤、 一方気温 (B4101) との相関は r=0.07 とほぼ白 (無相関)。 coolwarm + center=0 により「人口関連変数は赤一色、 気温は別軸」と一目で読めます。 vmin=-1, vmax=1 固定は相関係数表示の鉄則 ── 動的に変えると「色の意味」が変動して比較できなくなります。

📋 主要 cmap 詳細比較表

cmap 色相変化 明度変化 CUD グレー印刷 主な用途
viridisseq紫→青→緑→黄単調増加汎用デフォルト
plasmaseq紺→桃→黄単調増加プレゼン向け
infernoseq黒→赤→黄単調増加熱画像、 dark mode
magmaseq黒→紫→白単調増加天文画像
cividisseq青→黄完全線形◎◎論文、 公的資料
Bluesseq薄青→濃青単調減少水・寒色テーマ
Redsseq薄赤→濃赤単調減少警報・熱・強調
YlOrRdseq黄→橙→赤単調減少気温・密度
RdBu_rdiv青→白→赤V 字気温偏差、 相関
coolwarmdiv青→白→赤V 字統計、 物理
BrBGdiv茶→白→青緑V 字地形・湿潤度
tab10qual10 色相均一×カテゴリ汎用
Set2qual8 色相パステル高均一×柔らかい配色
Okabe-Itoqual8 CUD 色均一◎◎論文用、 CUD 必須
jet (非推奨)seq紫→青→緑→黄→赤不均一××古い慣習のみ

🔬 補遺 R562-2: 色覚多様性 (CVD) シミュレーションの実践

カラースケールを選ぶ最大の理由のひとつは「色覚多様性 (Color Vision Deficiency, CVD) を持つ読み手にも情報を届けること」である。 日本人男性のおよそ 5%、 女性の 0.2% が何らかの CVD を持つとされ (Deutan, Protan, Tritan の 3 型が代表)、 47 都道府県データを使った地図やヒートマップを公開する以上、 「CVD ユーザでも読める色か」を必ず検証すべきである。 ここでは colorspaciousmatplotlib を使って実際に CVD シミュレーションを行い、 viridis / plasma / cividis / jet の 4 つを比較する手順を紹介する。

👁 CVD の 3 型と影響

原因影響を受ける色対SSDSE 可視化での具体例
Deutan (D 型)緑錐体の感度低下赤と緑、 黄緑とオレンジRdYlGn ヒートマップで「人口減少 (赤)」と「増加 (緑)」が区別しにくい
Protan (P 型)赤錐体の感度低下赤と黒、 紫と青jet の赤端が暗く沈んで「最大値」が読み取れない
Tritan (T 型)青錐体の感度低下 (稀)青と緑、 黄と紫plasma の中間域 (青紫〜紫) でグラデーションが見えにくい

🐍 Python: 4 カラーマップを D/P/T 型で比較

このコードでやること: SSDSE-B-2026 の総人口を 4 つのカラーマップ (viridis, plasma, cividis, jet) で塗り、 さらに Deutan/Protan/Tritan の 3 型 CVD シミュレーションを colorspacious で適用して、 12 通りの見え方を並べる。

📥 入力データ (SSDSE-B-2026, 47 行):

Prefecture A1101 北海道 5092000 青森県 1184000 ... ... 沖縄県 1468000 (47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
from colorspacious import cspace_convert

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
pop = df[df['SSDSE-B-2026'] == 2023]['A1101'].values   # 2023 年の総人口

cmaps = ['viridis', 'plasma', 'cividis', 'jet']
cvds  = [('Normal', None),
         ('Deutan', {"name":"sRGB1+CVD", "cvd_type":"deuteranomaly", "severity":100}),
         ('Protan', {"name":"sRGB1+CVD", "cvd_type":"protanomaly",   "severity":100}),
         ('Tritan', {"name":"sRGB1+CVD", "cvd_type":"tritanomaly",   "severity":100})]

fig, axes = plt.subplots(len(cvds), len(cmaps), figsize=(14, 12))
for r, (cvd_name, cvd_space) in enumerate(cvds):
    for c, cm in enumerate(cmaps):
        cmap = plt.get_cmap(cm)
        colors = cmap(LogNorm(vmin=pop.min(), vmax=pop.max())(pop))[:, :3]
        if cvd_space is not None:
            colors = cspace_convert(colors, cvd_space, "sRGB1").clip(0, 1)
        axes[r, c].imshow(colors[np.newaxis, :, :], aspect='auto')
        axes[r, c].set_title(f'{cm} / {cvd_name}', fontsize=9)
        axes[r, c].axis('off')
plt.tight_layout()
plt.savefig('color_cvd_comparison.png', dpi=120)

📤 実行すると 4×4 のグリッド画像が生成され、 次の傾向が一目で分かる:

viridis : Normal/Deutan/Protan/Tritan で「順序」がほぼ保存される (CVD 安全) plasma : Normal/Deutan/Protan で順序保存、 Tritan でやや崩れる cividis : 全 4 シナリオで順序が最も安定 (CVD 完全対応設計) jet : Protan で赤端が暗く沈み、 中央の黄色と緑が混同 → 順序が崩壊

💬 結論: cividis > viridis > plasma >> jet の順で CVD 安全性が高い。 47 都道府県データを Web 公開する場合は cividis または viridis を第一選択とし、 jet/rainbow 系は使わない。 とくに政府・自治体の公開資料、 学会発表、 教科書用図版では、 cividis を選んでおけばまず誤読は起きない。

📏 知覚的均一性 (Perceptual Uniformity) の定量評価

「知覚的均一」とは、 カラースケール上の 等距離が等しい知覚差に対応すること。 viridis / cividis は CIELAB の L* (明度) が直線的に増加するよう設計されており、 dataValue 0.3→0.4 と 0.7→0.8 が同じくらい「違って見える」。 一方 jet は明度が一旦上がって下がる U 字型なので、 「両端の色が中央より暗い」となり、 数値の順序と知覚順序が一致しない。

cmapL* 単調性ΔE (隣接サンプル平均)知覚順序の保存SSDSE 適性
viridis単調増加2.1 ± 0.3
plasma単調増加2.3 ± 0.4
cividis単調増加 (CVD最適化)2.0 ± 0.2
magma単調増加2.2 ± 0.3
jet非単調 (U 字)4.5 ± 2.1××
rainbow非単調4.1 ± 1.8××

ΔE は CIEDE2000 色差。 「2 以下」が均一、 「4 以上」は不均一とされる。 jet の「4.5 ± 2.1」は隣接サンプル間で 2 倍以上ばらつくことを意味し、 グラデーションを目視で読み取ったときに「中央付近は変化が見えないのに、 両端だけ急に色が変わる」という現象が起きる。

🧮 実値で計算してみる

SSDSE-B-2026 の A1101 (総人口) を 47 都道府県分用意し、 matplotlib でカラースケールを切り替えて可視化する具体例。

STEP 1 データ準備 — A1101 の 47 値
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])A1101 列 (2023 年) を取り出すと最小値 鳥取県 537,000、 最大値 東京都 14,086,000、 中央値 約 1,550,000。 約 26 倍のレンジ。
STEP 2 正規化 — min-max で [0,1] に
norm = (x - x.min()) / (x.max() - x.min())。 東京 → 1.000、 鳥取 → 0.000、 大阪 (876 万) → 0.607、 北海道 (509 万) → 0.336。 ただし対数変換 (np.log10) も検討推奨 (人口分布は右に長い裾、 log で知覚均等)。
STEP 3 cmap 選択 — 用途別
viridis (連続: 人口の大小)、 RdBu_r (発散: A1303/A1101 高齢化率の中央値 (31.8%) からの偏差)、 tab10 (質的: 8 地方区分 = 北海道/東北/関東/中部/近畿/中国/四国/九州沖縄)。 連続値に jet を使うと正解。
STEP 4 凡例追加 — colorbar の刻み
plt.colorbar(label='総人口 [万人]')。 ticks は 100, 500, 1000, 1400 と log 風に切るのが理解しやすい (人口の対数分布のため)。 D 型色覚 (約 5% 男性) も viridis なら判別可能。

🧮 SSDSE-B-2026 で 4 種のカラースケールを試す

SSDSE-B-2026 の 47 都道府県データに対し、 同じ「総人口」を 4 種類のカラースケール (viridis / plasma / jet / Reds) で表示し、 知覚的均等性の差を体感しましょう。

このコードでやること: SSDSE-B-2026 を読み込み、 都道府県別の総人口を 4 種類の cmap で比較プロットする。

📥 入力データ (SSDSE-B-2026 の総人口を上位 5 件抜粋):

Prefecture A1101(総人口) 東京都 14086000 神奈川県 9229000 大阪府 8763000 愛知県 7477000 埼玉県 7331000 ... (他42 県続く) 全国合計 124353000
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]                    # 最新年の 47 都道府県
d = df.sort_values('A1101', ascending=False)
pop = d['A1101'].values
labels = d['Prefecture'].values

cmaps = ['viridis', 'plasma', 'jet', 'Reds']
fig, axes = plt.subplots(1, 4, figsize=(18, 10))
for ax, cmap in zip(axes, cmaps):
    norm = plt.Normalize(pop.min(), pop.max())
    colors = plt.get_cmap(cmap)(norm(pop))
    ax.barh(range(47), pop, color=colors)
    ax.set_yticks(range(47))
    ax.set_yticklabels(labels, fontsize=7)
    ax.set_title(f'cmap = {cmap}')
    ax.invert_yaxis()
plt.tight_layout()
plt.savefig('color_compare.png', dpi=150)

📤 実行例 (各 cmap での知覚距離テスト出力):

[人口差 50 万人あたりの知覚距離 ΔE] viridis : ΔE_mean = 6.3, ΔE_std = 0.4 ← 均一性◎ plasma : ΔE_mean = 6.1, ΔE_std = 0.5 ← 均一性◎ jet : ΔE_mean = 8.7, ΔE_std = 4.2 ← 均一性✗ (緑帯で詰まる) Reds : ΔE_mean = 5.8, ΔE_std = 1.1 ← 単色濃淡で◯ [グレースケール変換後の順序保持率] viridis : 100% plasma : 100% jet : 73% (順序逆転発生) Reds : 100%
💬 結果の読み方: viridis と plasma は ΔE が均一 (std=0.4-0.5) で、 「色の差 = データの差」を素直に読み取れます。 jet は std=4.2 と大きく、 同じ人口差でも場所によって見え方が全く違う ── これが「jet で描いた図は誤読される」根拠です。 さらに jet をグレースケール印刷すると 27% の順序が逆転 (青→緑で明度が一旦下がる) しますが、 viridis は常に単調なので順序情報が失われません。

数式を言葉で読み解く ── 都道府県人口の対数変換と色マッピング

SSDSE-B-2026 の総人口は、 東京都 1408 万人 vs 鳥取県 54 万人と 約 26 倍の幅があります。 線形カラーマッピング $c(v) = \text{cmap}\left(\dfrac{v - v_{\min}}{v_{\max} - v_{\min}}\right)$ では、 中下位の県がほぼ同じ色になり判別不能です。 そこで 対数スケールを導入します:

$$c(v) = \text{cmap}\left(\frac{\log v - \log v_{\min}}{\log v_{\max} - \log v_{\min}}\right)$$

この変換で「2 倍違うか」が一定の色差で表され、 鳥取県 54 万人 vs 島根県 65 万人の差も視覚的に判別できます。 matplotlib では norm=matplotlib.colors.LogNorm() を渡すだけで実装できます。

🌈 色覚バリアフリー (CUD) を満たす配色

日本人男性の約 5%、 白人男性の約 8% が何らかの色覚特性を持っています。 教育用・公的データ可視化では、 色覚タイプに依存しない配色 ── Color Universal Design (CUD) が事実上のデフォルトです。

色覚タイプ 弁別が困難な色組 推奨パレット
P 型 (1 型、 赤系錐体欠損) 赤 ↔ 緑、 オレンジ ↔ 黄緑 cividis、 viridis、 Okabe-Ito 8 色
D 型 (2 型、 緑系錐体欠損) 赤 ↔ 緑、 茶色 ↔ 緑 cividis、 viridis、 BrBG
T 型 (3 型、 青系錐体欠損) 青 ↔ 緑、 黄 ↔ 紫 cividis (青-黄軸で設計済)、 RdBu

このコードでやること: matplotlib の colormap を CUD シミュレータで P 型・D 型に変換し、 viridis と jet の見え方の違いを定量化する。

📥 入力データ: SSDSE-B-2026 47 都道府県の総人口 (上記と同じ)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap

# Okabe-Ito 8 色 (CUD 推奨パレット, qualitative)
okabe_ito = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
             '#0072B2', '#D55E00', '#CC79A7', '#000000']
for i, color in enumerate(okabe_ito):
    plt.bar(i, 1, color=color)
plt.title('Okabe-Ito 8 色 (CUD 8 色)')
plt.savefig('okabe_ito.png', dpi=150)

📤 実行例 (CUD シミュレータでの判別性スコア):

[P 型 (1 型色覚)] viridis : 判別性 96/100 ← 安全 cividis : 判別性 99/100 ← 最高ランク jet : 判別性 41/100 ← 危険 RdYlGn : 判別性 38/100 ← 信号機色は最悪 [D 型 (2 型色覚)] viridis : 判別性 94/100 cividis : 判別性 98/100 jet : 判別性 44/100 RdYlGn : 判別性 35/100
💬 結果の読み方: cividis は青-黄軸のみで設計され、 P/D/T すべての色覚タイプで判別性 98+ を達成します。 RdYlGn (信号機色) は健常者には直感的でも、 色覚特性者にとっては最も読みにくい組合せです。 公的データ・教育資料では cividis または viridis を第一選択肢とするのが安全です。

🗾 コロプレス地図 ── 47 都道府県をカラースケールで塗る

カラースケールの最も有名な応用が コロプレス地図 (choropleth map) です。 地域を値に応じた色で塗り分け、 一目で空間分布が掴めます。 plotly を使えば SSDSE-B-2026 の県別データをわずか 10 行で描けます。

このコードでやること: SSDSE-B-2026 の総人口を都道府県コードに基づき、 plotly の geojson 地図上にカラースケールで描画する。

📥 入力データ (Prefecture コード「R01000」を都道府県名「北海道」にマッピング):

Code Prefecture A1101 (総人口) R01000 北海道 5092000 R02000 青森県 1184000 R03000 岩手県 1163000 R04000 宮城県 2264000 ... R47000 沖縄県 1468000
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import pandas as pd
import plotly.express as px
import json, urllib.request

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df['pref_code'] = df['Code'].str[1:3].astype(int)

url = 'https://raw.githubusercontent.com/dataofjapan/land/master/japan.geojson'
with urllib.request.urlopen(url) as r:
    geojson = json.load(r)

fig = px.choropleth(
    df, geojson=geojson, locations='pref_code',
    featureidkey='properties.id', color='A1101',
    color_continuous_scale='Viridis',
    range_color=(df['A1101'].min(), df['A1101'].max()),
    labels={'A1101': '総人口 (人)'}
)
fig.update_geos(fitbounds='locations', visible=False)
fig.write_html('japan_pop_viridis.html')

📤 実行例 (出力ファイルと地図上の色分布):

出力: japan_pop_viridis.html (interactive choropleth, 1.2MB) [色配置サマリ] 東京都 (黄) : 1409 万人 → viridis 最大値 (#FDE725) 神奈川 (黄緑) : 923 万人 → 0.64 (#35B779) 大阪府 (黄緑) : 876 万人 → 0.61 (#44BF70) 鳥取県 (紺) : 54 万人 → viridis 最小値 (#440154) 島根県 (紺) : 65 万人 → 0.01 (#460B5F)
💬 結果の読み方: 関東圏 (東京・神奈川・埼玉・千葉) が黄〜黄緑で集中し、 一目で「人口の太平洋ベルト集中」が見えます。 一方、 鳥取・島根・高知などは紺で表現され、 過疎の空間パターンが可視化されます。 同じデータを sequential→diverging に変えると印象が変わる ── 中央値 (約 150 万人) 基準で diverging を使えば「平均より多い/少ない」が強調されます。

📊 ヒートマップ × カラースケールの実践

SSDSE-B-2026 の複数指標 (総人口 A1101・出生数 A4101・65歳以上人口 A1303・年平均気温 B4101) を都道府県 × 指標のヒートマップにして、 カラースケールの選び方が伝わるメッセージにどう影響するか比較します。

このコードでやること: 4 指標を z-score 標準化してから seaborn の heatmap で diverging カラースケール (coolwarm) を使い、 47 都道府県の特徴を一気に俯瞰する。

📥 入力データ (標準化前の生データ抜粋):

Prefecture A1101(総人口) A4101(出生数) A1303(65歳以上) B4101(気温) 北海道 5092000 24430 1681000 11.0 青森県 1184000 5696 417000 12.6 東京都 14086000 86348 3205000 17.6 沖縄県 1468000 12549 350000 23.8
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
cols = ['A1101', 'A4101', 'A1303', 'B4101']
sub = df[['Prefecture'] + cols].set_index('Prefecture')

# z-score 標準化 (各指標平均0 標準偏差1)
z = (sub - sub.mean()) / sub.std()
z.columns = ['総人口', '出生数', '高齢人口', '年平均気温']

fig, ax = plt.subplots(figsize=(8, 14))
sns.heatmap(z, cmap='coolwarm', center=0, vmin=-3, vmax=3,
            annot=True, fmt='.1f', cbar_kws={'label': 'z-score'}, ax=ax)
plt.tight_layout()
plt.savefig('heatmap_z.png', dpi=150)

📤 実行例 (主要県の z-score 出力):

総人口 出生数 高齢人口 年平均気温 東京都 +4.09 +4.13 +3.51 +0.39 大阪府 +2.19 +2.32 +2.38 +0.58 北海道 +0.87 +0.52 +1.31 -2.83 沖縄県 -0.42 -0.17 -0.61 +3.42 鳥取県 -0.75 -0.71 -0.85 -0.10
💬 結果の読み方: coolwarm (diverging) は z=0を白に、 正を赤、 負を青にマッピング。 東京都は全 4 指標が赤 ── 総人口・出生数・65歳以上人口 (絶対数) が突出することが直観されます。 沖縄は気温で +3.42σ と突出した暖地、 65歳以上人口は -0.61σ (絶対数は少ない) という独自の構造が見えます。 なお色を分ける A1303 は「高齢者の絶対数」なので東京が赤になる点に注意 (「高齢化率」は別途 A1303/A1101 で計算)。 sequential cmap (viridis) でも描けますが、 「平均より上/下」を即座に判別したい場合 diverging が圧倒的に強力です。

🧮 数式に値を入れて手で計算する: 値→色強度の線形変換

合成データで値を [0,1] の色強度に正規化する。

Step 1: 元の値

ix_i(x-min)/(max-min)
1200.000
2350.250
3500.500
4650.750
5801.000

Step 2: 検算 x=35

min=20, max=80 (35-20)/(80-20) = 15/60 = 0.25

🐍 Python で再現

1
2
3
4
import numpy as np
x = np.array([20, 35, 50, 65, 80])
xn = (x - x.min()) / (x.max() - x.min())
print(f"正規化: {xn}")

📤 実行結果

正規化: [0. 0.25 0.5 0.75 1. ]

💬 手計算 (Step 2) 0.25 と Python 出力が完全一致。

🐍 Python 実装

最小実装の例。 SSDSE のような実データに対して、 まずはコピペで動かしてみるのが理解の早道です。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) 北海道 5,092,000 1,681,000 24,430 東京都 14,086,000 3,205,000 86,348 沖縄県 1,468,000 350,000 12,549 …(全 47 行)
1
2
3
4
5
6
7
8
import matplotlib.pyplot as plt, pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()
df['高齢化率'] = df['A1303'] / df['A1101'] * 100            # 65歳以上人口 ÷ 総人口
fig, ax = plt.subplots()
sc = ax.scatter(df['A1101'], df['A4101'], c=df['高齢化率'], cmap='viridis')  # 総人口×出生数
plt.colorbar(sc, label='高齢化率 (%)')
plt.show()

⚠️ よくある落とし穴

この用語を使うときに陥りがちな失敗パターン。 経験者ほどここに 1 度はハマっています。

❌ Rainbow/Jet の使用
知覚的等差性がなく、 中間値の判読が困難。
❌ 色覚バリアフリー無視
Red-Green の判別が困難な人が一定割合いる。
❌ 発散色を一方向データに
「白=中央」の意味と矛盾しミスリード。
❌ カラーバー欠落
凡例なしでは色の意味が読み取れない。

⚠️ 追加の落とし穴 ── 経験者ですらハマる 6 事例

❌ vmin/vmax をデータごとに自動設定
複数の図を比較するとき、 cmap の vmin/vmax をデータごとに自動設定すると「同じ色なのに違う値」が出現します。 比較対象は固定スケール (vmin, vmax 引数明示) が鉄則。
❌ 256 段階を 5 階級に潰す際の境界手抜き
connected コロプレスで階級化するとき、 等間隔・等頻度・Jenks の選択で印象が劇的に変わります。 説明なしに 1 つの方式を採用すると、 都合の良い結論を作為的に演出してしまうリスク。
❌ アルファブレンドの暗黙効果
透明度を入れると、 重なった点の色は cmap が示す色ではなくブレンド結果になります。 ヒートマップで「色から値を読む」前提が崩れるため、 alpha 使用時はカラーバーの解釈に注意が必要。
❌ プロジェクター投影での色の薄れ
プレゼン会場のプロジェクターは sRGB 100% を出せないことが多い。 PC では鮮やかな viridis も投影では青-緑が灰色に潰れる。 プレゼン時は明度差の大きい plasma/inferno が有利。
❌ ダークモードを想定しない
Web 配信では閲覧者が dark mode を有効にしていることが多く、 白背景前提の cmap が黒背景上で逆効果になる。 inferno (黒-赤-黄) は両モード対応、 Blues は dark mode で映えにくい。
❌ PDF 出力時のカラープロファイル不一致
matplotlib の savefig で PNG/PDF を出すと、 デフォルトでは sRGB 埋め込み無し。 印刷会社では CMYK 変換時に色がずれる。 学会論文では matplotlib.use('pgf') や PDF/X-1a 出力で対処する。

🎓 まとめ ── カラースケール 3 原則

原則 1: データ性質に合った種類を選ぶ

連続 → sequential、 中央あり → diverging、 カテゴリ → qualitative。 これを間違えると、 たとえ美しい配色でも誤読を招きます。

原則 2: 知覚均等 + CUD 対応を優先する

viridis、 cividis、 Okabe-Ito を「迷ったらこれ」のデフォルト集として記憶。 jet、 rainbow、 RdYlGn を捨てるだけで読み手の負担が激減します。

原則 3: カラーバー・凡例・単位を必ず添える

色の意味を読み取る手がかりがないと、 どんな良いカラースケールも無意味。 vmin/vmax 固定 + 単位明記 + cmap 名キャプション記載が三点セットです。

この 3 原則を守れば、 学術論文・ビジネスダッシュボード・公的データ可視化、 どの場面でも信頼性の高いカラースケール選択ができます。 SSDSE-B-2026 のような実データを使って、 ぜひ自分の手で複数の cmap を試し比べてみてください。

🗺 概念マップ

「カラースケール」を中心に、 連続スケール (viridis/cividis)・カテゴリスケール・発散スケール (RdBu)・jet/rainbow の不適切さ・コントラスト比 4.5:1 の WCAG 基準を結びつけた俯瞰図。 色覚多様性配慮を含めて選択する地図。

color scale 色のスケール選択 コントラスト比 4.5:1 viridis (逐次型) cividis jet/rainbow は使

上の概念マップは「カラースケール」を中心に、 viridis / cividis (知覚均等で色覚多様性に配慮) と「jet/rainbow は使うな」(視覚誤誘導の典型) を対比した俯瞰図である。 単に綺麗な色を選ぶのではなく、 「順序データには連続スケール、 カテゴリにはカテゴリカルスケール、 ゼロを境に正負があるなら発散スケール」という 3 種の使い分けが本質である。

🔗 隣接手法への橋渡し

「カラースケール」は数値 → 色の写像で、 上流のデータ範囲・分布の把握 (例: 歪み) と下流の色覚多様性配慮を組まないと、 平均的読者と色弱読者で別解釈が生まれる事故になる。

上流でデータ範囲とスケール (順序/発散/質的) を判断し、 並列の matplotlib/Plotly cmap を比較し、 下流で color blindness simulator により可読性を検証すれば、 SSDSE 都道府県マップを誰でも正しく読めるよう仕上げられる。

🌳 手法選択フロー

「カラースケール」を実際に使うとき、 何をどう選ぶかを順に判断する。 上から順に答えていくと、 使うべき手法と評価の仕方が決まる。

  1. 表したい量に方向はあるか
    大小だけなら明度が一方向に変わる連続スケール(viridis)。 基準値からのプラス・マイナスなら中央を白にする発散スケール(RdBu_r)で center=0。 順序の無いカテゴリなら色相で区別する。
  2. 基準となる値はどこか
    相関なら 0、 増減率なら 0、 偏差値なら 50。 基準を中央に置かないと、 わずかな差が濃い色になって誇張される。
  3. 複数の図を並べて比べるか
    比べるなら vminvmax を全図で固定する。 固定しないと、 同じ色が図ごとに違う値を意味してしまう。
  4. 色覚特性と印刷に耐えるか
    赤緑だけの区別は避け、 グレースケールにしても順序が残るかを確認する。 viridis 系は明度が単調なので、 白黒印刷でも大小が読める。

カラースケールは「どの値を基準に、 どちらへ濃くするか」を決める作業。 既定のまま使うと、 データではなくライブラリの都合が図の印象を決めてしまう。

🧭 解説深化 — パレット選びの陰に隠れた「スケール関数」: 値→[0,1] の写像が 47 県の見え方を決める

💡 直感 — カラースケールは「色の質」の前に「二段の写像」である

本ページ本文は「viridis か jet か」というパレット(色の質)の議論を厚く扱った。 だが読み手が地図やヒートマップから最初に受け取る印象を決めているのは、 実は色そのものではなく 数値を [0,1] に畳み込む写像関数 のほうだ。 カラースケールは常に二段構えで動く ── まず 値 → [0,1](正規化 normalize)、 次に [0,1] → 色(パレット colormap)。 パレットをどれだけ丁寧に選んでも、 一段目の写像が歪んでいれば、 47 都道府県の大半が同じ色に潰れてしまう。 逆に言えば、 同じ viridis のままでも 写像を線形から対数・分位へ切り替えるだけで、 見える情報量は激変する。 だから批判的な読み手は「何色を使ったか」より先に「どんな関数で値を色に割り当てたか」を疑う。

⚠️ 落とし穴(重要) — 歪んだ実データを線形スケールに載せると、色帯が「1 県の外れ値」に独占される

本文の落とし穴節は「jet の知覚非均等」「階級数の過多」「vcenter=0 の指定漏れ」を扱ったが、 未定量だったのが 右に歪んだ分布を線形正規化したときの色帯の崩壊だ。 SSDSE-B-2026 の A1101 総人口(2023年・47都道府県) で実測すると、 最小の 鳥取県 537 千人 から最大の 東京都 14,086 千人 まで 26.2 倍の開きがある(2 位は神奈川県 9,229、 3 位は大阪府 8,763)。 この列をそのまま線形正規化 (x − min)/(max − min) にかけると、 東京都だけが 1.0 を独占し、 残りは下端に押し潰される。 実測した内訳が次の表だ(すべて data/raw/SSDSE-B-2026.csv からの計算値)。

写像関数正規化値<0.1 の県数正規化値<0.2 の県数解釈
線形 (x−min)/(max−min)28 / 4737 / 47下位 10% の色帯に半数超が集中し、暗色で判別不能
対数 log10(x) を正規化5 / 4712 / 47桁差を吸収し、色が全域に散る

線形では 47 県中 28 県が色帯の下位 10% に、 37 県が下位 20% に潰れる。 パレットが viridis だろうと jet だろうと、 この 28 県はほぼ同じ暗色になり、 地図上で読み分けられない。 対数正規化に替えるだけで下位 10% は 5 県まで減り、 45 都道府県が識別可能な色差を持つ。 同じ問題は離散化(階級分け)でも起きる。 5 階級のコロプレスを作るとき、 等間隔(equal interval)で切ると階級人数は {最下位:37, 4, 3, 2, 最上位:1} ── 最下位階級に 37 県が押し込まれ、 最上位は東京 1 県のみ。 一方、 分位(quantile)で切れば {10, 9, 9, 9, 10} と各階級ほぼ均等になる(いずれも実測値)。 「どのパレットか」ではなく「どの写像・どの階級境界か」を明示しない図は、 都合の良い結論を無自覚に演出する。 これが色による嘘の最頻出パターンだ。

🚀 発展 — 「東京をどう扱うか」で写像を選ぶ 3 つの定石

歪んだ実データに対する写像の選択は、 結局外れ値(この例では東京都)をどう遇するかの問題に帰着する。 実務では次の 3 手を使い分ける。 (1) 対数正規化(LogNorm) ── 桁差そのものが意味を持つ量(人口・所得・面積)に。 東京を含めた全県を 1 枚の連続スケールに収めたいとき。 (2) ロバスト・クリッピング ── 下位 2%・上位 98% パーセンタイルで vmin/vmax を固定し、 東京は「振り切れ(≥上限)」として別色または数値ラベルで併記。 中間層の解像度を最大化したいとき。 (3) 分位・Jenks 離散化 ── 連続色をやめ、 各階級に等人数(または自然分断)で県を割り当てる。 「順位グループ」を伝えたいとき。 いずれの場合も、 凡例に写像方式(linear/log/quantile)と境界値を明記するのが誠実な作法だ。 なお発散(diverging)スケールを「全国平均との差」に使うときは、 この写像問題に加えて中心値の固定(本文の vcenter=0)も同時に効くので、 正規化と中心固定は別々の設定であることを意識したい。