論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
関係性の可視化
Network Visualization
可視化
別称: グラフ可視化

🔖 キーワード索引(拡充版)

🎨 直感 📐 力学モデル 📐 中心性 🔬 数式を言葉で読み解く 🧮 手計算 🐍 networkx 作図 🐍 中心性 🐍 force-directed 🐍 PageRank ⚠️ 落とし穴 🌐 関連手法 🔗 関連用語 📚 教材

💡 30 秒で分かる結論(拡充版)

📍 文脈ボックス

あなたが今見ているもの: 用語集 / 可視化カテゴリ / ネットワーク可視化 (Network Visualization)

前提: グラフ理論 / 隣接行列 / 中心性(あれば)

並列: 散布図 / ヒートマップ / ツリーマップ

応用: SNS 分析 / 推薦システム / 知識グラフ

🎨 直感で掴む

「人と人の友達関係」を紙に書くとき、 友達同士を線で結ぶ。 これがネットワーク可視化の全て。 ただし「どの位置に描くか」と「どの線を強調するか」で印象がガラリと変わるのが難しさ。

レイアウトには 4 大流派がある。 力学モデル (force-directed) は「エッジをばね、 ノードを反発電荷」と見なし、 物理シミュレーションでバランスする位置を探す。 階層 (hierarchical) は親子関係を縦方向に並べる。 円環 (circular) は全ノードを円周上に置く。 スペクトル (spectral) はラプラシアン行列の固有ベクトルで座標を決める。

レイアウト向くケース弱点
force-directed (Fruchterman-Reingold, ForceAtlas2)汎用、 クラスタが自然に分離毎回位置が変わる、 1000 ノード超で重い
hierarchical (Sugiyama)DAG、 系統樹、 組織図サイクルがあると破綻
circular全関係を「弦」で表現したいノード数 50 超で混雑
spectralクラスタ構造の数学的可視化直感解釈しにくい

📐 数式または定義

グラフ $G = (V, E)$ は頂点集合 $V$ と辺集合 $E \subseteq V \times V$ で定義される。 隣接行列 $A \in \{0,1\}^{n \times n}$ は $A_{ij}=1$ iff $(i,j) \in E$。

Fruchterman-Reingold 力学モデル: ノード間の力を反発と引力の和で計算する。

$$F_{\text{rep}}(i,j) = -\frac{k^2}{\|x_i - x_j\|}, \quad F_{\text{att}}(i,j) = \frac{\|x_i - x_j\|^2}{k} \cdot \mathbb{1}_{(i,j) \in E}$$

ここで $k = \sqrt{\text{area}/|V|}$ は最適距離。 反復で位置 $x_i$ を更新し、 全体エネルギーを最小化する。

中心性 4 種:

$$C_D(v) = \frac{\deg(v)}{n-1}, \quad C_C(v) = \frac{n-1}{\sum_{u \neq v} d(v,u)}$$ $$C_B(v) = \sum_{s \neq v \neq t} \frac{\sigma_{st}(v)}{\sigma_{st}}, \quad \text{PR}(v) = \frac{1-d}{n} + d \sum_{u \in N^{-}(v)} \frac{\text{PR}(u)}{|N^{+}(u)|}$$

🔬 数式を言葉で読み解く

記号意味具体例(47 都道府県ネットワーク)
$V$頂点集合 (ノード)$|V|=47$ (47 都道府県)
$E$辺集合 (エッジ)5 指標距離で「近い 5 県」を結ぶ → $|E|=153$
$A_{ij}$隣接行列要素東京-神奈川: 1、 北海道-沖縄: 0
$\deg(v)$ノードの次数長野県=9 (最多、 ハブ)、 北海道=5 (最少)
$C_B(v)$媒介中心性静岡県 0.498 → 大都市圏県群と中規模県群を繋ぐ「橋」
$\text{PR}(v)$PageRank長野県 0.053 (1位)、 関東圏 4 県が上位
$k$最適距離 (FR モデル)描画領域 100x100, ノード 47 → $k=\sqrt{10000/47}\approx14.6$
$d$PageRank ダンピング係数通常 0.85 (15% は全ノードへランダムジャンプ)

🧮 実値で計算してみる

SSDSE-B-2026 の 47 都道府県 × 5 指標(総人口 A1101 / 死亡数 A4200 / 出生数 A4101 / 65 歳以上人口 A1303 / 婚姻件数 A9101)を標準化し、 各県の「近い 5 県」をエッジで結んだネットワークの実値です。

ノード数 |V| = 47 エッジ数 |E| = 153 連結成分数 = 2 (サイズ 24 + 23) → 「人口規模の大きい県群」「小さい県群」が分離 平均次数 = 6.51 直径 (最大経路) = 6 平均最短経路長 = 2.59 ▼ 次数中心性 top3 長野県 : C_D = 0.391 (= 9/23) 宮城県 : C_D = 0.348 栃木県 : C_D = 0.348 ▼ 媒介中心性 top3 静岡県 : C_B = 0.498 ← 大都市圏県群と中規模県群を繋ぐ橋 兵庫県 : C_B = 0.258 新潟県 : C_B = 0.203 ▼ 近接中心性 top3 静岡県 : C_C = 0.523 新潟県 : C_C = 0.511 宮城県 : C_C = 0.500 ▼ PageRank top3 長野県 : PR = 0.0526 埼玉県 : PR = 0.0505 千葉県 : PR = 0.0505

💬 読み解き: 「次数」と「PageRank」は似た上位を出すが、 「媒介」は別。 静岡県の媒介 0.498 はネットワーク全体の媒介中心性のほぼ半分を独占しており、 「ここを切ると大都市圏 9 県のグループと中規模県グループが断絶する」要衝(実際に静岡県を除去すると最大成分が 2 つに割れる)。

🐍 Python 実装

① networkx で 47 都道府県ネットワーク作図

🎯 このコードでやること: SSDSE-B-2026 の 47 都道府県を 5 指標で類似度ネットワーク化し、 force-directed レイアウトで描く。

📥 入力データ (SSDSE-B-2026.csv 抜粋):

Prefecture A1101 A4103 A1303 A9101 A4200 0 北海道 5092000 1.06 1681000 17281 75120 1 青森県 1184000 1.23 417000 3326 20835 2 岩手県 1163000 1.16 407000 3376 19612 3 宮城県 2264000 1.07 662000 7899 28640 4 秋田県 914000 1.10 357000 2302 17517 ... 12 東京都 14086000 0.99 3205000 71774 137241 30 鳥取県 537000 1.44 179000 1810 8290
 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
import pandas as pd
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
from scipy.spatial.distance import squareform, pdist

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df.iloc[:,0]==2023].reset_index(drop=True)

# 5 指標を標準化
feats = ['A1101','A4200','A4101','A1303','A9101']
X = df[feats].astype(float).values
Z = (X - X.mean(0)) / X.std(0)
D = squareform(pdist(Z, 'euclidean'))

# 各県の「近い 5 県」をエッジ化
G = nx.Graph()
for i in range(47):
    G.add_node(i, name=df['Prefecture'][i])
for i in range(47):
    for j in np.argsort(D[i])[1:6]:
        G.add_edge(int(i), int(j))

print(f'ノード数 {G.number_of_nodes()}  エッジ数 {G.number_of_edges()}')
pos = nx.spring_layout(G, seed=42, k=0.5)
plt.figure(figsize=(10,8))
nx.draw_networkx_nodes(G, pos, node_size=300, node_color='#4DB6AC')
nx.draw_networkx_edges(G, pos, alpha=0.4)
nx.draw_networkx_labels(G, pos, labels={i:df['Prefecture'][i] for i in range(47)}, font_size=8, font_family='Hiragino Sans')
plt.axis('off'); plt.tight_layout(); plt.savefig('prefnet.png', dpi=120)

📤 実行結果:

ノード数 47 エッジ数 153 prefnet.png (約 180 KB, 連結成分 2 が視覚的に分離して描画される)

💬 読み解き: spring_layout はばねモデル。 関東圏 (東京・神奈川・千葉・埼玉) が中央に固まり、 北海道・沖縄が端に弾き飛ばされる。 47 ノードは「ちょうど読める」サイズ。

② 中心性 4 種を計算

🎯 このコードでやること: ① で作ったネットワーク G に対し、 4 種類の中心性を計算して比較する。

📥 入力データ: ① で生成した G (47 ノード、 153 エッジの無向グラフ)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import networkx as nx
# (G, df は ① の続き)

# 連結成分が複数ある時は最大成分を抽出
largest = max(nx.connected_components(G), key=len)
G2 = G.subgraph(largest).copy()

deg = nx.degree_centrality(G2)
bet = nx.betweenness_centrality(G2)
clo = nx.closeness_centrality(G2)
pr  = nx.pagerank(G2, alpha=0.85)

def top3(c, label):
    items = sorted(c.items(), key=lambda x:-x[1])[:3]
    print(f'\n[{label}] top3:')
    for i, v in items:
        print(f'  {df["Prefecture"][i]:6s}  {v:.4f}')

for c, lbl in [(deg,'degree'), (bet,'betweenness'), (clo,'closeness'), (pr,'PageRank')]:
    top3(c, lbl)

print(f'\n直径={nx.diameter(G2)}, 平均最短経路={nx.average_shortest_path_length(G2):.3f}')

📤 実行結果:

[degree] top3: 長野県 0.3913 宮城県 0.3478 栃木県 0.3478 [betweenness] top3: 静岡県 0.4980 兵庫県 0.2582 新潟県 0.2027 [closeness] top3: 静岡県 0.5227 新潟県 0.5111 宮城県 0.5000 [PageRank] top3: 長野県 0.0526 埼玉県 0.0505 千葉県 0.0505 直径=6, 平均最短経路=2.591

💬 読み解き: 4 種の中心性で「1 位」が違うことに注意。 静岡県は媒介と近接で 1 位だが、 PageRank では 5 位以下。 「目的に応じた中心性の使い分け」がネットワーク可視化の核心。

③ レイアウト 4 種を並べて比較

🎯 このコードでやること: 同じネットワーク G を 4 種のレイアウト (spring / circular / kamada_kawai / spectral) で並べて描き、 印象差を確認する。

📥 入力データ: ① の G。 レイアウト関数の入出力は {node_id: (x, y)} の辞書。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import networkx as nx, matplotlib.pyplot as plt

layouts = {
    'spring (force-directed)': nx.spring_layout(G, seed=42),
    'circular':                 nx.circular_layout(G),
    'kamada_kawai':             nx.kamada_kawai_layout(G),
    'spectral':                 nx.spectral_layout(G),
}
fig, axes = plt.subplots(2, 2, figsize=(14, 12))
for ax, (name, pos) in zip(axes.flat, layouts.items()):
    nx.draw_networkx_nodes(G, pos, node_size=120, node_color='#4DB6AC', ax=ax)
    nx.draw_networkx_edges(G, pos, alpha=0.3, ax=ax)
    ax.set_title(name, fontsize=14); ax.axis('off')
plt.tight_layout(); plt.savefig('layouts.png', dpi=110)
print('保存: layouts.png')

📤 実行結果:

保存: layouts.png (2x2 グリッド、 spring と kamada_kawai は似た「クラスタが見える」配置、 circular は均等配置で関係性が読みづらい、 spectral は固有値の符号で離散的)

💬 読み解き: 「同じグラフでも見え方は無数」。 デフォルトの spring で違和感があったら kamada_kawai を試す。 報告書では 1 種類に決め打ちし、 「なぜそのレイアウトか」を 1 文で必ず添える。

④ PageRank で「県の重要度ランキング」

🎯 このコードでやること: PageRank をノードサイズに反映させて「重要度の見える化」をする。

📥 入力データ: ① の Gdf['Prefecture']

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import networkx as nx, matplotlib.pyplot as plt

pr = nx.pagerank(G, alpha=0.85, max_iter=200)
sizes = [pr[i] * 12000 for i in G.nodes()]
colors = [pr[i] for i in G.nodes()]

pos = nx.spring_layout(G, seed=42, k=0.6)
plt.figure(figsize=(11, 9))
nodes = nx.draw_networkx_nodes(G, pos, node_size=sizes, node_color=colors,
                                cmap=plt.cm.viridis, alpha=0.9)
nx.draw_networkx_edges(G, pos, alpha=0.25)
labels = {i: f'{df["Prefecture"][i]}\n{pr[i]:.3f}' for i in G.nodes() if pr[i] > 0.030}
nx.draw_networkx_labels(G, pos, labels=labels, font_size=8, font_family='Hiragino Sans')
plt.colorbar(nodes, label='PageRank')
plt.axis('off'); plt.tight_layout(); plt.savefig('pagerank.png', dpi=120)

# 上位 10 件を表で表示
ranking = sorted(pr.items(), key=lambda x:-x[1])[:10]
for rank, (i, v) in enumerate(ranking, 1):
    print(f'{rank:2d}. {df["Prefecture"][i]:6s}  PR={v:.4f}')

📤 実行結果:

1. 長野県 PR=0.0269 2. 山梨県 PR=0.0263 3. 奈良県 PR=0.0261 4. 岩手県 PR=0.0261 5. 大分県 PR=0.0258 6. 埼玉県 PR=0.0258 7. 千葉県 PR=0.0258 8. 兵庫県 PR=0.0255 9. 栃木県 PR=0.0250 10. 静岡県 PR=0.0248 pagerank.png 保存 (上位ノードが viridis 黄、 下位が紫で大小)

💬 読み解き: PageRank は「重要な県とつながっている県も重要」とする再帰的指標。 長野県は北陸・中部・関東の橋渡しになるため上位。 東京は意外に低い (人口最大なのに) — 「似た県が少ない」ためエッジが少なく PageRank が下がる。

⚠️ 落とし穴

  1. スパゲッティグラフ: ノード 500 超、 エッジ 2000 超になると人間は読めない。 解決策は ① 中心性で絞り込み、 ② コミュニティ検出してサブグラフに分割、 ③ アグリゲートして「ノードのノード」に縮約する。
  2. レイアウト依存: 同じグラフでも spring / kamada_kawai / circular で印象が変わり、 「クラスタが見える」と思っても実はレイアウトのアーティファクトの場合がある。 必ず複数レイアウトで確認する。
  3. シード依存: force-directed は乱数初期化のため、 seed= を固定しないと毎回違う図が出る。 報告書では必ず seed を記録する。
  4. 無向 vs 有向の取り違え: 「友達関係」は無向、 「フォロー関係」は有向。 PageRank は元来有向グラフ向け。 無向に適用すると結果は次数中心性とほぼ同じになる。
  5. 類似度の閾値 sensitivity: 「近い 5 県を結ぶ」と「距離 1.5 以下を結ぶ」では結果が全く違う。 閾値を変えて結果が大きくブレないかロバストネスを確認する。

前提: グラフ理論 / 行列 / 距離 / 類似度

並列: 散布図 / ヒートマップ / ツリーマップ / 弦図 / サンキー図

発展: コミュニティ検出 / グラフ埋め込み / GNN / PageRank / 推薦システム / SNS 分析

📚 関連グループ教材

🗺 概念マップ

ネットワーク可視化 ├── データ表現 │ ├── 隣接行列 A (n×n) │ ├── 隣接リスト │ └── エッジリスト (src, dst, weight) ├── レイアウト │ ├── force-directed │ │ ├── Fruchterman-Reingold │ │ ├── Kamada-Kawai │ │ └── ForceAtlas2 (Gephi) │ ├── hierarchical (Sugiyama) │ ├── circular │ └── spectral ├── 中心性 │ ├── degree ← 単純な「次数」 │ ├── closeness ← 「距離が短い」 │ ├── betweenness ← 「経路の橋」 │ ├── eigenvector ← 「重要な隣人」 │ └── PageRank ← 「Web のリンク重要度」 ├── コミュニティ検出 │ ├── Louvain │ ├── Girvan-Newman │ └── Modularity 最大化 ├── ツール │ ├── networkx (Python) │ ├── igraph (R/Python) │ ├── Gephi (GUI) │ ├── Cytoscape (GUI、 生物学系) │ └── D3.js (Web) └── 応用 ├── SNS 分析 ├── 推薦システム ├── 知識グラフ └── 生体ネットワーク (PPI)

📊 中心性 5 種の使い分けマトリクス

中心性定義向く解釈弱点
次数 (degree)$\deg(v)/(n-1)$「友達が多い」局所的、 ネットワーク全体は見ない
近接 (closeness)$\frac{n-1}{\sum d(v,u)}$「みんなに近い」非連結だと無限大、 サイズ依存
媒介 (betweenness)$\sum \sigma_{st}(v)/\sigma_{st}$「橋・ボトルネック」計算コスト $O(nm)$ で大規模に弱い
固有ベクトル (eigenvector)$Av = \lambda v$ の主固有ベクトル「重要な人とつながる人」無向、 連結グラフでないと不安定
PageRankダンピング付き固有ベクトル「ハイパーリンク的重要度」ダンピング係数の選択に依存

🛠 ツール選定ガイド

ツール向く用途最大ノード目安学習コスト
networkxPython パイプライン、 教育~5,000低 (matplotlib 連携)
igraph (Python/R)大規模解析、 C 実装で高速~100,000
GephiGUI、 ForceAtlas2、 論文図用~50,000低 (Java GUI)
Cytoscape生物ネットワーク (PPI, パスウェイ)~30,000
D3.jsWeb 上のインタラクティブ可視化~3,000高 (JavaScript SVG)
deck.gl / Sigma.jsWebGL ベース、 超大規模~1,000,000

🧪 SSDSE-B-2026 拡張: コミュニティ検出

先ほどの 47 都道府県ネットワーク (153 エッジ) に Louvain 法を適用すると、 5 個のコミュニティに自然分割される (random_state=42 で Q = 0.679)。 各コミュニティが「人口・死亡・出生・高齢人口・婚姻の規模パターンが似た県群」を表現する。

⑤ python-louvain でコミュニティ検出

🎯 このコードでやること: Louvain 法で modularity 最大化のコミュニティを検出し、 色分けして描画する。

📥 入力データ: ① の G

 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
import community as community_louvain  # pip install python-louvain
import networkx as nx, matplotlib.pyplot as plt

partition = community_louvain.best_partition(G, random_state=42)
modularity = community_louvain.modularity(partition, G)
print(f'コミュニティ数 = {len(set(partition.values()))}, modularity Q = {modularity:.4f}')

# 各コミュニティのメンバーを表示
from collections import defaultdict
groups = defaultdict(list)
for node, comm in partition.items():
    groups[comm].append(df['Prefecture'][node])
for c, members in sorted(groups.items()):
    print(f'  C{c}: {members}')

# 描画
pos = nx.spring_layout(G, seed=42)
plt.figure(figsize=(11, 9))
cmap = plt.cm.tab20
node_colors = [cmap(partition[i]) for i in G.nodes()]
nx.draw_networkx_nodes(G, pos, node_size=350, node_color=node_colors, alpha=0.9)
nx.draw_networkx_edges(G, pos, alpha=0.3)
labels = {i: df['Prefecture'][i] for i in G.nodes()}
nx.draw_networkx_labels(G, pos, labels=labels, font_size=8, font_family='Hiragino Sans')
plt.title(f'Louvain communities (Q={modularity:.3f})')
plt.axis('off'); plt.tight_layout(); plt.savefig('communities.png', dpi=120)

📤 実行結果:

コミュニティ数 = 5, modularity Q = 0.6788 C0: ['北海道', '埼玉県', '千葉県', '東京都', '神奈川県', '愛知県', '大阪府', '兵庫県', '福岡県'] C1: ['福井県', '山梨県', '鳥取県', '島根県', '徳島県', '高知県', '佐賀県'] C2: ['宮城県', '茨城県', '新潟県', '長野県', '静岡県', '京都府', '広島県'] C3: ['青森県', '岩手県', '秋田県', '山形県', '富山県', '石川県', '滋賀県', '奈良県', '和歌山県', '山口県', '香川県', '愛媛県', '長崎県', '大分県', '宮崎県', '沖縄県'] C4: ['福島県', '栃木県', '群馬県', '岐阜県', '三重県', '岡山県', '熊本県', '鹿児島県'] communities.png 保存

💬 読み解き: modularity Q=0.68 は強いコミュニティ構造を示す (0.3 以上で意味あり、 0.6 以上で明瞭)。 5 コミュニティは地理的区分ではなく人口規模の階層にほぼ一致する — C0 は人口 500 万超の大都市圏 9 道府県、 C2 と C4 は中規模県、 C1 と C3 は小規模県。 5 指標 (人口・出生・死亡・高齢人口・婚姻) がいずれも人口規模に比例する「実数系」のため、 規模の近い県同士が結ばれるのは当然の帰結。 地理的なまとまりを見たいなら高齢化率・合計特殊出生率のような「比率系」指標で構築し直す必要がある。

📈 実務でよくある質問

Q: 1000 ノード超のグラフが読めません
A: 3 段階で対応する。 (1) 次数下位を除外 — 次数 1-2 のノードは「葉」で構造に寄与しないことが多い。 (2) k-core 分解 — 中核構造を取り出す。 (3) コミュニティ集約 — Louvain でコミュニティ検出し、 各コミュニティを 1 ノードに縮約。
Q: 動的ネットワーク (時系列で関係が変わる) はどう描く?
A: スナップショット方式 (時刻 t1, t2, ... 毎に静止画) か、 アニメーション方式 (Gephi Timeline、 D3 Force) が定石。 全期間を 1 枚に重ねる「時間集約」は情報損失が大きく非推奨。
Q: PageRank と HITS の違いは?
A: PageRank はノードに 1 値、 HITS は「Hub スコア」と「Authority スコア」の 2 値を割り当てる。 ハイパーリンク Web の文脈では HITS のほうが解釈しやすい場面もある。 SNS の影響力分析では PageRank が標準。
Q: エッジに重みがあると何が変わる?
A: 中心性計算で重みを考慮する版 (weighted_betweenness 等) が使える。 ただし重みが「距離」なのか「強さ」なのかで逆数を取るか決まる。 networkx は weight='distance' 引数で挙動切替。
Q: 隣接行列とエッジリスト、 どちらで保存すべき?
A: 疎グラフ (エッジ密度 5% 未満) なら エッジリスト。 メモリ効率良し、 SQL/NoSQL に格納しやすい。 密グラフや行列演算が多いなら 隣接行列 (numpy/scipy.sparse)。 PageRank の冪乗法は隣接行列の方が速い。
Q: GNN とネットワーク可視化の関係は?
A: GNN (Graph Neural Network) はノード/エッジ/グラフ全体の特徴量を学習する。 学習後の埋め込みベクトルを UMAP/t-SNE で 2 次元化すれば可視化できる。 「ネットワーク構造そのもの」と「学習表現」は別物だが、 両者を併用すると強力。
Q: 円形配置 (circular) はどんな時に使う?
A: 「全ノードを等価に扱いたい」「弦 (chord) でやり取りを表現したい」場合。 例: 1 日の電話通話量を 24 ノード (時間) で結ぶ 弦図 (chord diagram)、 国際貿易の輸出入を国別に結ぶ circos plot

📐 数学的補足: 隣接行列の固有値とグラフ性質

隣接行列 $A$ の固有値分解は、 ネットワーク全体の性質を一気に語る強力な道具。 SSDSE-B-2026 ネットワーク (47×47 隣接行列) で具体的に確認できる。

意味SSDSE-B ネットワーク値
$\lambda_1$ (最大固有値)スペクトル半径 = ハブの強さ約 7.0 (平均次数 6.5 とほぼ一致)
$v_1$ (主固有ベクトル)固有ベクトル中心性長野・宮城など高次数県が大値
$\lambda_2$ (Fiedler 値、 ラプラシアン)代数的連結性2 連結成分なので $\lambda_2 = 0$ (要 union)
$v_2$ (Fiedler ベクトル)スペクトラルクラスタリングの分割軸符号で「人口規模 大/小」の 2 クラスタに分かれる
トレース $\text{tr}(A^k)$長さ k の閉路数$\text{tr}(A^3)/6$ = 三角形数

🎯 失敗事例集

事例 1: 10,000 ノード SNS グラフを spring_layout で描いて固まった
原因: spring_layout は $O(n^2)$ 反復で重い。 対策: nx.spring_layout(G, iterations=10) で反復数を絞る、 もしくは ForceAtlas2 (Gephi または fa2 Python パッケージ) を使う。 5000 ノード 5 秒程度で収束する。
事例 2: 「ハブが多い」と思ったら次数分布の対数スケール抜けだった
原因: 線形スケールで次数分布をプロットしたため、 大次数ノードが少ないことに気づかなかった。 対策: 必ず plt.loglog() でべき乗則 ($P(k) \propto k^{-\gamma}$) を確認する。 SNS は通常 $\gamma \approx 2{-}3$。
事例 3: PageRank 上位がスパムアカウントだらけだった
原因: PageRank は「リンクが多い = 重要」と単純に信じる。 リンク農場 (link farm) のスパムに脆弱。 対策: TrustRank (信頼ノードからの伝搬)、 SALSA (双方向構造) を併用する。
事例 4: 報告書にレイアウト指定なしで載せたら、 査読で再現できないと言われた
原因: spring_layout は乱数初期化。 seed 固定がなければ毎回違う図。 対策: 必ず seed=42 を指定、 ファイル名に _seed42 等を付記、 .gephi/.graphml ファイルも添付する。

📚 学習リソース

🔄 まとめ: 47 都道府県ネットワークから学ぶ 5 つの教訓

  1. 類似度ネットワークは「閾値」で形が変わる: 「近い 5 県」を「近い 3 県」に変えるとエッジが激減し、 連結成分数が増える。 必ずロバストネス確認。
  2. 中心性は 1 つだけ見ない: SSDSE-B では次数 1 位 (長野) と媒介 1 位 (静岡) が違う。 「どの観点で重要か」を明示。
  3. レイアウトはストーリーの一部: spring と circular で読者の印象は全く違う。 「何を強調したいか」で選択する。
  4. コミュニティ検出は「事前知識との整合」を確認: 5 コミュニティが人口規模の階層と一致したのは「使った 5 指標がすべて規模に比例する実数系」の証拠。 地理的まとまりを期待していたなら、 比率系指標(高齢化率・出生率など)への切り替えを検討する。
  5. ネットワークは仮説生成、 因果は別の手法で: 「東京と神奈川がつながる」のは似ているからで、 因果関係ではない。 因果は介入試験や DiD で別途検証。

🔬 詳細: Fruchterman-Reingold アルゴリズム逐次解説

FR レイアウトの内部動作を 1 反復ずつ追うと、 なぜ「自然な」配置が得られるかが分かる。

ステップ操作物理イメージ
1全ノードをランダム配置 $x_i^{(0)} \in [0,W]^2$空間にバラバラに撒く
2最適距離 $k = C \sqrt{\text{area}/|V|}$ を決める「これくらい離れていたい」の目安
3全ペアで反発力 $F_r = -k^2 / \|d\|$ を計算電荷同士の反発
4エッジ両端で引力 $F_a = \|d\|^2 / k$ を計算ばねの引っ張り
5合力で位置を更新 $x_i \leftarrow x_i + (F_r + F_a) \cdot t$temperature t で動きを制御
6temperature を $t \leftarrow 0.95 t$ で減衰 (simulated annealing)徐々に動きを小さく
73-6 を 50-100 反復エネルギー最小に収束

🎓 用語ミニ辞典

ノード (node, vertex)
ネットワークの「点」。 47 都道府県の例では各県が 1 ノード。
エッジ (edge, link, arc)
ノード間を結ぶ「線」。 無向 (undirected) と有向 (directed) がある。 重み付きエッジは数値を持つ。
次数 (degree)
ノードに繋がるエッジの数。 有向では入次数 (in-degree)、 出次数 (out-degree) に分かれる。
経路 (path)
ノード列 $v_0, v_1, ..., v_k$ で隣接ノード間がエッジを持つもの。 最短経路 (geodesic path) は最も短いもの。
直径 (diameter)
全ペア間の最短経路のうち最大値。 SSDSE-B ネットワークでは 6。
クリーク (clique)
全ノードが互いに繋がる部分グラフ。 「完全な相互友達グループ」。
クラスタ係数 (clustering coefficient)
「友達の友達も友達」率。 ノードの三角形密度。 SNS では 0.2-0.5、 ランダムグラフでは 0.01 程度。
スモールワールド (small world)
「平均最短経路は短く、 クラスタ係数は高い」性質。 Watts-Strogatz モデル。 SSDSE-B ネットワークは平均経路 2.59 で典型的スモールワールド。
スケールフリー (scale-free)
次数分布がべき乗則 $P(k) \propto k^{-\gamma}$ に従う。 Barabási-Albert モデル。 Web、 SNS で観測される。
アソータティビティ (assortativity)
「高次数ノードが高次数ノードと繋がる」傾向。 正なら同類好み (homophily)、 負なら逆。

🌍 多分野での応用事例

分野ノードエッジ主な解析
SNSユーザーフォロー / 友達インフルエンサー検出 (PageRank)、 コミュニティ検出
交通駅 / 空港路線最短経路 (Dijkstra)、 ハブ空港の特定
生物 (PPI)タンパク質相互作用疾患関連タンパク質予測、 モジュール解析
電力網変電所送電線故障伝搬解析、 ロバストネス評価
論文引用論文引用関係 (有向)研究分野特定、 古典論文の同定
金融銀行貸借関係システミック・リスク (too big to fail) 検出
推薦システムユーザー / 商品購入履歴 (二部グラフ)協調フィルタリング、 ランダムウォーク
疫学個人接触履歴感染シミュレーション (SIR モデル)、 隔離戦略

⚡ パフォーマンス目安

操作計算量1000 ノード時の目安
次数中心性$O(n + m)$10 ms 未満
媒介中心性 (Brandes)$O(nm)$ for unweighted1-5 秒
近接中心性$O(n(n+m))$数秒
PageRank (冪乗法 50 反復)$O(50 m)$100 ms
Louvain コミュニティ検出$O(n \log n)$500 ms
spring_layout 50 反復$O(50 n^2)$5-30 秒
kamada_kawai_layout$O(n^2)$10-60 秒
ForceAtlas2 (fa2)$O(n \log n)$ (Barnes-Hut)1-3 秒

📌 チェックリスト: 良いネットワーク図の 12 条件

  1. タイトルがある (「47 都道府県の人口指標類似度ネットワーク」など具体的に)
  2. 使ったレイアウト名・seed 値が記載されている
  3. ノード数・エッジ数が明示されている
  4. エッジ重みの意味 (距離か類似度か) が分かる
  5. ノードラベルは重要なノードだけに絞り、 重なりを避けている
  6. カラースケールに凡例がある (PageRank、 コミュニティ ID 等)
  7. ノードサイズに意味がある場合は凡例がある
  8. ネットワークが連結か非連結かが分かる
  9. 解像度が 100 dpi 以上 (印刷可能)
  10. 白黒印刷でも区別できる (色だけで意味を持たせない)
  11. キャプションに「何を発見できるか」が 1 文ある
  12. データソース・前処理が再現可能 (コード添付)

🧪 拡張演習: 重み付きエッジで関係の強弱を表現

これまでは「近い 5 県を 0/1 で結ぶ」二値エッジだった。 ここでは距離 $d_{ij}$ から類似度 $s_{ij} = \exp(-d_{ij}^2 / 2\sigma^2)$ をエッジ重みに使う Gaussian kernel グラフを構築する。

⑥ 重み付き隣接行列とヒートマップ

🎯 このコードでやること: 47x47 の距離行列から Gaussian kernel で類似度行列を作り、 ヒートマップとして可視化する。

📥 入力データ: ① で計算した D (47x47 ユークリッド距離行列)。

 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
import numpy as np, matplotlib.pyplot as plt

# D は ① で作った距離行列 (47x47)
sigma = D[D>0].mean()  # 平均距離をスケールに
S = np.exp(-D**2 / (2 * sigma**2))
np.fill_diagonal(S, 0)  # 自己ループ削除
print(f'類似度行列 形状: {S.shape}, 最大値: {S.max():.3f}, 平均: {S[S>0].mean():.3f}')

# 県名でラベル付け
prefs = df['Prefecture'].values
order = np.argsort(df['A1101'].values)[::-1]  # 人口で降順並べ替え
S2 = S[order][:, order]
prefs2 = [prefs[i] for i in order]

# ヒートマップ
plt.figure(figsize=(10, 9))
plt.imshow(S2, cmap='YlOrRd', aspect='auto')
plt.xticks(range(47), prefs2, rotation=90, fontsize=7, fontfamily='Hiragino Sans')
plt.yticks(range(47), prefs2, fontsize=7, fontfamily='Hiragino Sans')
plt.colorbar(label='類似度 exp(-d²/2σ²)')
plt.title('47 都道府県類似度ヒートマップ (人口降順)')
plt.tight_layout(); plt.savefig('simheatmap.png', dpi=120)

# 類似度上位 5 ペアを表示
import itertools
pairs = []
for i, j in itertools.combinations(range(47), 2):
    pairs.append((S[i,j], prefs[i], prefs[j]))
pairs.sort(reverse=True)
print('\n類似度 top5 ペア:')
for s, a, b in pairs[:5]:
    print(f'  {a} - {b}: {s:.4f}')

📤 実行結果:

類似度行列 形状: (47, 47), 最大値: 1.000, 平均: 0.675 類似度 top5 ペア: 徳島県 - 高知県: 0.9998 山梨県 - 佐賀県: 0.9998 山口県 - 愛媛県: 0.9998 島根県 - 徳島県: 0.9998 群馬県 - 岐阜県: 0.9998 simheatmap.png 保存 (左上が東京・神奈川などの大都市、 右下が鳥取・島根)

💬 読み解き: 類似度が高いペアは「人口・出生・高齢化の規模が似た小県」同士。 徳島-高知のような隣県だけでなく、 山梨-佐賀のように地理的に遠いペアも並ぶ — 結ばれているのは距離ではなく規模の近さだ。 ヒートマップの右下 (小県群) が高類似度ブロックを形成し、 左上 (大都市) は互いに似ていない (東京は唯一無二)。 これが先ほどの「東京の PageRank が低い」現象の正体。 なお σ を平均距離に取ると近いペアの類似度が 1 近くに飽和するため、 重みの分解能が欲しいときは σ を小さく (例: 距離の中央値の半分) 調整する。

🔍 視覚エンコーディングの選択

表現したい属性推奨エンコーディング具体例
ノードの重要度 (連続値)サイズ + 色 (順序尺度)PageRank → 半径と viridis 色
ノードのカテゴリ色 (名義尺度、 tab10 等)コミュニティ ID
ノードの 2 次元属性形状 + 色性別 (○/□) × 年代 (色)
エッジの強さ線の太さ + 透明度通話回数 → 太さ、 alpha
エッジの方向矢印 + 曲線フォロー関係
エッジのカテゴリ色 (3 種類まで)「友達」「家族」「同僚」
時系列アニメーション or small multiples月ごとの SNS スナップショット

📜 ライセンスとデータ取り扱い

🎯 ⑦ 知名度ノードに集中するエッジを「束ねる」(edge bundling)

🎯 このコードでやること: holoviews + datashader でエッジを束ね、 47 都道府県の関係を「川の流れ」のように描く。

📥 入力データ: ① の G を holoviews 形式のノード/エッジ DataFrame に変換。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import pandas as pd, networkx as nx
import holoviews as hv
from holoviews.operation.datashader import bundle_graph
hv.extension('matplotlib')

# G, df, pos は ① の続き
nodes_df = pd.DataFrame({
    'id':   list(G.nodes()),
    'x':    [pos[i][0] for i in G.nodes()],
    'y':    [pos[i][1] for i in G.nodes()],
    'name': [df['Prefecture'][i] for i in G.nodes()],
})
edges_df = pd.DataFrame([(u, v) for u, v in G.edges()], columns=['source','target'])

graph = hv.Graph((edges_df, hv.Nodes(nodes_df, kdims=['x','y','id'])))
bundled = bundle_graph(graph)
hv.save(bundled.opts(node_size=10, edge_alpha=0.5),
        'bundled.png', fmt='png', size=400)
print('保存: bundled.png  (エッジが滑らかな曲線で束ねられた図)')

📤 実行結果:

保存: bundled.png (エッジが滑らかな曲線で束ねられた図) 153 エッジが十数本の「太い束」に集約され、 大都市圏県群と中規模県群のつながりの幹が見える

💬 読み解き: 通常の描画では「直線エッジの重なり」で読めなくなる 100+ エッジも、 edge bundling で 10-15 本の太い流線に集約できる。 飛行機の航空路図、 SNS の情報伝播ヒート図に有効。

📊 ⑧ ハイブリッド: 中心性 + コミュニティ + サイズ

🎯 このコードでやること: 1 枚の図に (ノード色=コミュニティ ID、 ノードサイズ=PageRank、 エッジ太さ=類似度) の 3 次元情報を載せる。

📥 入力データ: ① の G、 ② の pr、 ⑤ の partition、 ⑥ の S。

 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
42
43
44
45
46
47
48
# ── この抜粋で使うグラフを用意します ──
# 47 都道府県を「指標が似ている県どうしを結んだ網」として作る。
import numpy as np, pandas as pd
import matplotlib.pyplot as plt, networkx as nx
from sklearn.preprocessing import StandardScaler
from sklearn.metrics.pairwise import cosine_similarity

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]          # 2023 年の 47 都道府県
df = df.reset_index(drop=True)
_X = StandardScaler().fit_transform(df[['A1101', 'A1303', 'A4101', 'L3221']].astype(float))
S = cosine_similarity(_X)
np.fill_diagonal(S, 0)
G = nx.Graph()
G.add_nodes_from(range(len(df)))
for _i in range(len(df)):
    for _j in range(_i + 1, len(df)):
        if S[_i, _j] > 0.8:
            G.add_edge(_i, _j, weight=float(S[_i, _j]))
pr = nx.pagerank(G, alpha=0.85, max_iter=200)
# community_louvain(python-louvain)はブラウザに無いので networkx の実装を使う
_comms = nx.community.greedy_modularity_communities(G)
partition = {n: c for c, grp in enumerate(_comms) for n in grp}


pos = nx.spring_layout(G, seed=42, k=0.6)
plt.figure(figsize=(13, 10))

# ノード: 色=コミュニティ、 サイズ=PageRank
cmap = plt.cm.tab10
node_colors = [cmap(partition[i] % 10) for i in G.nodes()]
node_sizes  = [pr.get(i, 0.01) * 14000 for i in G.nodes()]  # 第 2 成分は最小サイズ

# エッジ: 太さ=類似度 S
edge_widths = [S[u, v] * 3 for u, v in G.edges()]

nx.draw_networkx_edges(G, pos, width=edge_widths, alpha=0.35)
nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color=node_colors, alpha=0.9)
labels = {i: df['Prefecture'][i] for i in G.nodes() if pr.get(i, 0) > 0.025}  # pr は最大成分のみ
nx.draw_networkx_labels(G, pos, labels=labels, font_size=8, font_family='Hiragino Sans')

# 凡例
for c in sorted(set(partition.values())):
    plt.scatter([], [], c=[cmap(c % 10)], label=f'コミュニティ {c}', s=100)
plt.legend(loc='upper left', fontsize=9)
plt.title('47 都道府県ネットワーク (色=コミュニティ、 サイズ=PageRank、 太さ=類似度)')
plt.axis('off'); plt.tight_layout(); plt.savefig('hybrid.png', dpi=130)
print('保存: hybrid.png  (3 次元情報を 1 枚に圧縮)')

📤 実行結果:

保存: hybrid.png コミュニティ 0 (大都市圏 9 道府県: 北海道・埼玉・千葉・東京・神奈川・愛知・大阪・兵庫・福岡) コミュニティ 1 (小規模 7 県: 福井・山梨・鳥取・島根・徳島・高知・佐賀) コミュニティ 2 (中規模 7 県: 宮城・茨城・新潟・長野・静岡・京都・広島) コミュニティ 3 (小規模 16 県: 青森・岩手・秋田・山形・富山・石川・滋賀・奈良ほか) コミュニティ 4 (中規模 8 県: 福島・栃木・群馬・岐阜・三重・岡山・熊本・鹿児島) ラベルは PageRank > 0.025 のノード (最大成分 24 県)、 長野・埼玉・千葉が大ノード (PageRank 上位)

💬 読み解き: 3 つの情報を 1 図に重ねるのは Tufte の「データ密度」哲学。 色とサイズで主要情報を、 エッジ太さで補助情報を伝える。 ただし4 次元以上は読めないので、 形状/輪郭/透明度などをさらに追加するのは NG。

🌐 ハンズオン提案: あなた自身のデータで試す

SSDSE-B-2026 のネットワーク化を学んだら、 次は自分の手元データで試したい。 以下のアイデアが入門に最適。

  1. クラスメイトの履修科目: 学生 (ノード) と科目 (ノード) の二部グラフ → 「友達かもしれない人」の発見
  2. 論文の引用関係: Google Scholar から取得した自分の卒業研究関連論文 50 本 → 「古典論文」と「最新論文」の構造
  3. 共著ネットワーク: dblp.org から研究者の共著グラフ → 「コミュニティ検出」で研究室サブグループの発見
  4. カードゲームのデッキ: ポケモンカードや MTG のデッキ構成 → 「シナジーのあるカード」の同時採用パターン
  5. 家計簿の支出カテゴリ: 1 ヶ月の支出を「カテゴリ」(食費、 交通費等) × 「曜日」の二部グラフ → 「金曜は外食が多い」など発見

🧰 トラブルシューティング集

症状原因解決
日本語ラベルが □□□matplotlib のデフォルトフォントが日本語非対応font_family='Hiragino Sans' (Mac) / 'Yu Gothic' (Win) を指定
spring_layout が収束しないエッジが少なすぎる、 連結成分が孤立iterations=500 に増やす、 もしくは kamada_kawai に切替
PageRank が NaN孤立ノードあり、 ダンピング 1.0 を指定alpha=0.85 を明示、 孤立ノードを除外
betweenness 計算が遅い$O(nm)$ が大規模で重いk=100 でサンプル近似: nx.betweenness_centrality(G, k=100)
ノードが重なって読めないspring_layout の k パラメータが小さいk=1.5 など大きく、 figsize も拡大
隣接行列が巨大すぎる密行列で持っている (n=10000 で 800MB)scipy.sparse.csr_matrix に変換 (エッジ密度 5% 未満なら必須)
エッジに方向が表示されないnx.Graph (無向) で構築nx.DiGraph に変更、 描画時 arrows=True
draw_networkx_labels で文字が小さいfont_size デフォルト 12 が PDF で小さいfont_size=14 以上、 図全体を拡大

🆚 ネットワーク図 vs 他の可視化との比較

手法向くケース向かないケース
ネットワーク図関係性、 経路、 中心性が主役数値の比較 (棒グラフが優位)
ヒートマップ隣接行列の全体パターン経路の発見 (ネットワーク図が優位)
ツリーマップ階層 + 数量サイクル、 多対多
サンキー図フロー、 多段階遷移 (有向、 DAG)サイクルがあるネットワーク
弦図 (chord diagram)少数ノード (20 以下) の相互関係100+ ノード (混み合う)
散布図 + 線2 連続変量 + ペア関係3 次以上の関係性

📑 参考: 公式ドキュメントへのリンク

🧠 自己テスト 10 問

  1. ネットワーク図において、 ノードとエッジが表す情報を 1 文で答えよ。 (答: ノードは個体・要素、 エッジは関係性・つながり)
  2. force-directed レイアウトの 2 つの物理的力を答えよ。 (答: 反発力 (電荷的)、 引力 (ばね的))
  3. SSDSE-B-2026 で構築した 47 都道府県ネットワークの直径 (最大最短経路) は? (答: 6)
  4. 媒介中心性 1 位の県と、 その値を答えよ。 (答: 静岡県、 0.498)
  5. PageRank のダンピング係数 d=0.85 が意味する確率を答えよ。 (答: 85% でリンクをたどり、 15% でランダムジャンプ)
  6. Louvain 法が最適化する指標を答えよ。 (答: modularity Q)
  7. 連結グラフでない場合に近接中心性が問題になる理由を答えよ。 (答: 到達不能ノードへの距離が無限大で総和が無限大になる)
  8. 10,000 ノードのグラフで spring_layout が遅い理由を答えよ。 (答: 全ペア反発力計算が O(n²) で 1 反復 1 億回演算)
  9. スケールフリーネットワークの次数分布が従う法則を答えよ。 (答: べき乗則 P(k) ∝ k^{-γ})
  10. 無向グラフに PageRank を適用するとほぼ同等になる中心性は? (答: 次数中心性)

🚀 拡張: GraphML / GEXF への保存と Gephi 連携

🎯 このコードでやること: ① で作った G を Gephi で開けるファイル形式 (GraphML, GEXF) で保存する。

📥 入力データ: ① の G とノード属性 (Prefecture, 人口)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import networkx as nx

# ノード属性を付与
for i in G.nodes():
    G.nodes[i]['name']       = df['Prefecture'][i]
    G.nodes[i]['population'] = int(df['A1101'][i])
    G.nodes[i]['birth_rate'] = float(df['A4103'][i])
    G.nodes[i]['community']  = int(partition[i])
    G.nodes[i]['pagerank']   = float(pr[i])

# GraphML (XML) で保存 — Gephi, yEd, Cytoscape 対応
nx.write_graphml(G, 'prefnet.graphml')
# GEXF (Gephi 公式) で保存
nx.write_gexf(G, 'prefnet.gexf')

import os
print(f'GraphML: {os.path.getsize("prefnet.graphml"):,} bytes')
print(f'GEXF:    {os.path.getsize("prefnet.gexf"):,} bytes')
print('Gephi で開く → File > Open > prefnet.gexf → ForceAtlas2 を実行')

📤 実行結果:

GraphML: 32,100 bytes GEXF: 36,952 bytes Gephi で開く → File > Open > prefnet.gexf → ForceAtlas2 を実行

💬 読み解き: networkx で前処理 → Gephi で美しいレイアウト → PNG/SVG/PDF エクスポートが論文用作図の王道パターン。 ノード属性を全部仕込めば、 Gephi 上で色・サイズ・ラベルを自由に変えられる。

🎯 まとめポスター: 1 ページ要約

ネットワーク可視化 ─ 7 つの心得

  1. 目的を 1 文で決める — 「ハブを見つけたい」「クラスタを示したい」「経路を追いたい」のどれか。
  2. ノード/エッジ定義を明示 — 「ノード=都道府県、 エッジ=5 指標距離トップ 5」のように 1 文で書く。
  3. レイアウトを 1 つ選び理由を書く — spring を選ぶなら「クラスタを自然に分離するため」。
  4. seed を固定 — 再現性を死守。 報告書では seed=42 等を必ず記載。
  5. 中心性は目的別に — ハブなら degree/PageRank、 橋なら betweenness、 全体近さなら closeness。
  6. 視覚 3 次元以内に収める — 色・サイズ・太さ。 4 次元以上は読めない。
  7. キャプションに発見を 1 文 — 「静岡県が大都市圏県群と中規模県群を繋ぐ橋として中央に位置する」など。

以上で ネットワーク可視化 の用語ページ拡充版は終了。 SSDSE-B-2026 の 47 都道府県を素材に、 基本概念から実装・コミュニティ検出・edge bundling まで一通り扱った。 次は 中心性 や コミュニティ検出 の個別ページへ進み、 さらに GNN でグラフ学習の世界へ。

📦 補遺: networkx の主要関数チートシート

関数用途
nx.Graph()無向グラフ作成
nx.DiGraph()有向グラフ作成
G.add_node(i, name='x')ノード追加 (属性付き)
G.add_edge(u, v, weight=0.5)エッジ追加
G.add_edges_from([(0,1),(1,2)])エッジ一括追加
nx.degree(G)全ノードの次数
nx.shortest_path(G, src, tgt)最短経路
nx.connected_components(G)連結成分
nx.diameter(G)直径 (要連結)
nx.clustering(G)クラスタ係数
nx.degree_centrality(G)次数中心性
nx.betweenness_centrality(G)媒介中心性
nx.closeness_centrality(G)近接中心性
nx.pagerank(G, alpha=0.85)PageRank
nx.spring_layout(G, seed=42)力学レイアウト
nx.draw(G, pos, with_labels=True)簡易描画
nx.write_gexf(G, 'g.gexf')Gephi 形式保存
nx.adjacency_matrix(G)隣接行列 (scipy sparse)

🧩 47 都道府県交通ネットワークの追加実装パターン

ノード属性で人口・面積を埋め込む

SSDSE-B-2026 の A1101(総人口)と G7101(延べ宿泊者数)をノード属性に乗せると、 「サイズ=総人口、 色=延べ宿泊者数」の二重符号化ができる。 単なる地図ではなく、 「観光・宿泊需要が集中するハブはどこか」を一目で示せるのがネットワーク可視化固有の価値。

🎯 このコードでやること:SSDSE-B-2026 の 47 都道府県を networkx.Graph のノードに登録し、 総人口と延べ宿泊者数を属性として持たせる。 隣接県を edge として追加し、 ノードサイズ=総人口、 色=延べ宿泊者数で matplotlib 描画する。

📥 入力データ(SSDSE-B-2026 抜粋)

Code Prefecture A1101(総人口) G7101(延べ宿泊者数) R01000 北海道 5092000 32,783,470 R13000 東京都 14086000 80,273,650 R14000 神奈川県 9229000 20,134,930 R27000 大阪府 8763000 44,005,760 R47000 沖縄県 1468000 20,038,190
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import networkx as nx
import matplotlib.pyplot as plt
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026']==2023][['Prefecture','A1101','G7101']].reset_index(drop=True)

G = nx.Graph()
for _, r in df.iterrows():
    G.add_node(r['Prefecture'], pop=r['A1101'], night=r['G7101'])

# 隣接県を追加(一部)
edges = [('北海道','青森県'),('東京都','神奈川県'),('東京都','埼玉県'),
         ('東京都','千葉県'),('大阪府','京都府'),('大阪府','兵庫県'),
         ('大阪府','奈良県'),('福岡県','佐賀県'),('沖縄県','鹿児島県')]
G.add_edges_from(edges)

pos = nx.spring_layout(G, seed=42, k=0.6)
sizes = [G.nodes[n]['pop']/5000 for n in G.nodes()]
colors = [G.nodes[n]['night']/1e6 for n in G.nodes()]
nx.draw(G, pos, node_size=sizes, node_color=colors,
        cmap='YlOrRd', with_labels=True, font_size=8)
plt.savefig('pref_network.png', dpi=150)

📤 実行例

Number of nodes: 47 Number of edges: 9 Node sizes range: 107 - 2817 (pop / 5000) Node color range: 1.70 - 80.27 (延べ宿泊者数, 百万人泊) Layout: spring (Fruchterman-Reingold) File saved: pref_network.png

💬 結果の読み方:東京都ノードが最大サイズ(人口)+最濃色(延べ宿泊者数)で「人口・宿泊需要ハブ」が一目瞭然。 spring layout は引力(edge)と斥力(全ノード対)の均衡で配置を決めるため、 「東京・神奈川・埼玉・千葉」が中央に集約され、 沖縄・北海道は外側に分離される。 これは地理的な位置とは異なる「関係性の地図」になっている点が重要。

エッジ重みで都市間移動量を表現

エッジに weight 属性を持たせると、 「線の太さ=移動量・取引量」のような流量ネットワークが描ける。 47 都道府県の通勤流入量・物流量・電力融通量など、 ペアごとの量的関係はすべてエッジ重みで表現可能。 nx.draw_networkx_edges(G, pos, width=[d['weight']*0.01 for _,_,d in G.edges(data=True)]) のようにリスト指定する。

レイアウト 使う場面 計算量
spring_layout中規模、 一般用O(n²) 反復
kamada_kawai_layout最短経路ベース、 美しいO(n³)
circular_layout対称性を見せたい時O(n)
spectral_layoutグラフラプラシアン固有値O(n³)
pyvis interactiveWeb 上で zoom/drag物理エンジン JS

「どのレイアウトを選ぶか」自体が分析の主張になる。 spring は「クラスタを見せたい」、 circular は「全ノード対等性」、 spectral は「コミュニティ構造の連続性」を強調する。 47 都道府県で電力融通網を描く場合は kamada_kawai が美しく、 SNS 友達網のように規模が大きい場合は spring + ForceAtlas2 が現実的。

💡 30秒で分かる結論

🍰 まずはやさしく

つながりを図にする方法です。

データの関係をパッと見るために使います。

SNSの友達関係のような図です。

この手法のポイントを短くまとめます。

ネットワーク・グラフ・階層構造の可視化

network visualization を 30 秒で把握する重要ポイント:

📍 文脈ボックス

🍰 まずはやさしく

データのつながりを扱う学習です。

分析のスキルを身につけるために使います。

都道府県のデータを使って練習します。

定義から注意点まで順番に学びます。

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

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

🎨 直感で掴む

🍰 まずはやさしく

点と線で結ぶ地図のようなものです。

誰が中心にいるかを見つけるために使います。

部活の人間関係を線で結ぶイメージです。

図を描くときの4つのルールを学びます。

ネットワーク可視化は「点 (ノード)線 (エッジ) で関係を描き、 一目でクラスタ・ハブ・ボトルネックを見せる」道具です。 棒グラフや散布図が「1 つの量や 2 変数の関係」を見せるのに対し、 ネットワーク図は「多数の主体がどう繋がっているか」 (友達関係、 企業の取引、 引用、 路線図、 タンパク質相互作用など) を扱います。

代表的なレイアウトは ① 力学モデル (force-directed): 引き合う・反発する力で自然な配置 (Fruchterman-Reingold、 ForceAtlas2)、 ② 階層レイアウト: 親子関係を上下に配置 (Sugiyama)、 ③ 円環レイアウト: ノードを円周に並べエッジを弦で結ぶ (chord diagram)、 ④ 地理レイアウト: ノードを実座標に置く (路線図・物流網) の 4 つ。

SSDSE-B-2026 を題材にするなら、 47 都道府県をノード、 実在列(総人口 A1101・高齢人口 A1303・出生数 A4101・死亡数 A4200・婚姻件数 A9101 など)の標準化ベクトル間の類似度をエッジとした「県類似度ネットワーク」が定番(本ページで実装)。 長野・宮城が次数中心性 (degree centrality) の高いハブ、 東京は「似た県がない」ため次数最小級、 といった構造が見える化されます。 数値の表だけ眺めても気づけない「クラスタ構造」「ハブとなる県」が直感的に把握できるのが本手法の威力です。 なお県間の通勤・物流のような流動量(起点×終点の OD 行列)は SSDSE-B-2026 には収録されておらず、 「人口移動ネットワーク」を描くには国勢調査の従業地・通学地集計などの外部データが必要です。

💡 ネットワーク可視化を最短で身につける順路: ① NetworkX で 10 ノード程度の小グラフを描き、 ② 同じグラフを spring_layout / kamada_kawai_layout / circular_layout の 3 通りで描き比べ、 ③ nx.degree_centralitynx.betweenness_centrality でノードサイズに連動させると、 「レイアウトの違いで見え方が変わる」 (= 唯一の正しい絵は存在しない) という感覚が腑に落ちます。

📐 定義

🍰 まずはやさしく

ネットワーク可視化という手法です。

レポートなどで正しく使うために学びます。

スマホのアプリ開発などでも使われます。

使うための条件やルールを説明します。

ネットワーク・グラフ・階層構造の可視化

英語名 Network Visualization。 同義・関連語:グラフ可視化。

🎯 いつ・どこで使うか

📋 前提条件・適用範囲

この用語を理解・使用するときは、 次のような前提を意識してください:

🔬 数式を言葉で読み解く

前節の数式に含まれる記号を、 日本語の意味に翻訳する。

network visualization の数式は、 これらの記号を組み合わせて「データから未知量を推定する」あるいは「データの構造を要約する」プロセスを記述している。

🧮 数式に値を入れて手で計算する: 描画可能な最大エッジ数

🧮 数式に値を入れて手で計算する: 描画可能な最大エッジ数

合成データで完全グラフのエッジ数と可視化複雑度を計算する。

Step 1: 完全グラフ エッジ

N ノード最大エッジ
1045
501,225
1004,950
1,000499,500

Step 2: 公式

最大エッジ = N(N-1)/2 N=100 で約 5,000 エッジ N=1000 で約 50 万 → 可視化困難 (集約必要)

🐍 Python で再現

1
2
3
4
import numpy as np
N = np.array([10, 50, 100, 1000])
max_edges = N*(N-1)//2
print(f"最大エッジ: {max_edges}")

📤 実行結果

最大エッジ: [ 45 1225 4950 499500]

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

🐍 Python での扱い

SSDSE-B-2026 のような公的統計データを Python で扱う際の基本パターン:

📥 入力例(SSDSE-B-2026 全体:564 行 × 112 列 = 47 都道府県 × 2012〜2023 年) 年度 地域コード 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) … 2023 R01000 北海道 5,092,000 1,681,000 24,430 … 2023 R13000 東京都 14,086,000 3,205,000 86,348 … 2023 R47000 沖縄県 1,468,000 350,000 12,549 … …(残り 112 列は住宅・家計・教育・医療など)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import pandas as pd
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]  # 2023 年断面 (47 都道府県)
print(df.shape)
print(df.dtypes)
print(df.describe())

# 「関係性の可視化」の文脈で扱う場合の例:
# 分野: 可視化
# 関連手法は同カテゴリの他用語を参照してください。
📤 実行例(実測) (47, 112) SSDSE-B-2026 int64 Code object Prefecture object A1101 int64 A110101 int64 ... L322106 int64 L322107 int64 L322108 int64 L322109 int64 L322110 int64 Length: 112, dtype: object SSDSE-B-2026 A1101 ... L322109 L322110 count 47.0 4.700000e+01 ... 47.000000 47.000000 mean 2023.0 2.645809e+06 ... 27409.148936 55356.936170 std 0.0 2.797551e+06 ... 4495.381291 6610.419905 min 2023.0 5.370000e+05 ... 18374.000000 35708.000000 25% 2023.0 1.034000e+06 ... 25662.500000 51114.500000 50% …(以下略)

具体的なコードは 1変量の可視化 を参照してください。

📝 レポートでの報告

分析結果を報告するときに含めるべき情報:

✅ チェックリスト

⚠️ よくある落とし穴

❌ 軸の切り取り
原点を切ると差が誇張されます。
❌ 色覚多様性
赤緑の組合せは識別困難な人がいます。 ColorBrewer のセットを推奨。
❌ 面積の誤解
半径を 2 倍にすると面積は 4 倍。 視覚的に強調されすぎる。

network visualization を実務で使う際に頻発する誤用・落とし穴を列挙する。 多くは「前提の確認不足」「結果の過信」「他手法との比較不足」が原因で、 事前にチェックリスト化することで回避できる。

🗺 概念マップ

「network visualization」を中心とした関連概念マップ。

network visualization グラフ理論 Force-Directed 中心性指標 可視化ツール 隣接行列 コミュニティ検出

概念マップは「グラフ理論 → レイアウト戦略 → 構造解析 (中心性・コミュニティ) → 応用 (SNS / 通勤 / 引用ネット)」の流れを示す。 SSDSE-B-2026 を題材にするなら、 47 ノード × 実在 5 指標の類似度エッジ (本ページでは 153 エッジ) で spring layout を組み、 PageRank と Louvain で都道府県の階層構造を可視化するのが入り口 (通勤・物流エッジは SSDSE 非収録のため外部 OD データが必要)。

🔗 隣接手法への橋渡し

関係性の可視化は、 グラフ構築から構造解析、 描画、 解釈の各段階で隣接領域と組み合わせる。

SSDSE-B-2026 規模では「networkx + spring layout + PageRank ノードサイズ + Louvain 色分け」が標準ワークフロー。 大規模 SNS では Gephi や cugraph (GPU) を併用。

🌳 手法選択フロー

ネットワーク可視化のレイアウト選択は「ノード数・密度・伝えたい構造」で決まる。

  1. ノード ≤ 100 力学モデル (ForceAtlas2、 Fruchterman-Reingold) が最も直感的
  2. ノード > 10000 サンプリングまたは UMAP / t-SNE をグラフ埋め込みに適用
  3. 階層構造を強調 Sugiyama レイアウト or 円形配置で「方向性」を明示

SSDSE-B-2026 の 47 都道府県類似度ネットワークを可視化するなら 47 ノードと小規模なので力学モデルで OK、 地理座標を制約に入れると現実感が増す。

🎮 触って理解する

21 ノード・3 コミュニティの小さなネットワークを、 力学モデル (force-directed layout) でブラウザ内でリアルタイム計算して描画する。 エッジは「バネ」 (自然長より伸びると引き合い、 縮むと押し返す: フックの法則 F = k(d − L))、 全ノードペアには「斥力」 (クーロン力型: F = C / d²) が働き、 力の釣り合いに向かって配置がひとりでに安定していく。 spring_layout (Fruchterman-Reingold) が毎回やっている計算そのものだ。

🎨 直感: 「繋がりをバネで配置する」とは

力学モデルは「エッジで繋がったノードは近くに、 繋がっていないノードは斥力で遠くに」という 2 つの力の妥協点を探す最適化だ。 同じコミュニティ内はバネが多いので固まり、 コミュニティ間はバネが 1 本 (橋) しかないので離れる — クラスタ構造が「何も指定しなくても」空間的に分離して見えるのはこのため。 ハブ (次数最大のノード) は多くのバネに四方から引かれるためコミュニティの中心に鎮座する。 スライダーで斥力を上げると全体が膨らみ、 バネ自然長を縮めるとクラスタが凝縮する — どちらも「同じグラフ」の別の見せ方にすぎない。

⚠️ よくある落とし穴 (レイアウト編)

🚀 発展: この玩具グラフの先へ

🧭 関係性可視化を 47 都道府県データで体得する(深掘り編)

ここまで「関係性の可視化=ノード+エッジで描くグラフ図」という基本を扱った。 本章では SSDSE-B-2026 の 47 都道府県データ(2023 年)(総人口・出生数・死亡数・一般診療所数・延べ宿泊者数など 13 の実在数値列)を素材に、 (1) 相関ネットワーク(2) ヒストグラム+ネットワーク(3) コミュニティ別の箱ひげ の 3 ステップで「描く理由・読む方向・誤読の回避」を完成させる。 図は 3 枚、 コードは 4 ブロック(4 要素パターン完備)、 表は 6 枚、 最後に理解度チェック・ミニ辞書・ケーススタディを置く。

🖼 3 枚の図で「関係性可視化の前段と後段」を捉える

ネットワーク図は「いきなり描いてはいけない」というのが実務の鉄則だ。 まず (A) 散布図でペア関係の強さを確かめ、 次に (B) ヒストグラムでノードの大きさ(人口など)の分布を把握し、 最後に (C) コミュニティ別の箱ひげで「クラスタが意味を持つか」を検証する。 この前段検証を省くと、 「色が分かれているからクラスタがある」と錯覚するだけの“見せかけネットワーク”が量産される。

図 A: 県の総人口と一般診療所数の散布図 — 「ノード間にエッジを引く根拠」を確認する

総人口と一般診療所数の散布図(SSDSE-B-2026 47 都道府県)
図 A. SSDSE-B-2026(2023 年)の 47 都道府県、 総人口(横軸)× 一般診療所数(縦軸)。 点が直線に近いほど 2 変数間の関係が強い(r = 0.972)。

この散布図は「ペアの関係を 1 本のエッジで表してよいか」の事前確認だ。 ほぼ直線(相関係数 0.97 以上)なら「人口が決まれば一般診療所数がほぼ決まる」= 2 変数を別ノードにする意味は薄い。 一方、 ばらつきが大きい変数ペアこそ、 ネットワーク図でクラスタ構造を探す価値がある。 相関ネットワーク を作る前に、 必ず散布図行列で「線形にきれいに乗りすぎていないか」を確認する習慣をつけよう。

図 B: 県人口のヒストグラム — ノードサイズを「対数で見るか線形で見るか」を決める

47 都道府県の人口ヒストグラム
図 B. 47 都道府県の人口分布。 右に大きく裾が伸びる(東京・大阪・神奈川・愛知)。

ヒストグラムは「ノードサイズを node_size = pop に比例させてよいか」の判断材料だ。 東京の人口は鳥取の約 22 倍。 線形比例だと東京の丸が画面の半分を占め、 他県が点になる。 分布が右に長く裾を引いているなら、 ノードサイズは log 変換するか、 sqrt 変換 を使うのが定石。 ヒストグラムで右裾を確認 → ネットワーク図で node_size = np.log1p(pop) * 200 のように変換、 がワンセットだ。

図 C: クラスタ別の一般診療所数(箱ひげ) — 「コミュニティが意味を持つか」を検証する

クラスタ別の一般診療所数(箱ひげ図)
図 C. KMeans で得た 3 クラスタ別に「一般診療所数」の分布を箱ひげで比較。

ネットワーク図で「色が違う 3 クラスタ」が出ても、 それが意味のあるグルーピングかは別問題だ。 図 C のように クラスタごとに連続変量(一般診療所数・総人口・死亡数)の箱ひげを並べ、 中央値が大きく違うか・四分位範囲が重なるか を確認する。 重なりが大きいなら「ネットワーク上で色が分かれていても、 数値的には差が無い」= 描画パラメータが見せた幻のクラスタかもしれない。 箱ひげ図クラスタ妥当性 が事後検証のキーワード。

📊 6 枚の表で「描き分け」を整理

表 1: ネットワーク図 vs その他の可視化(使い分け一覧)

手法 主目的 ネットワーク図と比べた強み 使う場面 (47 都道府県例)
散布図2 変数の相関を見る外れ値の位置が一目で分かる総人口 × 一般診療所数
ヒートマップ多変数相関の全体俯瞰全ペアを一度に比較13 列の相関行列
階層クラスタ樹形図グルーピングの階層構造分割数を後から決められる47 県の経済構造類似度
コーラスマップ地理的分布場所と数値を同時表示人口分布
ネットワーク図多対多の関係構造クラスタ・ハブ・中継点が見える変数相関ネット・県類似度ネット(通勤流動・電力融通は外部 OD データ)

表 2: ノード属性とエッジ属性の対応関係(描画チャネル設計)

属性 視覚チャネル SSDSE-B-2026 で使える例 注意点
ノードサイズ面積(=値² ではなく値)総人口、 一般診療所数右裾分布なら log 化
ノード色カテゴリ or 連続スケール地方ブロック、 高齢化率7 色超えると区別不可
エッジ太さ線幅|相関係数|、 類似度(実在列から計算)線幅の上限を 10px に
エッジ色符号や種別正の相関=青、 負=赤色覚多様性に配慮
エッジ方向矢印因果仮説の向き(総人口 → 一般診療所数 等)※県間フロー OD は SSDSE 非収録無向と有向を混在させない
ラベルテキスト県名5 つ以上は重ねず間引く

表 3: 中心性指標の比較(誰が「中心ノード」か?)

中心性 定義 高い県の例 (本ページの 5 指標類似度ネット・実測) 解釈
次数中心性エッジ本数長野、 宮城、 栃木直接つながる相手が多い
媒介中心性最短経路に乗る回数静岡、 兵庫経由地点・ハブ役
近接中心性他全ノードへの平均距離の逆数静岡、 新潟全体に近い
PageRank隣接の重要度を再帰的に長野、 埼玉、 千葉「重要ノードに繋がる」も評価
固有ベクトル中心性PageRank の親戚長野、 新潟、 宮城「強力なハブと繋がる」

表 4: コミュニティ検出アルゴリズムの比較

アルゴリズム 最大化指標 計算量 47 県データでの挙動
LouvainモジュラリティO(n log n)3-5 クラスタに分割
Leidenモジュラリティ改良O(n log n)Louvain より安定
GirvanNewman媒介中心性を削るO(n³)小規模に向く
Label propagation隣接多数決O(n)高速だが結果不安定
Spectral clusteringグラフラプラシアン固有値O(n³)k を事前指定

表 5: ネットワーク図の「やってはいけない」

NG 何が起こるか 代替策
全ノードにラベル文字が重なって読めない上位 10 ノードのみラベル
エッジを全部描く毛糸玉 (hairball) になる閾値で間引く・重みでフィルタ
レイアウトを変えて比較「位置」が誤解を生む同じ seed で固定
色を 10 色以上色の区別がつかない7 色以内 + ラベル併記
サイズに値² を割当面積が極端に違う面積 = 値 で代用
「美しさ」だけ追求主張が伝わらない主張=中心性/クラスタを強調

表 6: 47 都道府県データで作れるネットワーク 5 種

ネットワーク種 ノード エッジ条件 読み取れること
変数相関ネットSSDSE-B 各列|r| ≥ 0.7変数のクラスタ
県類似度ネット47 都道府県距離 ≤ τ県のグルーピング
通勤フロー ※47 都道府県流入量 > 1 万人経済圏
産業共起 ※産業中分類同一県で上位産業集積
電力融通 ※10 電力エリア融通 > 0基幹インフラの依存

※ 印の 3 種は SSDSE-B-2026 非収録のデータが必要(通勤 OD は国勢調査 従業地・通学地集計、 産業データは経済センサス・産業連関表、 電力融通は電力広域的運営推進機関の公表値など)。 SSDSE-B-2026 の実在列だけで構築できるのは上 2 種(変数相関ネット・県類似度ネット)。

🐍 Python 4 ブロック — 4 要素パターン完備

ここから 4 つのコードブロックを 4 要素パターン(やること → 入力 → コード → 実行結果 → 読み方)で示す。 すべて data/raw/SSDSE-B-2026.csv の実データを使い、 乱数生成は一切しない。

コード 1: SSDSE-B-2026 を読み込んで「変数間の相関ネットワーク」を組み立てる

このコードでやること: SSDSE-B-2026 を読み込み、 数値列の ピアソン相関行列 を計算してから、 絶対値 0.7 以上のペアだけをエッジに変換して NetworkX のグラフを構築する。 「変数同士の関係性」を構造化する第一歩。

📥 入力データ (SSDSE-B-2026.csv 先頭 3 行、 列の一部):

Code Prefecture 総人口 一般診療所数 死亡数 婚姻件数 R01000 北海道 5,092,000 3,403 75,120 17,281 R02000 青森県 1,184,000 850 20,835 3,326 R03000 岩手県 1,163,000 879 19,612 3,376 …(全 47 都道府県、 13 数値列)
 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
import pandas as pd
import numpy as np
import networkx as nx

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]          # 2023 年・47 都道府県

cols = {'A1101':'総人口', 'A1301':'年少人口', 'A1303':'高齢人口', 'A4101':'出生数',
        'A4103':'合計特殊出生率', 'A4200':'死亡数', 'A5101':'転入者数', 'A9101':'婚姻件数',
        'B4101':'年平均気温', 'L3221':'消費支出', 'C3301':'着工建築物数',
        'I5102':'一般診療所数', 'G7101':'延べ宿泊者数'}
num = df[list(cols)].rename(columns=cols)     # 13 の実在数値列
corr = num.corr()

G = nx.Graph()
for c in num.columns:
    G.add_node(c)
for i, a in enumerate(num.columns):
    for b in num.columns[i+1:]:
        r = corr.loc[a, b]
        if abs(r) >= 0.7:
            G.add_edge(a, b, weight=round(float(abs(r)), 3), sign=float(np.sign(r)))

print('ノード数:', G.number_of_nodes())
print('エッジ数:', G.number_of_edges())
print('上位 3 エッジ:', sorted(G.edges(data=True), key=lambda e: -e[2]['weight'])[:3])

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

ノード数: 13 エッジ数: 45 上位 3 エッジ: [('高齢人口', '死亡数', {'weight': 0.999, 'sign': 1.0}), ('年少人口', '出生数', {'weight': 0.998, 'sign': 1.0}), ('総人口', '年少人口', {'weight': 0.997, 'sign': 1.0})]

💬 ノード 13・エッジ 45 のグラフが完成。 「高齢人口 - 死亡数」「年少人口 - 出生数」「総人口 - 年少人口」が相関 0.99 台で繋がる → これらは年齢構成が生む実質同じ情報のクラスタを形成している、 と読める。

コード 2: ノードサイズを「平均値の log」、 色を「変数カテゴリ」で塗ってネットワーク描画

このコードでやること: コード 1 で作った Gspring_layout で描く。 ノードサイズ=列平均の log色=カテゴリ(人口系/動態系/生活系) を割り当てて、 「変数の集まり方」が一目で読めるレイアウトに整える。

📥 入力データ: コード 1 で構築した G(13 ノード・45 エッジ)と、 各列の平均値 num.mean()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import matplotlib.pyplot as plt

categories = {
    '総人口':'pop', '年少人口':'pop', '高齢人口':'pop',
    '出生数':'flow', '死亡数':'flow', '転入者数':'flow', '婚姻件数':'flow',
    '一般診療所数':'life', '着工建築物数':'life', '延べ宿泊者数':'life',
    '消費支出':'life', '合計特殊出生率':'life', '年平均気温':'life'
}
color_map = {'pop':'#26A69A', 'flow':'#FFA726', 'life':'#7E57C2'}

means = num.mean()
sizes  = [np.log1p(means[n]) * 80 if n in means else 200 for n in G.nodes()]
colors = [color_map.get(categories.get(n, 'other'), '#90A4AE') for n in G.nodes()]

pos = nx.spring_layout(G, seed=42)
plt.figure(figsize=(7,7))
nx.draw_networkx_nodes(G, pos, node_size=sizes, node_color=colors, alpha=0.85)
nx.draw_networkx_edges(G, pos, width=[d['weight']*3 for _,_,d in G.edges(data=True)], alpha=0.6)
nx.draw_networkx_labels(G, pos, font_size=10, font_family='IPAexGothic')
plt.axis('off')
plt.title('SSDSE-B-2026 変数相関ネットワーク')
plt.savefig('var_corr_net.png', dpi=150, bbox_inches='tight')
print('Saved: var_corr_net.png')

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

Saved: var_corr_net.png (PNG 出力: 緑のノード [人口系] を中心に、 オレンジ [動態系: 出生数・死亡数・転入者数・婚姻件数] が密に連結し、 紫 [生活系: 一般診療所数・着工建築物数・延べ宿泊者数] もハブに繋がる。 太いエッジは「高齢人口 - 死亡数」「年少人口 - 出生数」。)

💬 「人口系 - 動態系」のリンクが太く、 「生活系」は人口系の中心ノードを介して繋がる。 一方で 合計特殊出生率・年平均気温・消費支出 は相関 0.7 未満でどのノードとも繋がらず独立して浮く。 「人口規模で決まる実数系指標と、 人口と独立な比率系指標」という構造仮説を立てる材料になる。

コード 3: 中心性 4 種を一気に計算して「ハブ列」を特定

このコードでやること: 同じ G に対して 次数中心性・媒介中心性・近接中心性・PageRank を計算し、 上位 5 件を表にする。 「どの変数が全体構造のハブか」を客観的に決める。

📥 入力データ: 同じ G (13 ノード・45 エッジ)。

1
2
3
4
5
6
7
8
deg  = nx.degree_centrality(G)
btw  = nx.betweenness_centrality(G, weight='weight')
clo  = nx.closeness_centrality(G)
pr   = nx.pagerank(G, weight='weight')

import pandas as pd
cen = pd.DataFrame({'degree':deg, 'between':btw, 'close':clo, 'pagerank':pr})
print(cen.sort_values('pagerank', ascending=False).head().round(3))

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

degree between close pagerank 総人口 0.75 0.0 0.75 0.098 出生数 0.75 0.0 0.75 0.098 婚姻件数 0.75 0.0 0.75 0.098 年少人口 0.75 0.0 0.75 0.098 高齢人口 0.75 0.0 0.75 0.097

💬 総人口・出生数・婚姻件数・年少人口 が次数 0.75・PageRank 0.098 で並ぶ最上位ハブ群 → 人口規模を表す実数系変数群が互いに密結合し、 媒介中心性はどれも 0(クラスタ内が近似完全グラフで橋渡し役がいない)。 「人口を抑える=多くの実数系指標を抑える」ことが定量的に裏付けられる。

コード 4: コミュニティ検出(Louvain)→ クラスタ別に箱ひげで検証

このコードでやること: Louvain で G をコミュニティ分割し、 さらに 47 都道府県を 13 変数の標準化値で KMeans クラスタリング → クラスタ別に「一般診療所数」の箱ひげを並べて、 ネットワーク上のクラスタが「数値的にも区別できるか」を検証する。

📥 入力データ: 同じ G と、 元の 47 行 × 13 列の num データ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from networkx.algorithms.community import louvain_communities
comm = louvain_communities(G, weight='weight', seed=42)
print('変数コミュニティ数:', len(comm))
for k, c in enumerate(comm):
    print(f'  community {k}:', sorted(c))

from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler

Xz = StandardScaler().fit_transform(num.fillna(num.mean()))
labels = KMeans(n_clusters=3, n_init=10, random_state=42).fit_predict(Xz)

import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(7,4))
groups = [num.loc[labels==k, '一般診療所数'] for k in range(3)]
ax.boxplot(groups, tick_labels=['cluster0','cluster1','cluster2'])
ax.set_title('クラスタ別 一般診療所数(47 都道府県)')
plt.savefig('cluster_box.png', dpi=150, bbox_inches='tight')
print('Saved: cluster_box.png')
print('各クラスタ中央値:', [int(g.median()) for g in groups])

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

変数コミュニティ数: 4 community 0: ['合計特殊出生率'] community 1: ['一般診療所数', '出生数', '婚姻件数', '年少人口', '延べ宿泊者数', '死亡数', '着工建築物数', '総人口', '転入者数', '高齢人口'] community 2: ['年平均気温'] community 3: ['消費支出'] Saved: cluster_box.png 各クラスタ中央値: [1159, 14894, 5001] (施設数、 重なり小)

💬 3 クラスタの一般診療所数の中央値が cluster0 = 1,159 / cluster2 = 5,001 / cluster1 = 14,894 施設 と明確に階段状 → ネットワーク図のクラスタ分割は数値的にも裏付けられている。 cluster1 は東京都 (1 県)、 cluster2 は北海道・埼玉・千葉・神奈川・愛知・大阪・兵庫・福岡 (大都市圏 8 県)、 cluster0 は残り 38 県、 という解釈が成立。

✅ 理解度チェック(5 問)

  1. Q1. 47 都道府県の人口分布が右に大きく裾を引いているとき、 ネットワーク図のノードサイズはどう決めるべきか?
    A. node_size = np.log1p(pop) * ksqrt(pop) * k など対数/平方根変換を使う。 線形比例だと東京のノードが他県を圧倒し読めない。
  2. Q2. ネットワーク図で「クラスタが見える」とき、 その妥当性を確認する追加プロットは?
    A. クラスタ別に主要変数の箱ひげ・分布を描いて、 中央値・四分位範囲が重ならないかをチェックする。
  3. Q3. 次数中心性が高いのに媒介中心性が低い県は、 どんな役割か?
    A. 「周りと多く繋がるが、 別クラスタとの中継地点ではない」= クラスタ内ハブ。 例: 大阪は近畿クラスタの内部ハブだが、 関東〜九州の最短経路には乗らない。
  4. Q4. エッジを全部描いて毛糸玉になったとき、 どう対処するか?
    A. 重みで閾値フィルタ(例: 上位 20%)、 もしくは backbone extraction(Disparity Filter)でエッジを減らす。
  5. Q5. 同じグラフを spring と circular で描いて「結果が違う」と報告された。 どう答えるか?
    A. レイアウトは座標を決めるだけで、 グラフ構造そのもの(エッジ・中心性・コミュニティ)は変わらない。 「位置」を構造の根拠に使ってはいけない。

📖 ミニ用語辞書

用語 説明(80-120 字)
ノード (vertex)ネットワークの「点」。 47 都道府県データなら県や変数を 1 ノードに対応させる。 属性 (人口・カテゴリ) を持てる。
エッジ (edge)ノード間をつなぐ「線」。 重み・方向・符号を持てる。 県間の類似度は重み付き無向エッジ、 通勤流入量のような流動(外部 OD データ)は重み付き有向エッジで表現するのが自然。
次数あるノードに繋がるエッジ本数。 「総人口」の次数は 9 で、 9 個の変数と相関 0.7 以上で繋がっている(残る 3 変数=合計特殊出生率・年平均気温・消費支出とは繋がらない)、 という意味。
モジュラリティ「同じコミュニティ内のエッジが多いほど大きい」分割指標。 Louvain はこれを最大化する。 0.3 以上で分割の意味があると言われる。
毛糸玉 (hairball)エッジが多すぎて構造が見えない状態。 重みフィルタや backbone extraction で減らす必要がある。
backbone extraction重要なエッジだけを残してネットワークの骨格を抽出する手法。 Disparity Filter が代表的。

📚 ケーススタディ: SSDSE-B-2026 で「一般診療所数 = 人口の関数」を可視化する

問い: 47 都道府県の医療・生活インフラの規模は、 人口・気候・世帯消費のうち、 どれで一番よく説明できるか?

手順 1 (前段): SSDSE-B-2026(2023 年)を読み、 13 個の数値列で散布図行列を作る。 総人口と一般診療所数がほぼ直線(r = 0.972)。 ヒストグラムで人口が右裾分布と確認。

手順 2 (構造): |相関| ≥ 0.7 のエッジで変数ネットワークを構築(13 ノード・45 エッジ)。 ノードサイズ = log(平均)。 「総人口・年少人口・出生数・死亡数・一般診療所数」など実数系 10 変数が密なハブを形成。

手順 3 (中心性): PageRank で「総人口」が出生数・婚姻件数・年少人口と並ぶ最上位(0.098)→ インフラ規模を 1 変数で説明するなら人口がベスト。

手順 4 (検証): Louvain でコミュニティ分割 → 実数系 10 変数の大クラスタと、 独立した 3 変数(合計特殊出生率・年平均気温・消費支出)が見える。 47 県を KMeans でクラスタリングし、 クラスタ別に一般診療所数を箱ひげで比較 → 中央値が階段状 (1,159 / 5,001 / 14,894 施設) で意味のある分割と判定。

結論: 一般診療所数は「人口」で約 9 割説明可能(r = 0.972、 R² ≒ 0.95)。 ただし「合計特殊出生率」「年平均気温」「消費支出」が独立クラスタ(人口と相関 0.7 未満)を作るため、 医療・生活の議論では人口規模だけでなく出生率・気候・世帯消費も同時に見るべき、 という多面的な含意が得られる。 ネットワーク図はその含意を 1 枚にまとめる強力なツールになる。

🕰 関係性可視化の歴史と理論的背景

ネットワーク図の原点は、 18 世紀のオイラーによる「ケーニヒスベルクの 7 橋問題」だ。 オイラーは都市と橋をノードとエッジに抽象化し、 「すべての橋をちょうど一度ずつ渡って戻れるか」という問いをグラフ理論として定式化した。 これが現代の関係性可視化のすべての出発点であり、 「現実の関係を抽象化して点と線で描く」という発想そのものを誕生させた革命だった。 SSDSE-B-2026 で 47 都道府県を扱うときも、 私たちは同じ抽象化を行っている: 県は点に、 指標の類似性や相関は線に置き換えられ、 数百万人規模の人口構造が一枚のグラフに凝縮される。

20 世紀に入ると、 心理学者のヤコブ・モレノが ソシオグラム を発明する。 これは人間関係を点と線で描いた最初の社会ネットワーク図であり、 「誰と誰が友達か」を可視化することで集団内のリーダー・孤立者・派閥を発見できることを示した。 現代の SNS 分析・組織分析・疫学接触追跡は、 すべてこのソシオグラムの末裔だ。 関係性の可視化は単なる「綺麗な絵」ではなく、 「目で見える形にすることで、 人間の認知が一気にその構造を把握できる」という認知科学的な意義 を持つ。 これが数値テーブルやリストでは得られない可視化の本質的な力である。

21 世紀になり、 グーグルの PageRank、 フェイスブックの友達推薦、 アマゾンの商品推薦、 ネットフリックスのレコメンド、 ツイッターのフォロー網解析、 新型コロナの接触追跡など、 すべてが 大規模グラフ+ネットワーク可視化 の応用だ。 47 都道府県の通勤フローやサプライチェーンも同じ枠組みで分析できる。 統計学の教科書では相関係数や回帰係数として「2 変数の関係」を学ぶが、 現実は多対多の関係であり、 それを 1 枚の図に圧縮するのがネットワーク可視化の核心的な役割なのだ。 さらに 2010 年代以降は、 グラフニューラルネット (GNN) や node2vec などの埋め込み手法と組み合わせて「グラフ × 機械学習」が爆発的に発展し、 関係性の可視化は AI の解釈装置としても重要視されている。

🗂 関係性データの典型的な 4 タイプ(47 都道府県データへの当てはめ)

関係性データを 1 枚の図に落とすときは、 まず「データのタイプ」を分類するのが先決だ。 タイプを誤ると描き方も誤る。 以下 4 タイプを SSDSE-B-2026 を題材に整理する。

(1) 共起ネットワーク: 「同じ県で上位に来る産業」「同じ年に発生した災害種別」のように、 同じ文脈で一緒に登場するものを線で結ぶ。 SSDSE-B-2026 で「出生数と婚姻件数の上位 10 県」を共起させると、 東京・神奈川・大阪・愛知などの大都市圏が密なクラスタを作る。 共起ネットワークは「グループ分け」の発見に向く。 一方で「因果」とは無関係なので、 「同じ県で多いから関連がある」と早合点しないこと。

(2) フローネットワーク: 「東京から神奈川へ通勤する人が 80 万人」のように、 量を持つ有向の流れを線で表す。 県間の通勤・物流・電力融通のペア間流動データはすべてこのタイプだが、 SSDSE-B-2026 には県間 OD(起点×終点)行列は収録されていないため、 描くには国勢調査の従業地・通学地集計などの外部データが必要。 SSDSE-B-2026 の転入者数 (A5101)・転出者数 (A5102) は県ごとの合計値であり、 どこから来てどこへ行くかの行列ではない点に注意。 ノードはほぼ常に「場所」、 エッジは「量+方向」を持つ。 線の太さで量、 矢印で方向を示すのが定石。 フローは「需要と供給の構造」を直感的に伝えるのに最適で、 政策提言・経済分析・物流最適化で頻出する。 一方、 47 都道府県 × 47 都道府県 = 2,209 エッジを全部描くと毛糸玉になるため、 上位 5% のみ描画するなどの間引きが必須となる。

(3) 類似度ネットワーク: 「県の経済構造ベクトル間のコサイン類似度が 0.9 以上なら線を引く」のように、 距離・類似度を閾値で 2 値化してエッジにする。 SSDSE-B-2026 で 13 の実在数値列の z スコアベクトルを使い、 県間のユークリッド距離をエッジに変換すると、 「人口・生活構造が似た県のクラスタ」が浮かぶ。 これは クラスタリング をネットワーク図で表現する方法とも言える。 注意点として、 閾値の選び方で図が劇的に変わるため、 複数閾値で描き比べる感度分析が必要。

(4) 因果ネットワーク: 「人口 → 一般診療所数」のように因果関係を有向エッジで描く。 SSDSE-B-2026 のような観測データから因果を推定するには、 操作変数法・差分の差分法・回帰不連続デザインなどの計量経済学手法が必要。 ネットワーク図は「仮説の整理ツール」として有効で、 因果推論の DAG (有向非巡回グラフ) はネットワーク可視化の代表的な応用だ。 因果ネットワークを描く際は、 必ず「これは推定であって観察ではない」と明示しないと、 視覚的な強さに引きずられて読者が因果と相関を混同する。

🧠 認知負荷の観点から見る「読みやすい関係性図」設計指針

関係性可視化は、 認知科学の観点では 「ワーキングメモリ + 視覚探索」 の総合課題だ。 人間が一度に保持できる視覚要素は概ね 5-9 個と言われている (ミラーの法則)。 47 都道府県を全部一度に描くと、 読者は「どこを見ればよいか」を見失う。 以下、 認知負荷を下げる設計指針 6 ヶ条を示す。

指針 1: 一画面 1 主張 — 「東京がハブだ」「地方都市が独立している」など、 主張を 1 つに絞る。 主張ごとに別図を作り、 並べて見せる。 1 枚の図で 3 つも主張すると、 読者の認知資源が分散し、 結局どれも伝わらない。

指針 2: 強調と背景の二段構え — 主張に関わるノード/エッジは鮮やかな色 + 太い線で、 背景情報は薄い灰色で描く。 全部同じ色で描くと「特に注目すべき点」が見えない。 大都市圏のハブを強調したいなら、 東京・大阪・名古屋だけ赤、 残り 44 県は灰色のような配色が効く。

指針 3: ラベルは選択的に — 全 47 ノードにラベルを付けると文字が重なる。 中心性上位 10 のみラベル、 残りはホバー時にツールチップで表示するのが定石 (pyvis を使えば標準機能)。 印刷物の場合は、 ラベルを付けるノードを事前に「主張に関係するノード」に絞る。

指針 4: 色は 7 色まで — 人間が瞬時に区別できる色は概ね 7 色 (色相環の主要色 + 黒白)。 8 色以上を使うと、 凡例を何度も確認しないと読めない。 47 都道府県を 8 ブロックに分けたいなら、 7 色 + 灰色 (その他) などの工夫が必要。 また色覚多様性に配慮して ColorBrewer などのカラーパレットを使うこと。

指針 5: 動きと静止を分ける — 時系列ネットワーク (例: 47 都道府県の通勤フローを 2010-2026) は、 年ごとの静止画を並べるか、 1 枚に差分を集約するか、 アニメーションにするか、 どれかに統一する。 アニメと差分を混在させると認知負荷が爆発する。 報告書なら静止画 4 枚、 Web ならアニメーション 1 本、 のように媒体特性に合わせて選ぶ。

指針 6: 凡例・出典・尺度を必ず付ける — ネットワーク図は「絵」として独立して扱われがちで、 表よりも凡例が省略されやすい。 ノードサイズが「人口に比例」なのか「log 比例」なのか、 エッジ太さが「実数」なのか「z スコア」なのか、 必ず凡例で明示する。 SSDSE-B-2026 のデータ出典 (年度・取得日) も図のキャプションに書く。 これを怠ると、 数年後に同じ図を再現できなくなり、 教育・研究の再現性が崩れる。

🧰 主要ツール比較(NetworkX / pyvis / Gephi / Cytoscape / D3.js)

関係性可視化を実務で行うときは、 ツール選定が成否を分ける。 47 都道府県データのような中規模 (~50 ノード) なら NetworkX + matplotlib で十分だが、 数千ノードを超えると Gephi や Cytoscape のような専用 GUI、 Web 公開なら pyvis や D3.js が必要になる。 以下、 5 つの代表ツールを比較する。

NetworkX (Python): 47 都道府県のような中規模グラフを Python パイプライン内で扱う標準ツール。 中心性・コミュニティ検出・最短経路など豊富な API を持ち、 matplotlib と組み合わせて静止画を作る。 利点は「pandas → networkx → matplotlib」のシームレスな連携。 欠点は描画速度が遅く、 5,000 ノードを超えると実用的でない点。 教育・研究・初期分析の主力。

pyvis (Python → HTML): NetworkX のグラフを nt = Network(); nt.from_nx(G); nt.show('out.html') で 1 行で対話的 HTML 化できる。 Web 上で zoom・drag・ホバーが動くため、 教育コンテンツや報告書 Web 版に最適。 47 都道府県の通勤フローを学生に触らせるなら pyvis 一択。 欠点はカスタマイズ性で、 細かい配色を変えにくい。

Gephi (GUI): ヨーロッパ発の老舗オープンソース GUI。 数万ノードまで実用、 ForceAtlas2 など独自レイアウトと豊富なフィルタが強力。 大学・研究機関の標準ツール。 SSDSE-B-2026 のサイズなら過剰スペックだが、 「動的フィルタで閾値を動かしながら探索する」体験は Gephi が圧倒的に優れる。 欠点は学習コストと、 描画結果を再現するスクリプトを書きにくい点。

Cytoscape (GUI): バイオインフォマティクス発で、 遺伝子・タンパク質相互作用網の業界標準。 47 都道府県データのような社会データに使う例は少ないが、 プラグインが豊富で、 統計解析パイプラインと連携できる。 学術論文用の高品質ベクター出力 (SVG, PDF) に強い。

D3.js (JavaScript): Web ネイティブの可視化ライブラリ。 自由度は最高だが、 自分でレイアウト・インタラクション・凡例をすべてコードで書く必要があり、 学習コストが極めて高い。 New York Times の対話的記事や、 OECD のダッシュボードなど、 プロのデータビジュアライザーが選ぶ最終形態。 47 都道府県データを政府ポータルで公開するなら検討の価値あり。

ツール選定の指針: 「規模 (ノード数) × 用途 (静止画 / 対話的 / 論文 / Web 公開) × チームのスキル」の 3 軸で決める。 学生・初学者は NetworkX + pyvis の組み合わせから始めると、 ほぼすべての教育用途を 1 つの Python ノートブックで完結できる。 47 都道府県 SSDSE-B-2026 の練習ならこれ以外を選ぶ理由はほぼ無い。

📰 読者別の関係性可視化の読み方(同じ図を 3 つの立場で読む)

同じネットワーク図でも、 読者の立場で読み方が変わる。 47 都道府県の通勤フロー図を、 (A) 政策担当者、 (B) ビジネス担当者、 (C) 研究者・学生 の 3 つの視点で読むと、 ネットワーク可視化が「誰のためのコミュニケーション装置か」が立体的に見えてくる。

(A) 政策担当者 は「東京一極集中の度合い」「地方拠点都市の役割」を読む。 中心性指標で東京の値が他を圧倒すれば、 「人口流出と地方衰退」の議論材料になる。 媒介中心性で福岡や仙台が高ければ、 「広域連携の中心都市」として政策的支援の根拠になる。 関係性図は、 政策担当者にとって「数百万人の動きを 1 枚で説明する説明資料」の役割を果たす。

(B) ビジネス担当者 は「物流拠点をどこに置くか」「採用エリアをどこに広げるか」を読む。 通勤フローの太いエッジは「通勤可能圏内=採用エリア候補」を示す。 同じ図でも、 政策担当者は「分散化」を、 ビジネス担当者は「集中=効率化」を読む。 関係性図は中立だが、 読者の関心軸で解釈が変わる典型例だ。

(C) 研究者・学生 は「クラスタの妥当性」「使用したデータの限界」を読む。 「東京を中心とした 1 つの大クラスタしかない」のは「データが日本全国のメッシュ移動データから生成されたバイアス」かもしれない。 関係性図は、 主張を生むだけでなく「主張の脆弱性」も同時に伝える義務がある。 学生はこの段階で「批判的読み方」を身につけることが、 データリテラシー教育の本質だ。

🚀 さらに学ぶための次のステップ

関係性可視化に習熟したあと、 次に学ぶべき領域は (1) グラフ理論の基礎 (オイラーパス・ハミルトン路・スモールワールド・スケールフリー)(2) グラフ機械学習 (node2vec・GNN・GraphSAGE)(3) 時空間ネットワーク分析 (時間変化するネットワーク) の 3 方向だ。 (1) は理論的基盤を与え、 (2) は予測・分類への応用を開き、 (3) は「動く関係性」を扱う武器になる。 47 都道府県データで (1)(2)(3) を順に練習することで、 統計学・機械学習・社会科学を横断するデータサイエンティストとしての視野が一気に広がる。

また、 「可視化=コミュニケーション設計」という観点では、 タフテの『定量情報の視覚表示』ベルタンの『図の意味論』カイザー・フンクの『データを使うストーリーテリング』 など、 視覚表現の古典を読むことを強く推奨する。 ネットワーク可視化は技術ではなく言語であり、 言語は読書によって深まる。 47 都道府県データで実践→古典で原理を学ぶ、 のサイクルを回せば、 関係性可視化は単なる「networkx の使い方」を超えた、 強力な思考道具になる。

⚠️ 追加の落とし穴 5 件

  • seed を変えると全く違う絵に見える: spring_layout は確率的初期配置のため、 seed を固定しないと毎回ノードが移動する。 必ず spring_layout(G, seed=42) のように固定する。
  • 無向グラフに矢印を描いてしまう: nx.draw_networkx_edges は有向グラフでないと矢印を描かない。 流動を表現するなら必ず nx.DiGraph() でグラフを作る。
  • 負の相関エッジを「絶対値だけ」で描く: 符号情報が消える。 エッジ色を青(正)/赤(負) に分け、 凡例を添える。 47 県で「合計特殊出生率と総人口」は負相関(約 -0.56)だが、 絶対値だけ見ると正相関と区別がつかない。
  • 動的ネットワーク (時系列) を 1 枚で描く: 通勤流動が年ごとに変わるのに、 全期間平均で 1 枚にまとめると変化が消える。 年別に並べる or 差分グラフを描く。
  • ノード位置を「地図上の位置」と勘違いする: spring_layout の (x,y) は意味を持たない。 都道府県の位置を地理座標に置きたいなら、 緯度経度を pos に直接指定する。

📝 まとめ — 47 都道府県データから関係性可視化を学ぶ意義

関係性の可視化は、 散布図・ヒストグラム・箱ひげのような「1 変数 / 2 変数」の可視化を超え、 「多対多の関係を一枚に圧縮する」ための言語だ。 SSDSE-B-2026 の 47 都道府県データは、 ノード数が手頃で意味解釈もしやすく、 練習素材として理想的だ。 本章で扱った相関ネットワーク・コミュニティ検出・中心性指標・箱ひげ検証の流れは、 そのまま実務の分析パイプライン(外部 OD データを使う通勤フロー分析など)に転用できる。 「描く前に確認する・描いたあとに検証する」 — この前後ろセットでネットワーク可視化は信頼できる分析装置になる。

最後に強調したいのは、 ネットワーク図は「説得装置」ではなく「思考装置」だという点だ。 美しい絵で読者を圧倒するのではなく、 構造を自分が理解するために描き、 その理解を最小限の装飾で他者と共有する。 これがデータサイエンスにおける関係性可視化の本質的な役割であり、 47 都道府県データを題材に練習する最大の意義だ。 描き手の責任は、 図そのものではなく「図を通じて読み手の理解が深まったかどうか」にある。

🧭 深掘り: 相関を「閾値で切って」ネットワークにする

このページの本文では「都道府県どうしの関係」をネットワークにした。ここでは角度を変えて、変数どうしの相関行列を閾値で二値化してネットワークにするという、実務で最も多い作り方を深掘りする。ノードは指標、エッジは「相関が強いペア」。連続量である相関係数を、どこかで線を引いてエッジの有無に変換するところに、この可視化の本質的な危うさが潜んでいる。

🎨 直感で掴む

相関ネットワークは「相関ヒートマップを地図にしたもの」だと思うとよい。6 つの指標なら相関行列は 6×6、上三角に 6×5÷2 = 15 個の数字が並ぶ。この 15 個それぞれについて「|相関| がしきい値以上ならエッジを引く」だけで、行列がグラフに化ける。つまり閾値 1 個の選び方が、そのまま「どのペアを関係アリと呼ぶか」という主張になる。閾値は装飾ではなく、分析の結論そのものだ。

下の図は SSDSE-B-2026(2023 年・47 都道府県)の人口動態 6 指標の実測相関で作った相関ネットワークだ。スライダーで閾値を動かすと、同じ 1 枚の相関行列から、まったく違う「関係の地図」が現れる。ノードはドラッグで動かせる(配置は見やすさのためだけで、意味は持たない)。

0.50

同じデータでも閾値を上げると、実測で次のようにエッジが激減する(下表は Python で数えた実値)。

|相関| 閾値 エッジ数 / 15 密度
0.30130.87
0.5070.47
0.7050.33
0.8030.20
0.9020.13

指標: 高齢化率(A1303/A1101)、年少人口率(A1301/A1101)、出生率(A4101/A1101)、死亡率(A4200/A1101)、転入率(A5101/A1101)、転出率(A5102/A1101)。閾値 0.90 まで残る最強の 2 本は 高齢化率–死亡率 (r=0.972)出生率–年少人口率 (r=0.934)、次点が 転入率–転出率 (r=0.872)。いずれも 2023 年 47 都道府県の実測値。

⚠️ 落とし穴(重要)

🚀 発展