論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
ランキング
Ranking
データ処理

🔖 キーワード索引

順位rankingsort順序Top-Kargsort順位相関rank

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

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

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

💡 30秒で分かる結論

🍰 まずはやさしく

ランキングは、順番をつけることです。

数値よりも順位が大切なときに使います。

スマホの検索結果などが身近な例です。

順位の決め方や統計的な使い方を学びます。

順位付け

📍 あなたが今見ているもの

🍰 まずはやさしく

ランキングは、スコアを順位に変える処理です。

データの絶対的な差をなくして比較するために使います。

都道府県の人口ランキングなどで考えます。

順位の付け方や、評価するための指標を読みます。

検索エンジンや推薦システムの内部は「アイテムをスコアでランキング」する処理。 統計の世界では「順位相関」「ノンパラメトリック検定」が代表的応用です。

本ページの主目的は「SSDSE-B-2026 の 47 都道府県を人口・高齢人口・出生数で順位付け」を題材に、 ranking が「順序のみを残して絶対値を捨てる変換」であることを定量化することである。 1 位東京と 47 位鳥取は人口比で約 26 倍だが、 29 位と 30 位の差はわずか数千人という非対称性が論点。

後段では (1) pandas の rank() 4 種 (average/min/dense/first)、 (2) Precision@k / MRR / nDCG など主要評価指標を都道府県ランキングで実装、 (3) Kendall τ と Spearman ρ の比較を扱う。

🎨 直感で掴む

🍰 まずはやさしく

ランキングは、順序だけを取り出す変換です。

極端に大きい値などの影響を避けるために使います。

テストの点数と順位の関係が分かりやすい例です。

人口や面積の順位を比べて、共通点を探ります。

テストの点数:90, 85, 85, 70, 60 → 順位は 1, 2, 3, 4, 5。 ただし同点の 85 は 2位タイ。 これを「2.5, 2.5」とするのが average 法、 「2, 2」が min 法、 「2, 3」が dense 法。 ランキングの肝は「絶対値ではなく順序情報だけ取り出す」という変換であり、 これによって外れ値や尺度の違いに左右されない頑健な比較ができる。

SSDSE-B-2026 で具体例を示そう。 47 都道府県の人口を多い順に並べると、 東京 (1409 万)、 神奈川 (923 万)、 大阪 (876 万)、 ... 鳥取 (54 万) となる。 1 位 東京は突出しているが、 「順位」だけ見れば 1 → 2 → 3 と均等に 1 ずつ増える。 同様に「高齢人口(A1303)」のランキングを並べ、 順位を比較すると、 人口と高齢人口は順位がほぼ揃う (Spearman rank correlation ≒ 0.98) のに対し、 人口と面積は順位が大きくズレる (東京は人口 1 位だが面積は 45 位)。 このように、 値そのものは線形性が壊れていても順位は強い結びつきを示す、 という現象がランキングの威力である。

もう一つ重要な直感: 順位は「単調変換に対して不変」である。 人口を log や平方根に変換しても順位は変わらない。 だから「分布が歪んで Pearson 相関では信用できない」場面でも、 順位ベースの Spearman 相関は素直に「単調関係の強さ」を測れる。 ランキングは外れ値・歪み・尺度の違いをすべて吸収し、 純粋な「並び順」だけを残す情報圧縮技法と考えてよい。

📐 定義/数式

🍰 まずはやさしく

ランキングとは、順位付けのことです。

同点の人をどう扱うかを決めるために使います。

テストで同じ点数だったときの順位を考えます。

平均を使った順位の出し方や、計算式を読みます。

ランキング(Ranking):順位付け

【順位の定義(average 法)】
$$ \text{rank}(x_i) = \frac{1}{|T_i|} \sum_{j \in T_i} \text{rank}_{\text{raw}}(j) $$
同値群 $T_i$ の中で順位を平均化。 $\text{rank}_{\text{raw}}$ は同値を考慮しない単純順位。

📐 もう少し深い数式 — tied rank と Spearman 計算式を 数式を言葉で読み解く

【Spearman 順位相関(tied なし版)】
$$ \rho = 1 - \frac{6 \sum_{i=1}^{n} d_i^2}{n(n^2 - 1)} $$
$d_i$ は i 番目の観測について「X の順位」と「Y の順位」の差。 すべての d² の合計が 0 なら ρ=1(完全一致)。
【tied rank の average 法】
$$ r_i = \frac{(\text{最小 raw rank}) + (\text{最大 raw rank})}{2} $$
同値が 2, 3 位を占めるなら (2+3)/2 = 2.5。 同値が 5, 6, 7 位を占めるなら (5+7)/2 = 6。

数式を言葉で読み解く(表)

記号 意味と直感
$d_i$i 番目の観測の「X のランク - Y のランク」。 完全一致なら全て 0。
$\sum d_i^2$順位の食い違いを 2 乗和したもの。 「どれだけ順位がズレているか」の総量。
$n(n^2-1)$最大ズレ(完全逆順)のときの 6×Σd² の値。 これで割って [-1, 1] に正規化する分母。
$r_i$tied 群の代表ランク(average なら平均、 min なら最小)。
$\rho$ρ=1 完全一致、 ρ=0 無関係、 ρ=-1 完全逆順。 ピアソン r と違い 順位だけ見る ので外れ値に強い。

🔬 記号・用語の読み解き

記号意味
$\text{rank}(x_i)$$x_i$ の順位
$T_i$$x_i$ と同値のインデックス集合
Top-K上位K件のみ取り出す(K=10など)
argsortソート後のインデックス(順位そのものではない)

📐 の average 法の式 $\text{rank}(x_i)=\frac{1}{|T_i|}\sum_{j\in T_i}\text{rank}_{\text{raw}}(j)$ を、 2023 年度の合計特殊出生率に当てはめて読む。 $T_i$ は「$x_i$ と同じ値を持つ県の集まり」、 $|T_i|$ はその県数、 $\text{rank}_{\text{raw}}$ は同点を気にせず上から 1, 2, 3, … と振った仮の順位。

🎯 このコードでやること:2023 年度の出生率に average・min・dense の 3 通りで順位を付け、 上位 12 県で同点の扱いを比べる。 5 県の総人口で argsort と rank の出力の違いも確かめる。

📥 入力例 SSDSE-B-2026.csv の 2023 年度 47 行、 A4103 合計特殊出生率 沖縄県 1.60 / 宮崎県 1.49 / 長崎県 1.49 / 鹿児島県 1.48 / … / 香川県 1.40 / 山口県 1.40 5 県の総人口: 北海道 5092000 / 秋田県 914000 / 東京都 14086000 / 愛知県 7477000 / 沖縄県 1468000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
t = df[df['SSDSE-B-2026'] == 2023].set_index('Prefecture')
tfr = t['A4103']
r = pd.DataFrame({'出生率': tfr,
                  'average': tfr.rank(ascending=False, method='average'),
                  'min': tfr.rank(ascending=False, method='min').astype(int),
                  'dense': tfr.rank(ascending=False, method='dense').astype(int)})
print(r.sort_values('average').iloc[:12].to_string())
# argsort は「何番目の行が何位か」ではなく「何位に来る行は何番目か」を返す
five = t.loc[['北海道', '秋田県', '東京都', '愛知県', '沖縄県'], 'A1101']
order = np.argsort(-five.to_numpy())
print('argsort(大きい順の行番号):', order.tolist(), '→', [five.index[i] for i in order])
print('rank(各行の順位)       :', five.rank(ascending=False).astype(int).tolist())
📤 実行例(実測) 出生率 average min dense Prefecture 沖縄県 1.60 1.0 1 1 宮崎県 1.49 2.5 2 2 長崎県 1.49 2.5 2 2 鹿児島県 1.48 4.0 4 3 熊本県 1.47 5.0 5 4 佐賀県 1.46 7.0 6 5 福井県 1.46 7.0 6 5 島根県 1.46 7.0 6 5 鳥取県 1.44 9.0 9 6 香川県 1.40 10.5 10 7 山口県 1.40 10.5 10 7 大分県 1.39 12.0 12 8 argsort(大きい順の行番号): [2, 3, 0, 4, 1] → ['東京都', '愛知県', '北海道', '沖縄県', '秋田県'] rank(各行の順位) : [3, 5, 1, 2, 4]

💬 宮崎県・長崎県は average で 2.5 位、 min で 2 位、 dense で 2 位。 3 県が並ぶ 1.46 は average で 7.0 位、 min で 6 位、 dense で 5 位になり、 同じ県でも方式によって 2 つずれる。 dense では 12 番目の大分県が 8 位で、 番号と県数が合わなくなる。 5 県の argsort は [2, 3, 0, 4, 1](東京都・愛知県・北海道・沖縄県・秋田県の行番号)、 rank は [3, 5, 1, 2, 4](北海道 3 位・秋田県 5 位・…)で、 argsort の結果をそのまま「順位」として列に入れると別の県に順位が付いてしまう。

🧮 実値で計算してみる

例:[90, 85, 85, 70, 60] → 単純順位 [1, 2, 3, 4, 5]、 average 順位 [1, 2.5, 2.5, 4, 5]。

🧮 SSDSE-B-2026 実値ランキング — 47 都道府県 人口(2023 年)

data/raw/SSDSE-B-2026.csv(encoding='cp932', skiprows=[1])の 2023 年・47 都道府県 を題材に、 「人口総数」列 A1101 を降順ランキングした実値を示す。 すべて cp932 読み込み・実 CSV から得た値であり、 合成データは一切使用していない。

上位 10 都道府県(人口降順)

順位 都道府県 人口(人) 全国シェア
1東京都14,086,00011.33 %
2神奈川県9,229,0007.42 %
3大阪府8,763,0007.05 %
4愛知県7,477,0006.01 %
5埼玉県7,331,0005.90 %
6千葉県6,257,0005.03 %
7兵庫県5,370,0004.32 %
8福岡県5,103,0004.10 %
9北海道5,092,0004.09 %
10静岡県3,555,0002.86 %

→ 上位 10 県だけで全国人口の約 58 % を占める。 「人口は順位の頭側に強く集中する」ことが、 ランキングを並べると一目で分かる。

下位 10 都道府県(人口昇順 = 後ろから)

順位 都道府県 人口(人) 東京との倍率
47鳥取県537,000×0.038(東京の 1/26)
46島根県650,000×0.046
45高知県666,000×0.047
44徳島県695,000×0.049
43福井県744,000×0.053
42佐賀県795,000×0.056
41山梨県796,000×0.057
40和歌山県892,000×0.063
39秋田県914,000×0.065
38香川県926,000×0.066

→ 下位 1 位(鳥取)と上位 1 位(東京)の差は 26 倍。 単純に順位だけ並べると「47 vs 1」だが、 値で見ると「3.8 %」と桁違いの差が隠れていることが分かる。 これが 「ランキングは順序情報しか残さない」 落とし穴である。

出生率ランキング — 「比例ベースのランク」と「絶対値ランク」の食い違い

同じ 47 県でも「絶対値(出生数)」のランクと「人口あたり(出生率 = 出生数 / 人口 × 1000)」のランクは 大きく入れ替わる。

県 出生数 A4101 出生数 順位 出生率(‰) 出生率 順位
沖縄県12,54914 位8.551 位
福岡県33,9427 位6.652 位
滋賀県9,24925 位6.573 位
東京都86,3481 位6.1312 位
秋田県3,61145 位3.9547 位
岩手県5,43236 位4.6746 位
北海道24,4309 位4.8045 位

→ 東京都は出生数 1 位だが、 出生率では 12 位。 北海道は出生数 9 位だが、 出生率は 45 位。 分母(母数)を揃えないランキングは、 規模効果に汚染される ことを示す典型例。

📊 Spearman 順位相関(人口 vs 出生数, 47 県)
実測値:ρ = 0.9781, p = 2.44 × 10⁻³²
→ 「人口の順位」と「出生数の順位」は ほぼ完全に一致。 つまり「出生数の県別順位」は、 人口の順位を見ているのとほぼ同義であり、 「出生数 1 位の県は子どもが多い」と言うのは無意味。 比較したいなら出生率(人口あたり)で見直すべき。

🧮 47 都道府県・人口 全順位表(2023 年、 SSDSE-B-2026 実値)

参考までに、 47 都道府県すべての人口順位を 1 つの表 に列挙する。 ランキングが「データの大局を一望させる」ツールであることを実感してほしい。

順位 県 人口 順位 県 人口
1東京都14,086,00025沖縄県1,468,000
2神奈川県9,229,00026滋賀県1,407,000
3大阪府8,763,00027山口県1,298,000
4愛知県7,477,00028奈良県1,296,000
5埼玉県7,331,00029愛媛県1,291,000
6千葉県6,257,00030長崎県1,267,000
7兵庫県5,370,00031青森県1,184,000
8福岡県5,103,00032岩手県1,163,000
9北海道5,092,00033石川県1,109,000
10静岡県3,555,00034大分県1,096,000
11茨城県2,825,00035宮崎県1,042,000
12広島県2,738,00036山形県1,026,000
13京都府2,535,00037富山県1,007,000
14宮城県2,264,00038香川県926,000
15新潟県2,126,00039秋田県914,000
16長野県2,004,00040和歌山県892,000
17岐阜県1,931,00041山梨県796,000
18群馬県1,902,00042佐賀県795,000
19栃木県1,897,00043福井県744,000
20岡山県1,847,00044徳島県695,000
21福島県1,767,00045高知県666,000
22三重県1,727,00046島根県650,000
23熊本県1,709,00047鳥取県537,000
24鹿児島県1,549,000——

※ 表は人口降順 2 列(左:1〜24 位、 右:25〜47 位)。 右下は列を揃えるための空欄。 数値は SSDSE-B-2026 の 2023 年 A1101 列を参照。

🎯 ユースケース詳細 — ランキングが実務で使われる 8 シーン

シーン 何を順位化するか・典型的指標
検索エンジンクエリと文書の関連度(BM25, TF-IDF, BERT)。 評価は NDCG@10。
推薦システムユーザー×アイテムスコア(行列分解 / Deep Learning)。 評価は Hit Rate@K, MRR。
EC サイトのおすすめ「あなたへのおすすめ」上位 5〜20 件。 在庫切れ時のスキップなど運用ルールあり。
広告オークション入札額 × 品質スコアで順位決定。 一般化第二価格(GSP)方式が長く標準として使われてきた。
スポーツ・コンテストElo レーティング、 BWF 世界ランキング。 同順位処理は min 法が一般的。
論文の影響度h-index, インパクトファクター, Google Scholar 引用数ランキング。
行政の指標SDGs ランキング、 都道府県幸福度ランキング。 政策評価では 分母揃えが必須。
クレジット・与信スコアカードの上位/下位を ROC で評価。 ローン承認の閾値設定に使う。

📝 ランキング結果をレポートに書くときの 6 か条

  1. 母数を明示:「47 都道府県中」「Top 10」など、 比較対象の集合を必ず書く。
  2. method を書く:「pandas.rank(method='min')」「numpy.argsort」など、 同順位処理を明示。
  3. 同順位の表記ルール:tied のとき「2 位タイ」「2.5 位」のどちらで書くかを冒頭で宣言。
  4. 絶対値と相対値を併記:「東京 1 位 (人口 14,086,000 / 全国比 11.33%)」のように、 順位だけでなく値も。
  5. 分母揃え(rate)も同時に提示:規模に汚染されないために「人口あたり」「面積あたり」も載せる。
  6. 順位相関で安定性チェック:「別年度と Spearman ρ = 0.95 で安定」と書けるとなお良い。

🧮 手計算で追える Spearman ρ — 5 県だけ抜粋

47 県の Spearman ρ を一気に手計算するのは無理なので、 東京 / 神奈川 / 大阪 / 沖縄 / 鳥取 の 5 県を抜き出して人口と出生数の Spearman ρ を電卓レベルで再現する。 ρ の式を 身体で 理解するためのドリル。

県 人口 (A1101) 人口 ランク X 出生数 (A4101) 出生数 ランク Y d = X-Y d²
東京都14,086,000186,348100
神奈川県9,229,000253,9913-11
大阪府8,763,000355,292211
沖縄県1,468,000412,549400
鳥取県537,00053,263500

Σd² = 2、 n = 5 → $$ \rho = 1 - \frac{6 \times 2}{5 \times (25-1)} = 1 - 0.1 = 0.9 $$
→ この 5 県では神奈川と大阪の順位が 1 つ入れ替わるだけなので ρ=0.9 と高い。 つまり「人口が多い県は出生数も多い」という単調関係を 5 県だけでも示せる。 47 県全体では ρ=0.9781。 中間順位の県でも入れ替わりは少数にとどまるため、 高い順位相関が保たれる。

→ Spearman ρ の式は 「順位の差を 2 乗して、 最大可能ズレで正規化する」 という直感そのもの。 数式が苦手でも電卓で追える計算量なので、 まずはこの 5 行表を自分で埋めてみることを推奨。

🧮 数式に値を入れて手で計算する: ランキング指標 nDCG

合成 5 件の関連度から DCG と nDCG を計算する。

Step 1: 関連度

取得順 rel = [3, 2, 3, 0, 1] 理想順 rel_ideal = [3, 3, 2, 1, 0]

Step 2: DCG

DCG = Σ rel_i / log₂(i+1) = 3/1 + 2/1.585 + 3/2 + 0/2.322 + 1/2.585 = 3 + 1.262 + 1.5 + 0 + 0.387 = 6.149 IDCG = 3 + 3/1.585 + 2/2 + 1/2.322 + 0 = 3+1.893+1+0.431+0 = 6.323

Step 3: nDCG

nDCG = DCG/IDCG = 6.149/6.323 ≈ 0.972

🐍 Python で再現

1
2
3
4
5
6
7
8
import numpy as np
rel = np.array([3, 2, 3, 0, 1])
ideal = np.sort(rel)[::-1]
def dcg(r): return (r / np.log2(np.arange(2, len(r)+2))).sum()
ndcg = dcg(rel) / dcg(ideal)
print(f"DCG: {dcg(rel):.3f}")
print(f"IDCG: {dcg(ideal):.3f}")
print(f"nDCG: {ndcg:.3f}")

📤 実行結果

DCG: 6.149 IDCG: 6.323 nDCG: 0.972

💬 DCG 6.149・IDCG 6.323・nDCG 0.972 は Step 2〜3 の手計算と一致する。 取得順は 2 位に rel=2、 3 位に rel=3 を置いており、 理想順と入れ替わっているのはこの 2 件だけなので、 失ったのは 6.323 − 6.149 = 0.174 にとどまる。

🧮 ケンドールの τ を手で数える — 5 県の人口順位と出生率順位

2 つのランキングの一致度は、 順位のペアを 1 組ずつ見て「同じ向きか(一致)」「逆向きか(不一致)」を数えれば求まる。 $n$ 件から作れるペアは $n(n-1)/2$ 組で、

$$\tau = \frac{C - D}{n(n-1)/2}$$

$C$ は一致ペア数、 $D$ は不一致ペア数。 2023 年度の北海道・秋田県・東京都・愛知県・沖縄県の 5 県で、 総人口(A1101)の順位と合計特殊出生率(A4103)の順位を比べる。

県総人口出生率人口順位出生率順位
北海道5,092,0001.0634
秋田県914,0001.1053
東京都14,086,0000.9915
愛知県7,477,0001.2922
沖縄県1,468,0001.6041

🎯 このコードでやること:Step 1〜5 を itertools.combinations で 10 組のペアを作って数え、 scipy.stats の kendalltau・spearmanr と一致するか確かめる。

📥 入力例 SSDSE-B-2026.csv の 2023 年度から 5 県(北海道・秋田県・東京都・愛知県・沖縄県) A1101 総人口 / A4103 合計特殊出生率 → それぞれ大きい順に 1〜5 位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from itertools import combinations
import pandas as pd
from scipy.stats import kendalltau, spearmanr
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
five = ['北海道', '秋田県', '東京都', '愛知県', '沖縄県']
t = df[(df['SSDSE-B-2026'] == 2023) & df['Prefecture'].isin(five)].set_index('Prefecture').loc[five]
r = pd.DataFrame({'総人口': t['A1101'], '出生率': t['A4103'],
                  '人口順位': t['A1101'].rank(ascending=False).astype(int),
                  '出生率順位': t['A4103'].rank(ascending=False).astype(int)})
print(r.to_string())
# Step 2〜3: 10 組のペアを一致(同じ向き)と不一致(逆向き)に分ける
C = D = 0
for a, b in combinations(five, 2):
    s = (r.loc[a, '人口順位'] - r.loc[b, '人口順位']) * (r.loc[a, '出生率順位'] - r.loc[b, '出生率順位'])
    C += s > 0; D += s < 0
    print(f'{a}-{b}: {"一致" if s > 0 else "不一致"}')
n = len(five)
print(f'C={C}, D={D}, τ=(C-D)/(n(n-1)/2)={(C - D) / (n * (n - 1) / 2):.3f}')
print(f'scipy kendalltau: {kendalltau(r["人口順位"], r["出生率順位"]).statistic:.3f}'
      f' / spearmanr: {spearmanr(r["人口順位"], r["出生率順位"]).statistic:.3f}')
📤 実行例(実測) 総人口 出生率 人口順位 出生率順位 Prefecture 北海道 5092000 1.06 3 4 秋田県 914000 1.10 5 3 東京都 14086000 0.99 1 5 愛知県 7477000 1.29 2 2 沖縄県 1468000 1.60 4 1 北海道-秋田県: 不一致 北海道-東京都: 不一致 北海道-愛知県: 一致 北海道-沖縄県: 不一致 秋田県-東京都: 不一致 秋田県-愛知県: 一致 秋田県-沖縄県: 一致 東京都-愛知県: 不一致 東京都-沖縄県: 不一致 愛知県-沖縄県: 不一致 C=3, D=7, τ=(C-D)/(n(n-1)/2)=-0.400 scipy kendalltau: -0.400 / spearmanr: -0.500

💬 10 組のうち一致 3・不一致 7 で τ = −0.400、 scipy の kendalltau も −0.400、 spearmanr は −0.500 で、 手計算とすべて一致した。 5 県の中では「人口が多い県ほど出生率が低い」向きに寄っているが、 愛知県はどちらも 2 位で、 秋田県は人口最下位なのに出生率は 3 位と、 きれいな逆順ではない。 5 県だけの τ は 1 組の入れ替わりで 0.2 動くので、 傾向を言うには 47 県全体で計算する。

🧠 理解度チェック(実測値で解く)

  1. 47 都道府県のペアは何組あるか。 そのうち 1 組だけが不一致なら τ はいくつか。
    解答:$47\times46/2=1{,}081$ 組。 $\tau=(1080-1)/1081=0.998$。
  2. 2023 年度の一般病院数(I510120)で東京都は 1 位だが、 人口 10 万人あたりでは何位か。
    解答:4.17 施設で 41 位(下の ⚠️ の実測)。 実数の順位と人口あたりの順位の Kendall τ は −0.008 で、 ほとんど無関係になる。
  3. 2012→2023 年度に合計特殊出生率は 47 県すべてで下がった。 それでも順位が上がった県があるのはなぜか。
    解答:順位は他県との相対位置なので、 下がり幅が小さければ上がる。 富山県は 1.42→1.35 で 32 位から 15 位になった。
  4. 出生率で「上位 2 県」を nlargest(2) で取り出すと、 何が起きるか。
    解答:2 位の 1.49 が長崎県と宮崎県で並ぶため、 既定では長崎県だけが入り、 宮崎県が黙って落ちる。 keep="all" にすると 3 県が返る。

🎮 触って理解する

ランキングの核心は「複数の指標をどう重み付けして 1 本の総合スコアにまとめるか」で順位がガラリと変わる点にある。 下の図は SSDSE-B-2026(2023 年・実測値)の 7 県を、 人口(規模)と 出生率‰(人口あたり)の 2 指標で持ち、 スライダーで重みを変えると総合スコアが再計算され、 バーが リアルタイムに並び替わる。 図の上を左右にドラッグ(タッチ可)しても重みが動く。

⚖️ 総合スコアの重み
出生率 重視 人口 重視
総合スコア = 人口重み × 正規化人口 + 出生率重み × 正規化出生率(各指標を min-max で 0〜100 に揃えてから合成)

🖐 何を体感できるか

🧠 直感 — ランキングは「合成関数」である

単一指標のランキングは単純なソートだが、 現実の「総合ランキング」(幸福度・住みやすさ・大学ランキング等)は複数指標の 加重和で作られる。 つまり順位は「生データ」ではなく「重みという意思決定の産物」だ。 スライダーを動かすと同じ 7 県から無数のランキングが生まれることが、 それを物語る。

⚠️ よくある落とし穴

🚀 発展 — ランキング学習と評価指標

「良い順位」を機械学習で作るのが Learning to Rank(LTR)。 損失の設計で 3 系統に分かれる:pointwise(各アイテムのスコアを回帰)、 pairwise(2 件の前後関係を当てる/RankNet 等)、 listwise(リスト全体の並びを最適化/LambdaMART 等)。 評価には nDCG(上位ほど重い割引利得。 本ページの計算例で nDCG≈0.972 を手計算済み)や MAP(平均適合率)、 MRR(最初の正解の逆数)を使い、 「上位に正解を集められたか」を測る。 順位の並びそのものを図示するなら 棒グラフが最も直感的だ。

🐍 Python での実装例

SSDSE-B-2026 などの実データを使った最小コード(5行):

📥 入力例(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
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1)
df = df[df['年度'] == 2023]   # 12 年度分が入っているので 2023 年の 47 都道府県に絞る
df['順位'] = df['総人口'].rank(ascending=False, method='average')
print(df[['都道府県', '総人口', '順位']].nsmallest(5, '順位'))  # Top 5
📤 実行例(実測) 都道府県 総人口 順位 144 東京都 14086000 1.0 156 神奈川県 9229000 2.0 312 大阪府 8763000 3.0 264 愛知県 7477000 4.0 120 埼玉県 7331000 5.0

💬 2023 年の 47 都道府県に絞ったので、総人口の順位は東京都 1,408.6 万人、神奈川県 922.9 万人、大阪府 876.3 万人、愛知県 747.7 万人、埼玉県 733.1 万人と重複なく並んだ。年度で絞らずに先頭の数値列を順位付けすると、年度 2023 の 47 行が同点になって全員 24.0 位という無意味な結果になる。同点が無いので method='average' でも整数順位になっているが、率や順位点のように同点が出る指標では method の違いが結果を変える。

※ data/raw/SSDSE-B-2026.csv は e-Stat SSDSE から取得した実データを想定。

🐍 Python 実装 — pandas.rank の 4 method 比較

① 47 都道府県を人口でランキングする

🎯 このコードでやること:SSDSE-B-2026 の 2023 年・47 都道府県を pd.DataFrame.rank() で人口降順ランキングし、 上位 5 / 下位 5 を表示する。

📥 入力データ(SSDSE-B-2026.csv を cp932 で読み込んだ抜粋):

SSDSE-B-2026 Code Prefecture A1101 2023 R13000 東京都 14086000 2023 R14000 神奈川県 9229000 2023 R27000 大阪府 8763000 ... 2023 R31000 鳥取県 537000
1
2
3
4
5
6
7
8
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023].copy()          # 47 都道府県のみ
d['順位'] = d['A1101'].rank(method='min', ascending=False)

print(d.nsmallest(5, '順位')[['Prefecture', 'A1101', '順位']])
print(d.nlargest(5,  '順位')[['Prefecture', 'A1101', '順位']])

📤 実行結果:

Prefecture A1101 順位 144 東京都 14086000 1.0 156 神奈川県 9229000 2.0 312 大阪府 8763000 3.0 264 愛知県 7477000 4.0 120 埼玉県 7331000 5.0 Prefecture A1101 順位 360 鳥取県 537000 47.0 372 島根県 650000 46.0 456 高知県 666000 45.0 420 徳島県 695000 44.0 204 福井県 744000 43.0

💬 結果の読み方:人口 14,086,000 の東京が 1 位、 537,000 の鳥取が 47 位(東京の 1/26)。 順位は等差(1, 2, 3, ...)に圧縮されるため、 値の規模差は失われる。

🐍 Python 実装 — 同順位(tied)の 4 method を比較

🎯 このコードでやること:同順位を含む配列 [90, 85, 85, 70, 60] に対して、 average / min / max / dense / first の 5 種類のランキング方式を比較する。 SSDSE データには厳密な同値はないが、 教育目的でランキング規則そのものを比較する。

📥 入力データ(小規模教材データ):

scores = [90, 85, 85, 70, 60] ↑ ↑↑ ← 85 が 2 つ(tied)
1
2
3
4
5
import pandas as pd

s = pd.Series([90, 85, 85, 70, 60])
for m in ['average', 'min', 'max', 'dense', 'first']:
    print(m, ':', s.rank(method=m, ascending=False).tolist())

📤 実行結果:

average : [1.0, 2.5, 2.5, 4.0, 5.0] ← 同順位の平均(2 と 3 → 2.5) min : [1.0, 2.0, 2.0, 4.0, 5.0] ← 最小値(2 位タイ) max : [1.0, 3.0, 3.0, 4.0, 5.0] ← 最大値(3 位タイ) dense : [1.0, 2.0, 2.0, 3.0, 4.0] ← 詰める(次は 3 位) first : [1.0, 2.0, 3.0, 4.0, 5.0] ← 出現順で機械的に

💬 結果の読み方:用途で選ぶ。 (a) スポーツ表彰なら min(2 位 2 人)。 (b) 統計の Spearman 計算なら average(理論上正しい)。 (c) 順位の番号を飛ばさずに詰めたいなら dense(85 の 2 件の次の 70 が 4 位ではなく 3 位になる)。 (d) ランキング表で重複を許さない実務用途なら first(先勝ち、 出現順)。

🐍 Python 実装 — Spearman 順位相関(47 県)

🎯 このコードでやること:「人口 (A1101)」と「出生数 (A4101)」の 順位がどれだけ一致するか を、 scipy.stats.spearmanr で計算する。 ピアソン相関と違い、 非線形でも順位さえ同じなら ρ=1 に近づく。

📥 入力データ:上で読み込んだ d(47 行)。 A1101 列と A4101 列のみ使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import pandas as pd
from scipy.stats import spearmanr, kendalltau, pearsonr

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

rho, p_s = spearmanr(d['A1101'], d['A4101'])
tau, p_k = kendalltau(d['A1101'], d['A4101'])
r,  p_r = pearsonr (d['A1101'], d['A4101'])
print(f'Spearman ρ = {rho:.4f}  p = {p_s:.2e}')
print(f'Kendall  τ = {tau:.4f}  p = {p_k:.2e}')
print(f'Pearson  r = {r:.4f}  p = {p_r:.2e}')

📤 実行結果(実 CSV で計算した値):

Spearman ρ = 0.9781 p = 2.44e-32 Kendall τ = 0.8964 p = 6.33e-19 Pearson r = 0.9954 p = 1.53e-47

💬 結果の読み方:3 つすべてが「人口と出生数は強く連動」を示す。 ρ=0.978 は「順位がほぼ一致」、 τ=0.896 は「ランダムに 2 つの県を選ぶと、 人口と出生数で大小の向きがそろう確率が (1+τ)/2 ≈ 0.95」と読む。 ピアソンが最も高いのは「ほぼ線形」だから。 ノンパラなら Spearman、 一致度を直感的に語るなら Kendall。

🐍 Python 実装 — argsort と Top-K(推薦システム式)

🎯 このコードでやること:47 都道府県の人口を numpy.argsort で並べ替え、 「順位(rank)」と「並べ替えインデックス(argsort)」の違い を確認する。 さらに numpy.argpartition で高速 Top-K を取り出す。

📥 入力データ:47 都道府県の人口配列(NumPy ndarray)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import pandas as pd
import numpy as np

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

# 降順 argsort(インデックス)
idx_desc = np.argsort(-pop)                  # 例: [12, 13, 26, 22, ...]
print('Top 3 (argsort):', [(name[i], pop[i]) for i in idx_desc[:3]])

# 高速 Top-K(順位は保証しないが O(n))
top5 = np.argpartition(-pop, 5)[:5]
top5_sorted = top5[np.argsort(-pop[top5])]
print('Top 5 (argpartition+sort):', [name[i] for i in top5_sorted])

📤 実行結果:

Top 3 (argsort): [('東京都', 14086000), ('神奈川県', 9229000), ('大阪府', 8763000)] Top 5 (argpartition+sort): ['東京都', '神奈川県', '大阪府', '愛知県', '埼玉県']

💬 結果の読み方:argsort は「並べ替え後のインデックス列」を返すだけで、 「順位」そのものではない(順位は rank())。 argpartition は K 個を選ぶだけなので O(n)、 47 県程度なら差は無視できるが、 1 億行のログから Top-100 を取り出すような推薦システムでは桁違いに速い。

🐍 Python 実装 — NDCG と MRR(ランキングの評価指標)

🎯 このコードでやること:「47 都道府県の理想ランキング(人口順)」と「予測ランキング(出生数 A4101 の順)」の NDCG@10(Normalized Discounted Cumulative Gain)を計算する。 検索・推薦の標準評価指標。

📥 入力データ:47 県の人口を「relevance スコア」とし、 別の列でランキングして上位 10 を取り出す。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import numpy as np
import pandas as pd
from sklearn.metrics import ndcg_score

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

# relevance = 人口(理想は人口順 = 正解)
rel = np.asarray([d['A1101'].values])
# 予測スコア = 出生数 (A4101) で並べる
pred = np.asarray([d['A4101'].values])

print(f'NDCG@10 (出生数で予測) = {ndcg_score(rel, pred, k=10):.4f}')
print(f'NDCG@5  (出生数で予測) = {ndcg_score(rel, pred, k=5):.4f}')

# MRR: 「東京都」を 1 個の正解として扱う場合
idx = np.argsort(-pred[0])
tokyo_pos = np.where(d['Prefecture'].values[idx] == '東京都')[0][0] + 1
print(f'MRR (東京都の順位の逆数) = 1/{tokyo_pos} = {1.0/tokyo_pos:.4f}')

📤 実行結果:

NDCG@10 (出生数で予測) = 0.9983 NDCG@5 (出生数で予測) = 0.9980 MRR (東京都の順位の逆数) = 1/1 = 1.0000

💬 結果の読み方:出生数で並べても、 人口の Top-10 にほぼ一致するため NDCG≈1.0。 一方、 「出生率」で並べると沖縄が 1 位になり、 NDCG@10 が一気に下がる(自分で試してみよう)。 MRR=1.0 は「東京が予測でも 1 位」だから。 ランキングの「上位ほど重い」という性質を log で表現するのが NDCG の本質。

🐍 ランキング評価指標を SSDSE-B-2026 で実値計算する

検索や推薦ではランキングの「上位ほど正解を当てているか」が重要である。 ここでは nDCG (normalized Discounted Cumulative Gain)、 MAP (Mean Average Precision)、 MRR (Mean Reciprocal Rank) の 3 つを、 SSDSE-B-2026 の都道府県「人口」順位を題材に実値で比べる。 数式を言葉で読み解く工程をはさみ、 単独の指標に頼らず複数指標を併用する理由を理解しよう。

📐 nDCG の定義

$$ \mathrm{DCG}_k = \sum_{i=1}^{k} \frac{2^{rel_i} - 1}{\log_2(i+1)} \quad,\quad \mathrm{nDCG}_k = \frac{\mathrm{DCG}_k}{\mathrm{IDCG}_k} $$

数式を言葉で読み解く: rel_i は位置 i のアイテムの関連度(人口の正解スコア等)、 分母 log2(i+1) は順位が下がるほど割引が大きくなる重み。 IDCG は理想ランキング(関連度の高い順に並べた時)の DCG なので、 nDCG は 0〜1 に正規化され「理想にどれだけ近いか」を表す。

🧮 SSDSE-B-2026 で人口上位 5 件のランキング評価

実値計算: 2023 年の人口上位 5 件 [東京, 神奈川, 大阪, 愛知, 埼玉] に人口順で正解関連度 5,4,3,2,1 を割り当て、 モデル A がこの順どおりに予測したとき、 各指標を表で比較する。

順位 iアイテム関連度 rellog2(i+1)寄与 (2^rel-1)/log
1東京51.00031.000
2神奈川41.5859.464
3大阪32.0003.500
4愛知22.3221.292
5埼玉12.5850.387
DCG@5 = 31.000 + 9.464 + 3.500 + 1.292 + 0.387 = 45.643 IDCG@5 = 45.643 (理想順=実順なので一致) nDCG@5 = 45.643 / 45.643 = 1.000

🐍 nDCG・MAP・MRR を sklearn と numpy で計算

🎯 このコードでやること:SSDSE-B-2026 の都道府県人口上位 5 件を 2 つのモデル A,B が予測した想定で、 nDCG@5、 MAP@5、 MRR の 3 指標を sklearn.metrics.ndcg_score と素の numpy で算出する。

📥 入力例 (SSDSE-B-2026, 人口上位 5 件):

📥 SSDSE-B-2026 (2023 年・人口上位 5 県) R13000 東京都 14,086,000 R14000 神奈川県 9,229,000 R27000 大阪府 8,763,000 R23000 愛知県 7,477,000 R11000 埼玉県 7,331,000
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
# SSDSE-B-2026 の人口データを正解関連度として、 2 モデルのランキングを評価
import numpy as np
from sklearn.metrics import ndcg_score

# 正解の関連度 (高いほど真の正解)
y_true = np.array([[5, 4, 3, 2, 1]])  # 東京,神奈川,大阪,愛知,埼玉(2023 年の人口順)

# モデル A: 正解順に予測 / モデル B: 順序を一部入替え
y_score_A = np.array([[5.0, 4.0, 3.0, 2.0, 1.0]])
y_score_B = np.array([[3.0, 5.0, 2.0, 4.0, 1.0]])

# nDCG@5
ndcg_A = ndcg_score(y_true, y_score_A, k=5)
ndcg_B = ndcg_score(y_true, y_score_B, k=5)

# 各モデルのスコアで並べ替えたときの「正解関連度の並び」
# (スコアそのものではなく、スコア順に並べた y_true を評価する)
rel_A = y_true[0][np.argsort(-y_score_A[0])]   # [5 4 3 2 1]
rel_B = y_true[0][np.argsort(-y_score_B[0])]   # [4 2 5 3 1]

# MAP@5: 関連あり=rel>=3 として平均精度を計算
def average_precision(rel_sorted):
    hits, ap = 0, 0.0
    for i, r in enumerate(rel_sorted, 1):
        if r >= 3:
            hits += 1
            ap += hits / i
    return ap / max(1, sum(1 for r in rel_sorted if r >= 3))

map_A = average_precision(rel_A)
map_B = average_precision(rel_B)

# MRR: 最初に関連あり(rel>=3)が出た順位の逆数
def mrr(rel_sorted):
    for i, r in enumerate(rel_sorted, 1):
        if r >= 3: return 1.0 / i
    return 0.0

print(f"nDCG@5  A={ndcg_A:.4f}  B={ndcg_B:.4f}")
print(f"MAP@5   A={map_A:.4f}  B={map_B:.4f}")
print(f"MRR     A={mrr(rel_A):.4f}  B={mrr(rel_B):.4f}")

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

nDCG@5 A=1.0000 B=0.9191 MAP@5 A=1.0000 B=0.8056 MRR A=1.0000 B=1.0000

💬 モデル A は理想順どおりなので 3 指標とも 1.0。 モデル B はスコア順に並べると関連度が [4, 2, 5, 3, 1](神奈川, 愛知, 東京, 大阪, 埼玉)となり、 nDCG@5 は 0.9191、 MAP@5 は 1 位・3 位・4 位の適合率 1, 2/3, 3/4 の平均で 0.8056 に下がる。 一方 MRR は 1 位の神奈川 (rel=4) がすでに「関連あり」なので B も 1.0 のままで、 最初の正解しか見ない指標では順序の崩れが見えない。 なお sklearn の ndcg_score は利得に rel をそのまま使い、 上の表の 2^rel−1 とは異なる。

⚠️ ランキング評価の落とし穴

🧭 図で見る順位の性質 — 間隔・一致度・安定性

2023 年度 47 都道府県の総人口を 1 位(東京都 1,409 万人)から 47 位(鳥取県 54 万人)まで並べた図(対数軸)。29 位愛媛県 129 万人と 30 位長崎県 127 万人の差は 2.4 万人
図A:順序統計量のイメージ。2023 年度の総人口を多い順に 1〜47 位へ並べた(縦軸は対数)。ランキングは「並べ替えた後に何番目にあるか」だけを残すので、順位は等間隔でも値の差は等間隔ではない。1 位東京都 1,409 万人と 2 位神奈川県 923 万人の差は 486 万人、29 位愛媛県と 30 位長崎県の差は 2.4 万人、最も近い 41 位山梨県と 42 位佐賀県の差は 0.1 万人(1,000 人)しかない。1 位 / 47 位(鳥取県 54 万人)は 26.2 倍。
総人口の順位と 65 歳以上人口の順位の散布図(Spearman ρ 0.985、Kendall τ 0.929)と、総人口の順位と 2022→2023 年度の人口増減率の順位の散布図(ρ 0.692、τ 0.517)
図B:2 つのランキングの一致度(2023 年度 47 都道府県、点線 = 完全一致)。左の総人口と 65 歳以上人口の順位はほぼ点線上に並び、Spearman ρ = 0.985、Kendall τ = 0.929(最も外れるのは沖縄県 25 位 → 36 位)。右の総人口と人口増減率(2022→2023 年度)の順位も ρ = 0.692、τ = 0.517 と正の順位相関があり、人口の多い県ほど減り方が小さい。ただし山梨県(41 位 → 17 位)・佐賀県(42 位 → 18 位)・新潟県(15 位 → 39 位)のように大きく入れ替わる県もある。同じデータでも τ は ρ より小さく出るので、どちらの指標かを必ず併記する。
47 都道府県の出生率(人口千人当たり)の順位が 2012〜2023 年度の 12 年でとった最小〜最大の幅を、平均順位の順に並べた図。沖縄県と秋田県は 12 年とも 1 位と 47 位で幅 0、静岡県は 16〜36 位
図C:順位はどれくらい安定しているか。出生率(出生数 / 総人口 × 1000)の都道府県順位を 2012〜2023 年度の 12 年分求め、県ごとの最小〜最大の幅を線で、12 年の平均順位を縦棒で、2023 年度の順位を点で示した。値が離れている両端は安定しており、沖縄県は 12 年とも 1 位(2023 年度 8.55、2 位福岡県 6.65)、秋田県は 12 年とも 47 位。中位は値が僅差で並ぶため入れ替わりが激しく、幅の中央値は 8 位、静岡県は 16〜36 位、栃木県は 19〜39 位を動く(2023 年度の 24 位徳島県 5.616 と 25 位長野県 5.551 の差は 0.065)。「何位か」を語る前に、その順位が年や標本の揺れでどれだけ動くかを確かめる。

🐍 上位 k 件の境界で同点が切られる — nlargest の keep 引数

「上位 k 件」を取り出すとき、 k 位の値と同じ値がほかにもあると、 どれを入れてどれを落とすかを決めなければならない。 pandas の nlargest は既定(keep="first")で元の並び順が先の行を残し、 残りを黙って落とす。

🎯 このコードでやること:2023 年度の合計特殊出生率で上位 2・5・10 県を nlargest で取り出し、 keep の 3 通りで結果を比べ、 境界で同じ値の県がいくつ並ぶかを数える。

📥 入力例 SSDSE-B-2026.csv の 2023 年度 47 行、 A4103 合計特殊出生率 沖縄県 1.60 / 宮崎県 1.49 / 長崎県 1.49 / 鹿児島県 1.48 / 熊本県 1.47 …(小数 2 桁なので値の種類は少ない)
1
2
3
4
5
6
7
8
9
10
11
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
tfr = df[df['SSDSE-B-2026'] == 2023].set_index('Prefecture')['A4103']
print('nlargest(2) 既定(keep="first"):', tfr.nlargest(2).to_dict())
print('nlargest(2, keep="last")      :', tfr.nlargest(2, keep='last').to_dict())
print('nlargest(2, keep="all")       :', tfr.nlargest(2, keep='all').to_dict())
# 「上位 k 県」の境界で同じ値がいくつ並ぶか
for k in [2, 5, 10]:
    cut = tfr.nlargest(k).iloc[-1]
    print(f'上位 {k:2d} 県の境界値 {cut}: 境界と同じ値の県 {int((tfr == cut).sum())} 県, keep="all" で {len(tfr.nlargest(k, keep="all"))} 県')
print('2023 年度の出生率の異なり値:', tfr.nunique(), '/ 47 県')
📤 実行例(実測) nlargest(2) 既定(keep="first"): {'沖縄県': 1.6, '長崎県': 1.49} nlargest(2, keep="last") : {'沖縄県': 1.6, '宮崎県': 1.49} nlargest(2, keep="all") : {'沖縄県': 1.6, '長崎県': 1.49, '宮崎県': 1.49} 上位 2 県の境界値 1.49: 境界と同じ値の県 2 県, keep="all" で 3 県 上位 5 県の境界値 1.47: 境界と同じ値の県 1 県, keep="all" で 5 県 上位 10 県の境界値 1.4: 境界と同じ値の県 2 県, keep="all" で 11 県 2023 年度の出生率の異なり値: 31 / 47 県

💬 上位 2 県は、 既定では沖縄県と長崎県、 keep="last" では沖縄県と宮崎県になり、 同じ「上位 2 県」でも中身が変わる。 keep="all" なら 3 県が返る。 上位 10 県も境界の 1.40 に 2 県が並ぶので、 keep="all" では 11 県になる。 47 県の出生率は小数 2 桁で 31 種類しか値がないため、 境界での同点は珍しくない。 表彰や予算配分に使うなら、 同点をどう扱うかを先に決めて書いておく。

🐍 同点があるときのスピアマン ρ — 簡便式と scipy の違い

📐 の $\rho = 1 - 6\sum d_i^2 / (n(n^2-1))$ は「同点がない」ときの式で、 同点があるときの正式な ρ は「平均順位どうしのピアソン相関」になる。 出生率のように同点の多い列で、 どれだけずれるかを確かめる。

🎯 このコードでやること:2023 年度 47 県の合計特殊出生率と高齢化率に平均順位を付け、 簡便式・平均順位のピアソン相関・scipy.stats.spearmanr の 3 通りで ρ を計算する。

📥 入力例 SSDSE-B-2026.csv の 2023 年度 47 行 A4103 合計特殊出生率(小数 2 桁で同点が多い) / 高齢化率 = A1303 ÷ A1101 × 100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd
from scipy.stats import spearmanr, pearsonr
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
t = df[df['SSDSE-B-2026'] == 2023]
x = t['A4103']                                   # 合計特殊出生率(同点が多い)
y = t['A1303'] / t['A1101'] * 100                # 高齢化率(%)
rx, ry = x.rank(), y.rank()                      # 同点は平均順位
n = len(t)
d2 = ((rx - ry) ** 2).sum()
print(f'同点のある県数: 出生率 {int(x.duplicated(keep=False).sum())} 県, 高齢化率 {int(y.duplicated(keep=False).sum())} 県')
print(f'Σd² = {d2:.1f}')
print(f'簡便式 1 - 6Σd²/(n(n²-1)) = {1 - 6 * d2 / (n * (n ** 2 - 1)):.4f}')
print(f'平均順位どうしの Pearson 相関   = {pearsonr(rx, ry).statistic:.4f}')
print(f'scipy.stats.spearmanr         = {spearmanr(x, y).statistic:.4f}')
📤 実行例(実測) 同点のある県数: 出生率 30 県, 高齢化率 0 県 Σd² = 12568.0 簡便式 1 - 6Σd²/(n(n²-1)) = 0.2734 平均順位どうしの Pearson 相関 = 0.2729 scipy.stats.spearmanr = 0.2729

💬 出生率は 47 県中 30 県がどこかの県と同点で、 高齢化率には同点がない。 Σd² = 12,568 から簡便式では 0.2734、 平均順位どうしのピアソン相関と scipy の spearmanr は 0.2729 で、 差は 0.0005 だった。 同点が 30 県あってもこの程度のずれで、 結論(出生率の高い県ほど高齢化率も高い、 弱い正の順位相関)は変わらない。 ただし同点が多い 5 段階評価のような列では差が大きくなるので、 手計算の簡便式ではなく scipy の値を報告する。

⚠️ よくある落とし穴

❌ 同順位の扱いを統一しない
method を指定しないとデフォルト挙動に依存。 明示する。
❌ 昇順/降順の逆転
scores は降順、 ranks は昇順 — 混乱の元。
❌ 値の差を捨てている
1位と2位の差が10点でも 0.01点でも順位は同じ。 文脈次第で問題。
❌ Top-Kの罠
K=10 と K=100 で評価が変わる。 評価指標と K を合わせる。

⚠️ 実データで気づく追加の落とし穴 5 件

❌ 分母を揃えていない比較ランキング
出生数で 47 県を並べると東京 1 位だが、 出生率(÷人口)に直すと 東京は 5 位、 沖縄が 1 位。 規模に汚染されたランキングは政策議論を歪める。
❌ method を指定しない
pandas のデフォルトは method='average'、 numpy.argsort は安定ソートで first 相当。 「順位 2.5」と「順位 2」が混在するレポートになりがち。
❌ 単位混同(円 vs ドル、 千人 vs 万人)
SSDSE 列 A1101 は「人」単位(東京 14,086,000 人)だが、 別資料が「千人」(東京 14,086)と書いていることがある。 ランキングは順位だけ見るので食い違いに気づきにくい。
❌ ランキングは順序情報しか残さない
東京 14,086,000 と神奈川 9,229,000 の差は 480 万人だが、 順位上は「1, 2」と隣接して見える。 「順位だけ見て政策評価」をすると、 値の絶対差が見えない。
❌ Top-K と K の取り方で順位の意味が変わる
「東京・神奈川・大阪・愛知・埼玉」の Top 5 は揺るぎないが、 Top 10 になると 9 位(北海道 5,092,000)と 8 位(福岡 5,103,000)が わずか 11,000 人差で逆転リスクがある。 K の境界付近は順位が脆い。

⚠️ 実データで確かめる: 年度で絞らずに順位を付けると「東京都が 1〜12 位」になる

SSDSE-B-2026 は 47 県 × 12 年度 = 564 行のパネルデータ。 年度で絞らずに rank() をかけると、 同じ県の別の年度どうしが順位を奪い合う。

🎯 このコードでやること:総人口に 564 行全体で順位を付けた場合と、 groupby("年度") で年度ごとに順位を付けた場合を比べ、 年度内の順位が動いた県の推移も見る。

📥 入力例 SSDSE-B-2026.csv 全 564 行(年度・都道府県・A1101 総人口) 2023 東京都 14086000 / 2022 東京都 14038000 / … / 2012 鳥取県 …
1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df.rename(columns={'SSDSE-B-2026': '年度', 'Prefecture': '都道府県', 'A1101': '総人口'})
# 年度で絞らずに 564 行全体で順位を付けると
df['順位_全体'] = df['総人口'].rank(ascending=False, method='min').astype(int)
print('全体順位の上位 13 行:')
print(df.nsmallest(13, '順位_全体')[['年度', '都道府県', '総人口', '順位_全体']].to_string(index=False))
print('順位の最大値:', df['順位_全体'].max(), '/ 行数:', len(df))
# 年度ごとに順位を付ける
df['順位_年度内'] = df.groupby('年度')['総人口'].rank(ascending=False, method='min').astype(int)
p = df[df['都道府県'].isin(['沖縄県', '奈良県', '山口県', '愛媛県'])].pivot(index='都道府県', columns='年度', values='順位_年度内')
print(p[[2012, 2016, 2020, 2023]].to_string())
📤 実行例(実測) 全体順位の上位 13 行: 年度 都道府県 総人口 順位_全体 2023 東京都 14086000 1 2020 東京都 14047594 2 2022 東京都 14038000 3 2021 東京都 14010000 4 2019 東京都 14007000 5 2018 東京都 13887000 6 2017 東京都 13768000 7 2016 東京都 13646000 8 2015 東京都 13515271 9 2014 東京都 13399000 10 2013 東京都 13307000 11 2012 東京都 13234000 12 2020 神奈川県 9237337 13 順位の最大値: 564 / 行数: 564 年度 2012 2016 2020 2023 都道府県 奈良県 30 30 29 28 山口県 25 27 27 27 愛媛県 26 28 28 29 沖縄県 28 25 25 25

💬 全体で順位を付けると 1〜12 位がすべて東京都の各年度で埋まり、 13 位にやっと神奈川県(2020 年度)が来る。 順位の最大値も 564 で、 「47 都道府県中の順位」になっていない。 年度ごとに付け直すと、 沖縄県は 2012 年度の 28 位から 2016 年度以降 25 位へ、 愛媛県は 26 位から 29 位へと、 年度内の順位の動きが読める。 年度が混ざった表で順位を付けるときは、 必ず groupby で「何の中での順位か」を決めてから rank() を呼ぶ。

⚠️ 実データで確かめる: 順位が上がっても値は下がっている

順位は他の県との比較でしか決まらないので、 「順位が上がった = 良くなった」とは限らない。 全体が一斉に下がる局面では、 下がり幅が小さい県の順位が上がる。

🎯 このコードでやること:2012 年度と 2023 年度の合計特殊出生率について、 値の変化と順位の変化を県ごとに並べ、 順位が大きく上がった県・下がった県の値の動きを確かめる。

📥 入力例 SSDSE-B-2026.csv の 2012 年度と 2023 年度、 A4103 合計特殊出生率(各年度 47 行) 富山県 1.42 → 1.35 / 岩手県 1.44 → 1.16 / 沖縄県 1.90 → 1.60 …
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df.rename(columns={'SSDSE-B-2026': '年度', 'Prefecture': '都道府県', 'A4103': '出生率'})
df = df[df['年度'].isin([2012, 2023])].copy()
df['順位'] = df.groupby('年度')['出生率'].rank(ascending=False, method='min').astype(int)
w = df.pivot(index='都道府県', columns='年度', values=['出生率', '順位'])
d = pd.DataFrame({'出生率2012': w[('出生率', 2012)], '出生率2023': w[('出生率', 2023)],
                  '順位2012': w[('順位', 2012)].astype(int), '順位2023': w[('順位', 2023)].astype(int)})
d['値の変化'] = (d['出生率2023'] - d['出生率2012']).round(2)
d['順位の上昇'] = d['順位2012'] - d['順位2023']
print('出生率が下がった県:', int((d['値の変化'] < 0).sum()), '/ 47')
print('順位が上がった県:', int((d['順位の上昇'] > 0).sum()), '/ 下がった県:', int((d['順位の上昇'] < 0).sum()))
print(d.sort_values('順位の上昇', ascending=False).head(3).to_string())
print(d.sort_values('順位の上昇').head(3).to_string())
print('全国の中央値:', d['出生率2012'].median(), '→', d['出生率2023'].median())
📤 実行例(実測) 出生率が下がった県: 47 / 47 順位が上がった県: 20 / 下がった県: 18 出生率2012 出生率2023 順位2012 順位2023 値の変化 順位の上昇 都道府県 富山県 1.42 1.35 32 15 -0.07 17 徳島県 1.44 1.36 24 14 -0.08 10 兵庫県 1.40 1.29 35 25 -0.11 10 出生率2012 出生率2023 順位2012 順位2023 値の変化 順位の上昇 都道府県 岩手県 1.44 1.16 24 39 -0.28 -15 静岡県 1.52 1.25 15 29 -0.27 -14 栃木県 1.43 1.19 27 37 -0.24 -10 全国の中央値: 1.44 → 1.3

💬 出生率は 47 県すべてで下がったが、 順位は 20 県で上がり、 18 県で下がった。 富山県は 1.42→1.35 と下がったのに 32 位から 15 位へ 17 位上がり、 岩手県は 1.44→1.16 と大きく下がって 24 位から 39 位へ落ちた。 全国の中央値も 1.44→1.30 に下がっている。 「富山県の出生率ランキングが大幅上昇」という見出しは、 値が下がったことを隠してしまうので、 順位の変化を報告するときは値の変化を必ず並べて書く。

⚠️ 実データで確かめる: 実数の順位と人口あたりの順位はほぼ無関係になる

件数のランキングは、 ほとんどが「人口の多さのランキング」になる。 人口で割ると順位が大きく入れ替わる。

🎯 このコードでやること:2023 年度の一般病院数を、 実数と人口 10 万人あたりの 2 通りで順位付けし、 上位 5 県と 2 つの順位の Kendall τ を比べる。

📥 入力例 SSDSE-B-2026.csv の 2023 年度 47 行 I510120 一般病院数 / A1101 総人口 → 10 万人あたり = 病院数 ÷ 総人口 × 100,000 東京都 588 施設・14,086,000 人 / 高知県 107 施設・666,000 人 …
1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd
from scipy.stats import kendalltau
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
t = df[df['SSDSE-B-2026'] == 2023].set_index('Prefecture')
hosp = t['I510120']                                  # 一般病院数
per = hosp / t['A1101'] * 100_000                    # 人口 10 万人あたり
r = pd.DataFrame({'病院数': hosp, '順位_実数': hosp.rank(ascending=False, method='min').astype(int),
                  '10万人あたり': per.round(2), '順位_人口あたり': per.rank(ascending=False, method='min').astype(int)})
print(r.sort_values('順位_実数').head(5).to_string())
print(r.sort_values('順位_人口あたり').head(5).to_string())
print(r.loc[['神奈川県', '鳥取県']].to_string())
print(f"Kendall τ(実数の順位 vs 人口あたりの順位)= {kendalltau(r['順位_実数'], r['順位_人口あたり']).statistic:.3f}")
print(f"総人口と病院数の Kendall τ = {kendalltau(t['A1101'], hosp).statistic:.3f}")
📤 実行例(実測) 病院数 順位_実数 10万人あたり 順位_人口あたり Prefecture 東京都 588 1 4.17 41 北海道 464 2 9.11 10 大阪府 463 3 5.28 32 福岡県 390 4 7.64 17 兵庫県 312 5 5.81 26 病院数 順位_実数 10万人あたり 順位_人口あたり Prefecture 高知県 107 25 16.07 1 徳島県 90 28 12.95 2 鹿児島県 191 11 12.33 3 大分県 126 17 11.50 4 宮崎県 112 21 10.75 5 病院数 順位_実数 10万人あたり 順位_人口あたり Prefecture 神奈川県 289 7 3.13 47 鳥取県 39 46 7.26 19 Kendall τ(実数の順位 vs 人口あたりの順位)= -0.008 総人口と病院数の Kendall τ = 0.606

💬 実数では東京都 588・北海道 464・大阪府 463 が上位だが、 人口 10 万人あたりでは東京都は 4.17 で 41 位、 大阪府は 32 位に下がり、 高知県 16.07・徳島県 12.95・鹿児島県 12.33 が上位になる。 神奈川県は実数 7 位なのに人口あたりは 47 位。 総人口と病院数の Kendall τ は 0.606 あるのに、 実数の順位と人口あたりの順位の τ は −0.008 で、 2 つのランキングはほぼ無関係。 何を分母にするかで「どの県が上か」が別物になるので、 両方を並べて示す。

🗺 概念マップ — ランキングの周辺地図

           ┌──────────────────────────────────┐
           │     ランキング(順位付け)         │
           └─────────────┬────────────────────┘
                         │
      ┌──────────────────┼──────────────────────┐
      │                  │                       │
  ① 計算法             ② 統計指標              ③ 評価指標 / 応用
   ├ pandas.rank       ├ Spearman ρ           ├ NDCG / MRR / MAP
   ├ numpy.argsort     ├ Kendall τ            ├ Hit Rate@K
   ├ argpartition      ├ Wilcoxon 検定         ├ Learning to Rank
   └ Borda count       └ Mann-Whitney U        ├ NDC Recommendation
                                               └ 検索ランキング

→ ① は 「順位をどう作るか」、 ② は 「2 つのランキングがどれだけ一致するか」、 ③ は 「ランキングの良し悪しをどう測るか」。 この 3 つの軸を切り分けると、 ランキング関連用語のほとんどを整理できる。

ランキング 前提: 順序尺度 並列: Spearman / Kendall 発展: NDCG / MAP 応用: 検索 / 推薦 対比: スコア値 統合: Learning to Rank

🔗 隣接手法への橋渡し

「ランキング」は単独で完結する手法ではなく、 隣接領域と連携することで真価を発揮する。

SSDSE-B-2026 の人口・高齢人口・出生数などを並び替えてランキングを作る際、 同点処理 (rank method) と「上位 5 県の安定性」 (bootstrap で再ランキング) を確認すると結論の堅さが分かる。

🌳 手法選択フロー

「ランキング」を実際の課題に当てはめるとき、 状況別に何を選ぶかを 5 段階で判定する。 ch14 の「上流: ソート/順序尺度 → 並列: 比率尺度/スコア → 下流: スピアマン相関/順位検定」の流れに沿って絞り込む。

  1. 分析の目的は順位か値か?
  2. 同順位 (tie) の扱いは?
    • 平均順位 → Spearman 等の統計手法、 公平性重視
    • 最小順位 (競争順位 1, 1, 3) → スポーツ・コンペティション
    • 密集順位 (1, 1, 2) → 序列のみで間を空けない用途
    • 同順位を許さない (1, 2, 3) → 第 2 基準で機械的に区別
  3. 順位の安定性 (perturbation 耐性) は?
    • 上位 5 件のみ示す → 多少の誤差では順位入れ替わらず頑健
    • 中位帯 (25-35 位) を比較 → ブートストラップで順位 CI を計算
    • 下位 (ワースト) も同様に注目される → 上位と同じ精度で扱う
  4. 母集団全体か上位 N 抽出か?
    • 全体ランキング (47 都道府県全部) → 平均順位で集計
    • Top-K 抽出 (推薦システム) → Recommendation の Hit@K / NDCG@K で評価
    • 多段ランキング (予選 → 決勝) → 各段で異なる重み付け可能
  5. 下流の用途は?
    • レポート・報道 → 棒グラフ + 表で順位明示
    • 順位相関の検証 → Spearman / Kendall's τ
    • 機械学習の特徴量 → 順位を数値として Random Forest 等に投入 (外れ値耐性)

SSDSE-B-2026 の都道府県を人口でランキングすると、 上位 5 (東京・神奈川・大阪・愛知・埼玉) は安定 (Step 3 の頑健ゾーン) だが、 中位 (25-35 位) は数千人差で順位が入れ替わる。 「N 位だから上」とは言いにくい。 Step 4 で全体ランキングを選び Step 5 で Spearman を使えば、 「人口順位」と「高齢人口順位」の関連を信頼性高く議論できる。

🎨 直感をさらに深める — 「順序だけ」を取り出すという発想

ランキングの本質は「対象を 1 本の順序 に並べ、 相対的な前後関係だけ を残す」ことにある。 前段で見た人口順位はその典型だが、 ここでは「なぜ絶対値ではなく順位に注目すると嬉しいのか」を 4 つの角度から捉え直す。

① 順位は「相対量」— 基準集合が変われば意味が変わる

「3 位」という値は それ自体では情報を持たない。 「47 都道府県中 3 位」なのか「10 チーム中 3 位」なのかで意味は全く違う。 順位は必ず 母数(比較対象の集合) とセットで初めて解釈できる相対量である。 だからレポートでは「N=47 中」を必ず添える(本ページ上部「6 か条」参照)。

② 同点(tie)は「順序が付けられない」というシグナル

同点は単なる面倒ごとではなく、 「その 2 対象は この指標では区別できない」という積極的な情報である。 スポーツ表彰は「区別しない」を尊重して min(2 位が 2 人)、 Spearman 相関の計算は理論的整合のため average(2.5 位)を使う——という 使い分けの根拠を、 同点の意味から逆算できる(4 method の比較は上の Python 節を参照)。

③ 順位は「単調変換に不変」— 尺度の呪縛から自由になる

人口を対数・平方根・偏差値のどれに変換しても 順位は 1 つも動かない。 この性質のおかげで、 分布が右に大きく歪んだ人口のような量でも「並び順」は安定して扱える。 Pearson 相関が外れ値や歪みに敏感なのに対し、 順位ベースの Spearman 相関 が頑健なのはこの不変性が源泉である。

④ 「to rank」を学習する — 検索・推薦の中心問題

現代の検索エンジン・推薦システムは、 内部で「クエリやユーザーに対する各アイテムのスコア」を計算し、 それを降順に並べる(=ランキングする)ことで結果を返す。 ここで重要なのは、 最終的にユーザーが見るのは スコアの絶対値ではなく順序 だという点。 だから学習の目的関数も「スコアを正確に当てる」より「正しい順序を作る」ことに寄せていく——これが後述の Learning to Rank の出発点である。

⚠️ 落とし穴をもっと掘る(重要)— 順位が「動く」理由を分解する

ランキングは強力だが、 「順位が動いた」ことの解釈は驚くほど難しい。 ここでは順位の不安定性を生む原因を実データで一つずつ切り分ける。

① 順位は「差の大きさ」を捨てる — 僅差も大差も同じ「1 位差」

これは本ページの背骨となる落とし穴。 人口 8 位 福岡(5,103,000)と 9 位 北海道(5,092,000)の差は わずか 11,000 人(0.2%) なのに、 1 位 東京と 2 位 神奈川の差は 約 486 万人。 どちらも「順位差 1」に圧縮される。 「順位が 1 つ違う」を一律に扱うと、 実質的にほぼ同じものを「上位/下位」と断じる誤りに陥る。

② 指標を変えると順位は激変する — 人口 vs 高齢化率(実測)

SSDSE-B-2026(2023 年・47 県実測)で、 人口総数(A1101)順位と、 高齢化率=高齢人口 A1303 ÷ 人口 A1101 の順位を比べる。 同じ 47 県・同じ年でも、 指標を変えるだけで主役が総入れ替えになる。

県 人口 順位 高齢化率 高齢化率 順位 順位変動 Δ
東京都1 位22.75 %47 位−46
神奈川県2 位25.90 %44 位−42
愛知県4 位25.72 %45 位−41
秋田県39 位39.06 %1 位+38
高知県45 位36.34 %2 位+43
徳島県44 位35.40 %3 位+41
📊 実測の順位相関(人口 vs 高齢化率, 47 県)
Spearman ρ = −0.7105 / Kendall τ = −0.5190
→ 符号が マイナス。 「人口が多い県ほど高齢化率は低い」という 逆向きの単調関係が読み取れる。 出生数のように「人口順位のほぼコピー」(ρ=0.9781)になる指標もあれば、 高齢化率のように ほぼ反転する指標もある。 「どの指標で並べるか」は結論を決定的に左右する恣意的選択であることが、 実データで裏付けられる。

③ 母数依存・標本依存 — 「別の切り取り」で順位は動く

47 県全体での順位と、 「東北 6 県だけ」に絞った順位は当然別物になる(母数が変わる)。 さらに、 対象が母集団からの標本である場合、 別の標本を引けば順位は揺らぐ。 順位は観測データの偶然性を強く受け取るため、 「今回 3 位」が「真に 3 位」とは限らない。 これが次の「順位の不安定性」につながる。

④ 順位変動の過大解釈 — 「上がった/下がった」を語る前に

境界付近では、 元の値がほとんど変わらなくても順位は簡単に入れ替わる。 前述の福岡(8 位)と北海道(9 位)は 1.1 万人差——翌年の推計誤差程度で逆転しうる。 「昨年 9 位→今年 8 位」を「改善」と報じるのは、 多くの場合 ノイズを実体として語る過大解釈である。 対策は次章の「順位の信頼区間」を添えること。

⑤ 複数指標の統合と外れ値 — 「総合順位」は作り方で変わる

住みやすさ・幸福度・大学ランキングのような「総合順位」は複数指標の合成物であり、 重みの置き方で結論が動く(本ページの 🎮 ウィジェットで体感できる)。 加えて、 正規化前に外れ値(東京の人口のような突出値)が混じると min-max 正規化のスケールが歪み、 他県のスコアが潰れる。 統合ランキングは「① 各指標をどう正規化したか ② どんな重みか ③ 外れ値をどう扱ったか」の 3 点を必ず開示すべきである。

🚀 発展 — 順位相関・LTR・評価指標・レーティング・信頼区間・集約

① 順位相関 — Spearman ρ と Kendall τ の使い分け

Spearman ρ は「順位そのものに Pearson 相関を当てた量」、 Kendall τ は「全ペアのうち順序が一致(concordant)した割合 −不一致割合」。 τ は「2 対象を無作為に選んだとき順序が保たれる確率」という 直感的な意味を持ち、 同点や小標本で ρ より安定しやすい。 一般に |τ| は |ρ| よりやや小さく出る(本ページ実測でも人口 vs 出生数は ρ=0.978, τ=0.896)。 順位データの相関は Pearson より外れ値に強く、 ノンパラメトリック解析の中核をなす。

② Learning to Rank — pointwise / pairwise / listwise

「良い順序」を教師あり学習で作るのが LTR。 損失の設計で 3 系統に分かれる:

系統 学習単位・代表手法・特徴
pointwise各アイテムのスコアを個別に回帰/分類。 実装は最も単純だが「順序」を直接最適化しない。
pairwise2 件の前後関係の正誤を学習(RankNet, RankSVM)。 「順序の誤り数」を減らす発想。
listwiseリスト全体の並びと評価指標を直接最適化(LambdaMART, ListNet)。 nDCG を直に効かせられ、 実務の第一選択になりやすい。

③ 評価指標 — nDCG / MAP / MRR の役割分担

nDCG は段階的関連度(rel∈{0,1,2,3}…)を扱え、 上位ほど重い対数割引で「上位に良いものを集めたか」を測る標準指標(本ページで手計算 nDCG≈0.972 を実演)。 MAP(Mean Average Precision)は正解が上位に固まるほど高く、 二値関連度の情報検索で定番。 MRR(Mean Reciprocal Rank)は「最初の正解の順位の逆数」で、 FAQ・Q&A のように 1 件当たれば十分なタスク向き。 タスクの目的(1 件で十分か/網羅性か/段階的関連度か)で選ぶ。

④ 対戦からのランキング — Elo と Bradley-Terry

絶対スコアが無く「勝った/負けた」しか無い場面(チェス・スポーツ・A/B 対戦)では、 対戦結果から潜在能力を推定して順位化する。 Elo は期待勝率 $E_A = 1/(1+10^{(R_B-R_A)/400})$ を用い、 勝てば $R_A \leftarrow R_A + K(S_A - E_A)$ で更新する動的レーティング。 その 確率モデル版が Bradley-Terry モデルで、 「i が j に勝つ確率 = $\pi_i/(\pi_i+\pi_j)$」と置き、 対戦データから最尤で強さ $\pi$ を推定する。 順位を「固定値」でなく「確率的に推定されるもの」として扱う発想である。

⑤ 順位の信頼区間 — 「3 位(CI 2–7 位)」と書く

順位の不確かさは ブートストラップで定量化できる。 データを N 回リサンプリングして毎回ランキングを作り、 各対象の順位の 2.5%〜97.5% パーセンタイルを取れば「3 位(95% 区間 2–7 位)」のように書ける。 値が僅差で密集する中位帯ほど区間は広く、 値が離れた上位・下位は区間が狭い。 教育評価や政策指標で順位を公表する際、 区間の併記は説明責任の観点で重要である。

⑥ ボルダ集約 — 複数ランキングを 1 本に統合する(実測)

複数の順位を統合する古典的な投票方式が ボルダ集約(Borda count)。 各指標で「1 位に n−1 点、 最下位に 0 点」を配り、 点数の総和で総合順位を決める。 SSDSE-B-2026(2023 年・47 県実測)で 「出生率が高い」「高齢化率が低い」「人口が多い」の 3 指標を等重みでボルダ集約した総合ランキング(あくまで手法デモ用の指標選択):

総合 県 ボルダ点 内訳(出生率/低高齢化/人口)
1愛知県12942 / 44 / 43
2東京都12735 / 46 / 46
3大阪府12339 / 40 / 44
4福岡県12245 / 38 / 39
6沖縄県11346 / 45 / 22
46秋田県 = 高知県(同点)8— / — / —
→ 総合 1 位は 愛知県。 出生率単独 1 位の沖縄でも、 人口単独 1 位の東京でもない。 集約すると「どの単一指標の 1 位とも違う勝者」が生まれ得る。 さらに最下位は 秋田県と高知県が同点 46 位——集約は新たな同点(tie)を生む。 「総合ランキング」を見るときは、 指標選択・重み・同点処理という 3 つの恣意性が結論に効いていることを忘れてはならない。