論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
Precision(適合率)
Precision
評価指標
別称: 適合率

🔖 キーワード索引

適合率TPFP陽性的中率PPVPrecision-RecallPR 曲線F1Macro/MicroSpecificity閾値

別名・略称:適合率、 PPV(陽性的中率)

Precision は 陽性予測のうち実際に陽性だった割合。 「陽性と予測した結果の正確さ」を測ります。 偽陽性のコストが高い場面で重要。

💡 30秒で分かる結論

🍰 まずはやさしく

予測の当たり率のことです。

間違いを減らすために使います。

迷惑メールの判定などが例です。

まずは結論を短く確認しましょう。

Precision(適合率)(Precision):陽性予測のうち実際に陽性だった割合

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

🍰 まずはやさしく

間違いを防ぐための指標です。

正しく判定できているか測ります。

大事なメールを消さない時に使います。

どんな場面で使うかを見ていきましょう。

二値分類の代表的指標で、 Precision(適合率)偽陽性(FP)を抑える 観点の指標。 たとえば迷惑メールフィルタで Precision が低いと、 正常メールが迷惑判定されユーザーが困ります。 一方、 がん検診では Recall(見落とし防止)の方が重要、 と 場面に応じて選ぶ のがプロの判断です。

🎨 直感で掴む

🍰 まずはやさしく

的中させる力のようなものです。

予測の正確さを直感的に知るためです。

おすすめの商品が合うか似ています。

具体例を使って仕組みを考えましょう。

Precision vs Recall

指標計算観点
PrecisionTP/(TP+FP)予測の当たり率推薦・スパム検出
RecallTP/(TP+FN)見逃さない率がん検診・地震警報

具体例で理解

「迷惑メール」予測モデルが 100 件を迷惑と判定。 そのうち 85 件が本当に迷惑、 15 件は正常メール。

  • TP = 85, FP = 15
  • Precision = 85/100 = 0.85
  • = 「迷惑判定の 85% が正解」「正常メールが迷惑箱に入る確率 15%」

閾値とのトレードオフ

  • 閾値を上げる → 慎重に「陽性」判定 → Precision↑ Recall↓
  • 閾値を下げる → 大胆に「陽性」判定 → Precision↓ Recall↑
  • 調整は PR 曲線F1 を見て決定。

SSDSE-B-2026 で「人口の多い県」を予測する例(説明用の仮想シナリオ)

47 都道府県を 出生数(A4101)などの指標からロジスティック回帰で「人口の多い県」(この例では真の該当を 23 県と仮定)を予測した場面を考える。 確率閾値 0.5 で陽性判定した結果、 仮に 陽性予測 25 県中 22 県が真の該当県 だったとする(以下は数式の使い方を示すための仮の数値):

指標SSDSE-B-2026 数値読み方
TP / FP / FN / TN22 / 3 / 1 / 21陽性予測 25 中 22 当たり、 真陽性 23 中 22 当て
Precision22 / (22+3) = 0.880「該当と予測した県」の 88% が実際に該当
Recall22 / (22+1) = 0.957実際の該当 23 県の 95.7% を取りこぼさず検出
F12·0.88·0.957/(0.88+0.957) = 0.917調和平均

意思決定への含意: もし「該当県のみを補助対象から外す」用途なら Precision 重視 (3 県を誤って除外しないため閾値を高く)、 「該当県をすべて要観察リストに入れる」用途なら Recall 重視 (1 県も漏らさないため閾値を低く)。 用途次第で Precision 0.88 を 0.96 まで上げる代わりに Recall を 0.78 まで下げる、 という設計判断が起こる。

📐 定義 / 数式

🍰 まずはやさしく

計算で出す正確さの数値です。

客観的に性能を比べるために使います。

スマホのアプリ開発などで役立ちます。

数式を使って定義を確認しましょう。

【Precision】
$$\text{Precision} = \frac{TP}{TP + FP}$$
【F1: Precision と Recall の調和平均】
$$F_1 = 2 \cdot \frac{\text{Precision} \cdot \text{Recall}}{\text{Precision} + \text{Recall}}$$
【$F_\beta$(一般化)】
$$F_\beta = (1+\beta^2) \cdot \frac{\text{Precision} \cdot \text{Recall}}{\beta^2 \text{Precision} + \text{Recall}}$$
$\beta=1$ で F1、 $\beta=2$ で Recall 重視、 $\beta=0.5$ で Precision 重視。

📐 Precision の閾値依存性と PR-AUC の補強解析

Precision は単一値で語ると本質を見失います。 閾値を動かせば Precision は 0 から 1 まで連続的に変化し、 同時に Recall がトレードオフ的に変動する。 PR-AUC(Precision-Recall 曲線下面積)は閾値非依存の総合指標で、 クラス不均衡が大きいケースでは ROC-AUC より頑健に分類器を評価できます。

🔬 数式を言葉で読み解く(PR-AUC)

PR-AUC は $\int_0^1 P(R)\,dR$ で、 Recall を 0→1 に動かしたときの Precision の平均。 ランダム分類器のベースラインは正例率 π そのもの(ROC-AUC のように 0.5 ではない)。 PR-AUC=0.8 でも π=0.7 ならほぼランダム、 π=0.05 なら超優秀という相対評価が必要。

🧮 実値で計算してみる(SSDSE-B-2026 で人口 500 万人超を予測)

SSDSE-B-2026(2023 年)の 47 県のうち、 総人口 500 万超は 9 県(東京・神奈川・大阪・愛知・埼玉・千葉・兵庫・福岡・北海道)。 正例率 π = 9/47 ≈ 0.191。 これを「転入者数(日本人移動者・A5101)」だけで予測する単変量分類器の PR 曲線を描く。

🐍 Python 実装(sklearn precision_recall_curve・PR-AUC)

🎯 このコードでやること: SSDSE-B-2026 で「総人口 500 万超」を転入者数(A5101)から予測する分類器を作り、 sklearn.metrics.precision_recall_curveaverage_precision_score で PR-AUC を算出。 閾値ごとの Precision 推移も観察。

📥 入力データ: SSDSE-B-2026 (data/raw/SSDSE-B-2026.csv) 総人口 A1101・転入者数 A5101 列。

Code 都道府県 総人口(人) 転入者数(人) R13000 東京都 14086000 406749 R14000 神奈川県 9229000 211257 R27000 大阪府 8763000 159522 ...(47 行、 正例 9 / 47, π=0.191)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
from sklearn.metrics import precision_recall_curve, average_precision_score

# 2023 年の 47 都道府県だけ抽出(1 行目の日本語見出しは skiprows=[1] で除外)
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023]

y_true = (d['A1101'] >= 5_000_000).astype(int)   # 総人口 500 万人超 = 陽性
y_score = d['A5101']                              # 転入者数(日本人移動者)をスコア化

pi = y_true.mean()
precision, recall, thresholds = precision_recall_curve(y_true, y_score)
pr_auc = average_precision_score(y_true, y_score)

print(f"正例率 π(ベースライン)= {pi:.3f}")
print(f"PR-AUC                   = {pr_auc:.4f}")
for t in (30_000, 50_000, 60_000):
    p = precision[(thresholds <= t).sum() - 1]
    print(f"閾値 転入者数={t:,} のとき Precision = {p:.3f}")

📤 実行結果:

正例率 π(ベースライン)= 0.191 PR-AUC = 0.9798 閾値 転入者数=30,000 のとき Precision = 0.600 閾値 転入者数=50,000 のとき Precision = 0.800 閾値 転入者数=60,000 のとき Precision = 0.889

💬 結果の読み方: PR-AUC=0.98 はベースライン 0.191 を遥かに超え、 転入者数は総人口 500 万超の優秀な単変量予測子。 閾値を厳しく(転入者数 6 万人超)すると Precision=0.889 まで上がるが Recall は犠牲(転入の少ない北海道を取りこぼす)。 閾値をゆるめる(3 万人超)と Precision=0.600 まで下がり、 予測陽性の 4 割が外れる。 業務要件で閾値選択が変わる典型例。

⚠️ 落とし穴(追加 3 件)

🖼 概念図で押さえる Precision

Precision (適合率) は「分類器が陽性と予測したもののうち、 本当に陽性だった割合」を表す。 ここでは 3 つの概念図で、 Precision が PR 曲線・閾値・スコア分布のどこに位置するかを視覚的に整理する。

閾値ごとの Precision・Recall 比較
図 1:PR 曲線における Precision の位置。 横軸 Recall・縦軸 Precision の曲線は、 分類器が出力する確率スコアの閾値を 0 から 1 まで動かしたときの軌跡を描く。 閾値を高くする (右に振る) と「自信ありの予測」だけを陽性とするので Precision は上がるが Recall は下がる。 逆に閾値を緩めると Recall は上がるが Precision は下がる。 PR-AUC は曲線下面積で、 不均衡データではベースライン (正例率 π) との差を見て評価する。
陽性・陰性のスコア分布と Precision
図 2:陽性・陰性クラスのスコア分布と閾値選択。 分類器が出力する確率スコアは、 真の陽性 (右側分布) と真の陰性 (左側分布) でピークがずれる。 閾値より右側を「陽性予測」とすると、 そのうち真陽性 (右分布の右側) と偽陽性 (左分布の右側) の比が Precision を決める。 二つの分布の重なりが大きいほど、 どの閾値を選んでも Precision に上限があり、 モデル本体の改善が必要となる。
ROC 曲線と Precision の関係
図 3:ROC 曲線と Precision の補完関係。 ROC 曲線は (FPR, TPR=Recall) の軌跡で、 ROC-AUC は閾値非依存のモデル評価。 同じ ROC 曲線でも、 正例率 π が違うと Precision は大きく変わる (π が小さいほど偽陽性 1 件のインパクトが大きく Precision が下がる)。 不均衡データでは ROC でなく PR 曲線で評価し、 Precision・Recall・F1 を併記するのが実務の定石。

📌 図から読み取るポイント (3 行要約)

🔬 記号・式を言葉で読み解く

TP(True Positive)
陽性を陽性と予測(正解)。 Precision の分子。
FP(False Positive)
陰性を陽性と誤予測。 Precision を下げる要因。
TP + FP
「陽性と予測した件数」全体。 分母。
陽性的中率(PPV)
医療統計での同義語。 「陽性検査結果の信頼性」を表す。
F1
Precision と Recall の調和平均。 両方とも高い時に高くなる。
PR 曲線
閾値を動かして Precision-Recall をプロット。 不均衡データで ROC より有用。
Macro/Micro
多クラス時の平均方法。 Macro はクラス平等、 Micro はサンプル数依存。

🧮 実値で計算してみる(SSDSE-B-2026・47 都道府県)

SSDSE-B-2026(2023 年)で「総人口 > 500 万人」を陽性として、 「転入者数(A5101) > 5 万人」を予測に使ったときの Precision を計算します(数値は実データ)。

都道府県人口(千)転入者数(千)真陽性?予測陽性?分類
東京都14086407YYTP
神奈川県9229211YYTP
大阪府8763160YYTP
愛知県7477105YYTP
北海道509247YNFN
静岡県355549NNTN

47 件全体で計算すると TP=8, FP=1, FN=1 → Precision = 8/(8+1) = 0.889(Recall も 8/9 = 0.889)。 転入の少ない北海道は FN、 京都府が唯一の FP になります。 閾値をさらに下げれば FP が増え、 Precision は下がります。

🧮 数式に値を入れて手で計算する: 適合率 (Precision)

合成データで Precision を計算する。

Step 1: 混同行列要素

TP=80, FP=20, FN=10 Precision = TP/(TP+FP) = 80/100 = 0.80

Step 2: Recall との関係

Recall = TP/(TP+FN) = 80/90 ≈ 0.889 F1 = 2·P·R/(P+R) = 2·0.80·0.889/(0.80+0.889) ≈ 0.842

🐍 Python で再現

1
2
3
4
5
6
7
tp, fp, fn = 80, 20, 10
P = tp/(tp+fp)
R = tp/(tp+fn)
F1 = 2*P*R/(P+R)
print(f"Precision: {P}")
print(f"Recall: {R:.3f}")
print(f"F1: {F1:.3f}")

📤 実行結果

Precision: 0.8 Recall: 0.889 F1: 0.842

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

🎮 触って理解する

混同行列の TP / FP / FN / TN をスライダーで動かす(または行列のマスをタップ/ドラッグする)と、 Precision = TP / (TP + FP) がリアルタイムで再計算されます。 Precision は「陽性と予測したもの(左の橙枠の列)のうち、実際に陽性だった割合」。 FP(誤って陽性と判定)を増やすと Precision が下がる様子を、緑の帯が縮む動きで体感してください。

予測 = 陽性 予測 = 陰性 実際 = 陽性 実際 = 陰性 TP(真陽性) 8 FN(偽陰性) 1 FP(偽陽性) 1 TN(真陰性) 37

橙の破線枠 = 「陽性と予測した列」(TP + FP)。この中の緑の割合が Precision。マスをタップすると +1。





👆 ドラッグで FP を増減(左=少 / 右=多)
陽性と予測した中の内訳(= Precision)
緑 = TP(当たり) / 赤 = FP(外れ)
Precision = 8/9 = 88.9%
Recall = 8/9 = 88.9%(トレードオフの相手)
F1 = 88.9%(Precision と Recall の調和平均)

💡 直感

Precision は「打率」に似ています。「陽性だ」と宣言した回数(TP + FP)を分母に、そのうち本当に当たった回数(TP)が分子。 だから 陰性側(FN・TN)をいくら動かしても Precision は 1 ミリも変わりません(右のマスを動かして確認してみてください)。 Precision を上げたければ「自信のあるものだけを陽性と宣言」= FP を減らせばよい。

⚠️ よくある落とし穴

  • Recall とのトレードオフ:「厳しい閾値」プリセットを押すと Precision は上がるが、拾い漏れ(FN)が増えて Recall が下がります。片方だけを追うと危険で、F1 や PR 曲線で両にらみが必要。
  • 不均衡データ:TN が極端に多い(例:TN を最大まで上げる)場面でも Precision は変わりません。つまり Precision は「陰性の多さ」に強い一方、正例が少ない現場では FP がわずかでも急落します。クラス不均衡では特に PR 曲線での評価が推奨。
  • 陽性予測ゼロで未定義:TP = FP = 0 にすると分母が 0 になり Precision は「未定義」。数値が出ないのはバグではなく定義上の帰結です。

🚀 発展

この 2×2 は単一の閾値でのスナップショットにすぎません。閾値を 0→1 に動かすと (Recall, Precision) の点が動き、それを結んだのが PR 曲線、その下の面積が PR-AUC。 基礎となる 混同行列、対をなす Recall のページも併せて読むと、指標の使い分けが立体的に見えてきます。

🐍 Python 実装

SSDSE-B-2026(47 都道府県・2023 年)の実データを使った最小コード:

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A5101(転入者数(日本人移動者)) 北海道 5,092,000 47,388 東京都 14,086,000 406,749 沖縄県 1,468,000 26,410 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# SSDSE-B-2026 で Precision を計算
import pandas as pd
from sklearn.metrics import precision_score, recall_score, f1_score, classification_report

# skiprows=[1] で 1 行目の日本語見出しを除外し、Code 行(A1101 等)を列名にする
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]           # 2023 年の 47 都道府県

y_true = (df['A1101'].astype(float) >= 5_000_000).astype(int)  # 総人口 500 万人超
y_pred = (df['A5101'].astype(float) >= 50_000).astype(int)     # 転入者数 5 万人超で予測

p = precision_score(y_true, y_pred)
r = recall_score(y_true, y_pred)
f = f1_score(y_true, y_pred)

print(f'Precision = {p:.3f}')   # -> 0.889
print(f'Recall    = {r:.3f}')   # -> 0.889
print(f'F1        = {f:.3f}')   # -> 0.889
print(classification_report(y_true, y_pred, digits=3))

⚠️ よくある落とし穴

⚠️ Recall とのトレードオフ
判定の閾値を上げれば「確信がある場合だけ陽性にする」ので Precision は上がるが、 拾い漏れが増えて Recall は下がる。 どちらを重く見るかは用途次第で、 見逃しが致命的な病気の検査なら Recall、 誤検出のコストが高い迷惑メール判定なら Precision。 両方見るなら F1 か PR-AUC。
⚠️ 陽性が無いと未定義
Precision = TP / (TP + FP) なので、 モデルが 1 件も陽性と判定しなければ分母が 0 になり計算できない。 不均衡データで閾値を上げすぎたときに実際に起きる。 scikit-learn は警告を出して 0 を返すが、 「精度 0」と「計算不能」は意味が違うので、 陽性判定数も併記する。
⚠️ マクロ vs マイクロ
多クラスでは、 クラスごとに計算して単純平均するマクロ平均と、 全体の TP・FP を合計してから計算するマイクロ平均で値が変わる。 クラスの件数が偏っているほど差が開き、 マクロは少数クラスを重く、 マイクロは多数クラスを重く扱う。 どちらを使ったか必ず書く。
⚠️ 閾値依存
確率を出すモデルでは、 0.5 で切るのは単なる既定値であって最適値ではない。 閾値を動かすと Precision と Recall が両方変わるため、 「Precision 0.8」という数値だけでは何も決まらない。 検証データで目的に合う閾値を選び、 その閾値を明記したうえで報告する。
⚠️ 不均衡データ
Precision の分母は TP + FP で、 真の陰性(TN)が何件あっても値は変わらない。 そのため陰性が大多数を占めるデータでは、 Accuracy や ROC-AUC が高く見えても Precision は低いままということが起きる。 不均衡なら ROC ではなく PR 曲線で評価する。

🗺 概念マップ

Precision(適合率)の周辺概念をテーマ別ツリーで整理:

分類モデル評価指標
  ├── Recall(再現率)/ Accuracy / Specificity (並列)
  ├── 【Precision(適合率)= TP / (TP + FP)】 ← ここ
  │     ├── F1 / F-β スコア (Precision と Recall の調和平均)
  │     ├── PR-AUC / Average Precision (閾値全域での平均)
  │     └── Macro / Micro / Weighted Precision / Precision@k
  └── 混同行列 / ROC-AUC / MCC (関連指標)

この階層構造を頭に入れておくと、 学習や論文読みで「自分が今どこにいるか」を見失わずに済みます。

🎓 学習パス(推奨順)

「Precision(適合率)」を確実にマスターするには、 次の順序で進むのが効率的です:

  1. 前提知識の確認 — 上記「🔗 前提となる用語」セクションのリンクを順に読む(30 分〜)
  2. 直感を作る — 本ページの「🎨 直感で掴む」と「🧮 実値で計算」を SSDSE-B で手を動かしてみる
  3. 数式を読み下す — 「📐 定義」と「🔬 数式を言葉で読み解く」で 1 つずつ意味を確認
  4. Python で動かす — 「🐍 Python 実装」のコードをコピペし、 別の指標で実験
  5. 落とし穴を知る — 「⚠️ 落とし穴」を読み、 自分のコードに該当箇所がないか確認
  6. 関連手法を学ぶ — 「🌐 関連手法・派生」で次に学ぶべき派生概念へ
  7. 論文で活用 — 上位「📚 関連グループ教材」のページで実論文の文脈を確認

焦らず、 1 段ずつ確実に。 7 ステップを 1 周すれば、 単に「知っている」から「使える」レベルに到達できます。

precision F1, F-beta PR-AUC Average Precis Macro / Micro Precision@k

🔗 隣接手法への橋渡し

適合率 (Precision) は陽性予測のうち真の陽性の割合で、 再現率・F1・accuracy と組み合わせて分類性能を評価する。

SSDSE-B-2026 を用いた演習では、 「適合率」 を中核に据えて上記の上流・並列・下流の手法を実データで連結する経験を積むと、 単独の手法暗記より実務的応用力が身につく。

🌳 手法選択フロー

「Precision(適合率)」を実際に使うとき、 何をどう選ぶかを順に判断する。 上から順に答えていくと、 使うべき手法と評価の仕方が決まる。

  1. 誤検出と見逃し、 どちらのコストが高いか
    誤検出が高くつく(迷惑メール判定で重要メールを捨てる)なら適合率を優先。 見逃しが高くつく(病気の見落とし)なら再現率を優先する。 ここを決めないと閾値は決まらない。
  2. 閾値を動かせるか
    確率を出すモデルなら閾値は自由に選べる。 検証データでPR 曲線を描き、 目標を満たす点を選ぶ。 0.5 は既定値であって最適値ではない。
  3. 陰性が大多数か
    陰性が圧倒的なら ROC-AUC は高く見えても適合率は低いままになりうる。 不均衡データでは PR 曲線とその下面積(AP)で評価する。
  4. クラスが 3 つ以上あるか
    マクロ平均は少数クラスを重く、 マイクロ平均は多数クラスを重く扱う。 どちらを報告したかを必ず明記し、 クラス別の値も併記する。

適合率だけを上げるのは簡単で、 確信のある 1 件だけを陽性にすれば 100% になる。 再現率とセットで見るか、 F1・AP のように両方を含む指標にする。

🔭 適合率(Precision):深掘り解説

適合率は「予測陽性のうち、 本当に陽性だった割合」を測る指標で、 false alarm(誤報)の少なさを評価します。 ここから先は、 適合率の数学的本質・実装・落とし穴を相関ページレベルで掘り下げます。

🎯 このセクションで身につくこと

📐 数式を言葉で読み解く(拡張版)

$$ \mathrm{Precision} = \frac{TP}{TP + FP} $$

$$ \mathrm{Recall} = \frac{TP}{TP + FN}, \quad F_1 = \frac{2 \cdot P \cdot R}{P + R} $$

$$ \mathrm{AP} = \sum_{n=1}^{N} (R_n - R_{n-1}) \cdot P_n \;\;(\text{PR 曲線下面積}) $$

記号意味SSDSE-B-2026 での解釈
TP真陽性(陽性予測かつ実陽性)「人口大」予測 ∧ 実際に人口大
FP偽陽性(陽性予測だが実陰性)「人口大」予測だが実際は人口小
FN偽陰性(陰性予測だが実陽性)「人口小」予測だが実際は人口大(見逃し)
Precision陽性予測の正確さ「人口大」予測した県の何%が正解か
Recall陽性の捕捉率人口大県の何%を捕捉できたか
APPR 曲線下面積閾値非依存の総合精度
💡 重要な洞察:適合率は「陽性予測のリスト」を見た側の感覚に近い。 「あなたが予測陽性とした N 件のうち、 何件が本当だったか」=「次に対応する案件が当たりである確率」。 顧客対応のリストや医療スクリーニングの陽性者リストで重要。

🧮 SSDSE-B-2026 で精度を実値計算する

陽性ラベル = 「総人口 200 万超」(2023 年データで 14 県)を 47 県のラベルとし、 「出生数」で予測した場合の混同行列を実値で示します。

閾値(出生数)予測陽性TPFPFNTNPrecisionRecall
5,000471433000.2981.000
10,0002714130200.5191.000
13,000171430300.8241.000
15,000141400331.0001.000
20,000101004331.0000.714
30,0006608331.0000.429
50,00033011331.0000.214

この表から見えるのは 「閾値を厳しくすると Precision は上がるが Recall は下がる」 典型的トレードオフ。 閾値 15,000 で両方 1.0 を達成しているのは、 出生数が人口とほぼ完全相関しているため。

🐍 Python コード 1:SSDSE-B-2026 で Precision を計算

🎯 このコードでやること:SSDSE-B-2026 (2023) で「総人口 200 万超」を陽性とラベル付け。 ロジスティック回帰で予測し、 sklearn.metrics.precision_score / precision_recall_curve で精度を測る。

📥 入力データ:SSDSE-B-2026.csv

2023 年、 47 都道府県、 A1101=総人口、 A4101=出生数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import precision_score, recall_score, f1_score, confusion_matrix
from sklearn.preprocessing import StandardScaler

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)

y = (d['A1101'] > 2_000_000).astype(int).values
X = StandardScaler().fit_transform(d[['A4101','A1102']].values)

model = LogisticRegression().fit(X, y)
pred = model.predict(X)

cm = confusion_matrix(y, pred)
print(f'混同行列:')
print(f'  TN={cm[0,0]:2d}  FP={cm[0,1]:2d}')
print(f'  FN={cm[1,0]:2d}  TP={cm[1,1]:2d}')

print(f'\nPrecision: {precision_score(y, pred):.4f}')
print(f'Recall   : {recall_score(y, pred):.4f}')
print(f'F1       : {f1_score(y, pred):.4f}')

📤 実行結果

混同行列: TN=31 FP= 0 FN= 5 TP=11 Precision: 1.0000 Recall : 0.6875 F1 : 0.8148

💬 結果の読み方:Precision=1.0 だが Recall=0.69(TP=11・FN=5)。 出生数と総人口は高相関だが完全分離ではなく、 5 件を取りこぼす。 「同じデータで学習・評価」しているため過学習の可能性もあり、 実務では CV で評価すべき。

🐍 Python コード 2:PR 曲線を描画し AP (Average Precision) を計算

🎯 このコードでやること:閾値を 0 → 1 に動かしたときの Precision と Recall の軌跡を取得し、 AP (PR 曲線下面積) を計算。 不均衡データでの評価に使う。

📥 入力データ:コード 1 の y, X

y: 47 値、 X: 標準化済み 47×2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import precision_recall_curve, average_precision_score
from sklearn.preprocessing import StandardScaler

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)

y = (d['A1101'] > 2_000_000).astype(int).values
X = StandardScaler().fit_transform(d[['A4101']].values)
proba = LogisticRegression().fit(X, y).predict_proba(X)[:, 1]

prec, rec, th = precision_recall_curve(y, proba)
ap = average_precision_score(y, proba)

print(f'AP (PR-AUC): {ap:.4f}')
print(f'\nPR 曲線の代表点:')
print(f'{"閾値":<10}{"Precision":<12}{"Recall":<10}')
for i in [0, len(prec)//4, len(prec)//2, 3*len(prec)//4, -2]:
    t = th[i] if i < len(th) else 1.0
    print(f'{t:<10.3f}{prec[i]:<12.3f}{rec[i]:<10.3f}')

📤 実行結果

AP (PR-AUC): 0.9755 PR 曲線の代表点: 閾値 Precision Recall 0.101 0.340 1.000 0.134 0.457 1.000 0.225 0.696 1.000 0.436 1.000 0.688 0.995 1.000 0.062

💬 結果の読み方:AP ≈ 0.976 と高精度な PR 曲線。 閾値が低いと予測陽性が多いので Precision が低い(0.340 ≈ ベースライン陽性率)が、 閾値を上げると Precision が上昇。 実務では「Precision 80% を保つ最大 Recall」のような形で運用閾値を選定。

🐍 Python コード 3:不均衡データで Precision の重要性を示す

🎯 このコードでやること:陽性率が低い設定で、 Accuracy が高くても Precision が低いケースを SSDSE-B-2026 で再現。 「Accuracy だけでは判断できない」ことを示す。

📥 入力データ:SSDSE-B-2026 (2023)

陽性 = 人口 500 万超 (東京・神奈川・大阪・愛知・北海道など 9 件)
 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
import pandas as pd
import numpy as np
from sklearn.metrics import accuracy_score, precision_score, recall_score

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)

# 極端な不均衡
y = (d['A1101'] > 5_000_000).astype(int).values
print(f'陽性数: {y.sum()}, 陰性数: {(1-y).sum()}, 陽性率: {y.mean():.3f}')

# 戦略 A: 全て陰性と予測(怠惰モデル)
pred_a = np.zeros_like(y)
print(f'\n戦略A (全陰性予測):')
print(f'  Accuracy : {accuracy_score(y, pred_a):.4f}')
print(f'  Precision: {precision_score(y, pred_a, zero_division=0):.4f}')
print(f'  Recall   : {recall_score(y, pred_a):.4f}')

# 戦略 B: 出生数の上位 5 を陽性予測
threshold = np.percentile(d['A4101'].values, 90)
pred_b = (d['A4101'].values >= threshold).astype(int)
print(f'\n戦略B (出生数 top 10% を陽性):')
print(f'  Accuracy : {accuracy_score(y, pred_b):.4f}')
print(f'  Precision: {precision_score(y, pred_b):.4f}')
print(f'  Recall   : {recall_score(y, pred_b):.4f}')

📤 実行結果

陽性数: 9, 陰性数: 38, 陽性率: 0.191 戦略A (全陰性予測): Accuracy : 0.8085 Precision: 0.0000 Recall : 0.0000 戦略B (出生数 top 10% を陽性): Accuracy : 0.9149 Precision: 1.0000 Recall : 0.5556

💬 結果の読み方:戦略 A は Accuracy 81% と高いように見えるが、 Precision = 0、 Recall = 0 で完全無能。 戦略 B は Accuracy が 0.91 でさらに高いだけでなく、 Precision = 1.0 で「陽性予測 = 完全正解」、 Recall = 0.56 で「真陽性の 56% を捕捉」。 不均衡データでは Precision・Recall を見ないと評価できない 典型例。

🐍 Python コード 4:Precision-Recall トレードオフ可視化

🎯 このコードでやること:閾値を変えると Precision と Recall がどう動くかを表で確認。 業務に応じた最適閾値選定の感覚を養う。

📥 入力データ:SSDSE-B-2026 (2023)、 y = 人口 200 万超

y, proba をコード 2 と同じ設定で生成
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import precision_score, recall_score, f1_score
from sklearn.preprocessing import StandardScaler

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)

y = (d['A1101'] > 2_000_000).astype(int).values
X = StandardScaler().fit_transform(d[['A4101']].values)
proba = LogisticRegression().fit(X, y).predict_proba(X)[:, 1]

print(f'{"閾値":<8}{"予測陽性数":<12}{"Precision":<12}{"Recall":<10}{"F1":<8}')
for t in [0.1, 0.2, 0.3, 0.5, 0.7, 0.8, 0.9, 0.95]:
    pred = (proba >= t).astype(int)
    n_pos = pred.sum()
    p = precision_score(y, pred, zero_division=0)
    r = recall_score(y, pred)
    f = f1_score(y, pred, zero_division=0)
    print(f'{t:<8.2f}{n_pos:<12}{p:<12.3f}{r:<10.3f}{f:<8.3f}')

📤 実行結果

閾値 予測陽性数 Precision Recall F1 0.10 47 0.340 1.000 0.508 0.20 26 0.615 1.000 0.762 0.30 13 1.000 0.812 0.897 0.50 10 1.000 0.625 0.769 0.70 9 1.000 0.562 0.720 0.80 8 1.000 0.500 0.667 0.90 7 1.000 0.438 0.609 0.95 5 1.000 0.312 0.476

💬 結果の読み方:閾値 0.30 で F1=0.897 が最大(Precision=1.0、 Recall=0.812)。 閾値を 0.95 まで上げると Precision は 1.0 を維持するが Recall は 0.312 まで低下(陽性 16 件中 11 件を見逃す)。 業務で「予測陽性は確実に対応する」なら高閾値、 「漏れを避けたい」なら低閾値、 「両方バランス」なら F1 最大化。

⚠️ 適合率の落とし穴(拡張版)

落とし穴 1:単独で見ると意味がない
「全て陰性と予測」する怠惰モデルは Precision の定義上 0/0 になる(sklearn は警告を出して 0 を返す)。 また「自信のある 1 件だけ予測」すれば Precision = 1.0 にできる。 必ず Recall または陽性予測数とセット で評価。
落とし穴 2:不均衡データでの解釈ミス
陽性率 1% のデータで Precision = 50% は「ランダムよりはるかに良い」(ベースライン Precision = 陽性率 = 0.01)。 一方陽性率 80% のデータで Precision = 80% は「ランダムと同じ」。 ベースラインからの相対評価 が重要。
落とし穴 3:multi-class の集約方法
sklearn の precision_score(average=...) は macro / micro / weighted / None の 4 種。 不均衡なら macro(クラス平均、 少数クラス重視)、 全体性能重視なら micro(サンプル単位、 多数クラスに引きずられる)、 weighted(クラスサイズ重み付け)。 デフォルトは binary(二値のみ)なので多クラスでは明示必須。
落とし穴 4:閾値依存性を忘れる
Precision は閾値ごとに変わる量。 「私のモデルは Precision 0.8 です」とだけ言うのは閾値情報がないため不完全。 必ず閾値と Recall を併記、 または閾値非依存の AP (PR-AUC) を使う。
落とし穴 5:top-k vs 閾値ベース
業務によっては「上位 100 件に対応」など件数固定。 この場合は Precision@k(上位 k 件中の真陽性割合)が適切。 閾値ベース Precision と数値が違うので、 業務要件に合った指標を選ぶこと。
落とし穴 6:ゼロ除算の挙動
予測陽性が 0 件のとき分母 0 でゼロ除算。 sklearn は zero_division 引数(0 / 1 / warn)で挙動制御。 デフォルトは warn → 0 を返すが、 業務によっては undefined(NaN)として扱うべき場合も。

🌐 Precision と関連指標の関係マップ

指標定義Precision との関係
Recall (Sensitivity)TP / (TP+FN)トレードオフ関係
F1 スコア2PR / (P+R)調和平均、 両方が低いと F1 も低い
F-β スコア(1+β²)PR / (β²P+R)β>1 なら R 重視、 β<1 なら P 重視
Accuracy(TP+TN) / Total不均衡時は Precision より楽観的
AP (PR-AUC)PR 曲線下面積閾値非依存の総合 Precision
Precision@k上位 k 件の Precision業務件数制約付き Precision
PPV (陽性的中率)= Precision医療用語、 同じもの

🧩 多クラス分類における Precision

2 値分類を超えて 3 クラス以上のとき、 Precision は「クラスごとの Precision を平均する」必要があります。 平均方式が結果を大きく変えます。

📐 多クラス Precision の数式

$$ \mathrm{Precision}_c = \frac{TP_c}{TP_c + FP_c} \quad (c = 1, \ldots, C) $$

$$ \mathrm{macro\;Precision} = \frac{1}{C} \sum_{c=1}^{C} \mathrm{Precision}_c $$

$$ \mathrm{micro\;Precision} = \frac{\sum_c TP_c}{\sum_c (TP_c + FP_c)} $$

$$ \mathrm{weighted\;Precision} = \frac{1}{N} \sum_{c=1}^{C} n_c \cdot \mathrm{Precision}_c $$

🔬 数式を言葉で読み解く

🐍 Python コード 5:多クラス Precision の 3 方式比較

🎯 このコードでやること:SSDSE-B-2026 の都道府県を人口 3 クラス(小/中/大)に分け、 macro/micro/weighted Precision の差を確認。

📥 入力データ:SSDSE-B-2026 (2023)、 47 県

クラス分類: - 小 (人口 < 100 万): 10 県 - 中 (100-300 万) : 27 県 - 大 (≥ 300 万) : 10 県
 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
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import precision_score, classification_report
from sklearn.preprocessing import StandardScaler

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)

def to_class(p):
    if p < 1_000_000: return 0
    if p < 3_000_000: return 1
    return 2
y = d['A1101'].apply(to_class).values
X = StandardScaler().fit_transform(d[['A1102','A4101','A4103']].values)
pred = LogisticRegression(max_iter=500).fit(X, y).predict(X)

print(f'macro    Precision: {precision_score(y, pred, average="macro"):.4f}')
print(f'micro    Precision: {precision_score(y, pred, average="micro"):.4f}')
print(f'weighted Precision: {precision_score(y, pred, average="weighted"):.4f}')

print(f'\nクラスごと Precision: {precision_score(y, pred, average=None)}')

print(f'\n--- classification_report ---')
print(classification_report(y, pred, target_names=['小','中','大']))

📤 実行結果

macro Precision: 0.9099 micro Precision: 0.7872 weighted Precision: 0.8447 クラスごと Precision: [1. 0.72972973 1. ] --- classification_report --- precision recall f1-score support 小 1.00 0.10 0.18 10 中 0.73 1.00 0.84 27 大 1.00 0.90 0.95 10 accuracy 0.79 47 macro avg 0.91 0.67 0.66 47 weighted avg 0.84 0.79 0.72 47

💬 結果の読み方:micro 0.787 < weighted 0.845 < macro 0.910。 中クラス(最大、 27 件)の Precision = 0.730 が micro/weighted を引き下げる一方、 小・大クラスの Precision = 1.000 が macro を押し上げる。 macro は全クラス対等、 micro/weighted は多数クラス依存。 少数クラスを軽視したくないなら macro全体性能なら micro/weighted を使う。

🏭 Precision の実務応用ケース

ケース A:医療スクリーニング検査

要素内容
陽性がん患者
陽性率0.5%
FP コスト不要な精密検査・患者の不安
FN コストがん見逃し(致命的)
使う指標Recall (=Sensitivity) 優先、 Precision は副指標
典型要件「Recall 99% を保つ Precision」

ケース B:スパムメール分類

要素内容
陽性スパムメール
陽性率50-80%
FP コスト正規メールがスパム扱い(重大)
FN コストスパム見逃し(軽微)
使う指標Precision 優先(FP を許容しない)
典型要件「Precision 99.5% を保つ Recall」

ケース C:検索エンジン

要素内容
陽性クエリに関連する文書
陽性率非常に低い (0.001%)
FP コスト無関係結果がトップに(UX 低下)
FN コスト関連結果を見落とし
使う指標Precision@k (k=10, 20 等)、 nDCG
典型要件「Top 10 の Precision を最大化」

🐍 Python コード 6:Precision@k の自作実装

🎯 このコードでやること:SSDSE-B-2026 でスコア上位 k 件のうち真陽性が何割かを計算。 ランキングシステムの標準評価。

📥 入力データ:SSDSE-B-2026 (2023)、 y = 人口 200 万超

y, proba
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler

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)

y = (d['A1101'] > 2_000_000).astype(int).values
X = StandardScaler().fit_transform(d[['A4101']].values)
proba = LogisticRegression().fit(X, y).predict_proba(X)[:, 1]

# スコア降順でソート
order = np.argsort(proba)[::-1]
y_sorted = y[order]

print(f'真陽性数: {y.sum()}')
print(f'\n{"k":<6}{"Precision@k":<14}{"Recall@k":<12}')
for k in [3, 5, 10, 14, 20, 30, 47]:
    p_k = y_sorted[:k].sum() / k
    r_k = y_sorted[:k].sum() / y.sum()
    print(f'{k:<6}{p_k:<14.3f}{r_k:<12.3f}')

📤 実行結果

真陽性数: 16 k Precision@k Recall@k 3 1.000 0.188 5 1.000 0.312 10 1.000 0.625 14 0.929 0.812 20 0.800 1.000 30 0.533 1.000 47 0.340 1.000

💬 結果の読み方:k=10 まで Precision@k = 1.0(上位 10 件がすべて陽性)。 k=14 で Precision@14 = 0.929、 k=20 で 0.800(陽性 16 件をすべて含むが偽陽性が混じる)。 検索エンジンで「Top 10 表示」なら Precision@10 = 1.0 で完璧。 業務の k に合わせた評価 が肝心。

⚖️ F-β スコア:Precision と Recall のバランス調整

F1 は Precision と Recall を 1:1 で重み付けする調和平均ですが、 業務によっては片方を重視したいケースがあります。 そこで F-β スコアが使われます。

📐 F-β スコアの数式

$$ F_\beta = (1 + \beta^2) \cdot \frac{P \cdot R}{\beta^2 \cdot P + R} $$

β 値意味使いどころ
β = 0F0 = P(Precision のみ)Precision のみ重視(理論限界)
β = 0.5Precision 2 倍重視スパムフィルタ、 検索エンジン
β = 1P と R 同等標準的な分類タスク
β = 2Recall 2 倍重視医療診断、 不正検知
β → ∞F∞ = R(Recall のみ)Recall のみ重視(理論限界)

🔬 数式を言葉で読み解く(F-β)

🐍 Python コード 7:F-β スコアの比較

🎯 このコードでやること:同じ予測に対し β=0.5, 1, 2 の F スコアを計算し、 業務によってどう優先順位が変わるか観察。

📥 入力データ:SSDSE-B-2026 (2023)

y, proba(コード 2 と同じ)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import fbeta_score, precision_score, recall_score
from sklearn.preprocessing import StandardScaler

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)

y = (d['A1101'] > 2_000_000).astype(int).values
X = StandardScaler().fit_transform(d[['A4101']].values)
proba = LogisticRegression().fit(X, y).predict_proba(X)[:, 1]

print(f'{"閾値":<8}{"Precision":<12}{"Recall":<10}{"F0.5":<10}{"F1":<10}{"F2":<10}')
for t in [0.2, 0.3, 0.5, 0.7, 0.8, 0.9]:
    pred = (proba >= t).astype(int)
    p = precision_score(y, pred, zero_division=0)
    r = recall_score(y, pred)
    f05 = fbeta_score(y, pred, beta=0.5, zero_division=0)
    f1 = fbeta_score(y, pred, beta=1, zero_division=0)
    f2 = fbeta_score(y, pred, beta=2, zero_division=0)
    print(f'{t:<8.2f}{p:<12.3f}{r:<10.3f}{f05:<10.3f}{f1:<10.3f}{f2:<10.3f}')

📤 実行結果

閾値 Precision Recall F0.5 F1 F2 0.20 0.615 1.000 0.667 0.762 0.889 0.30 1.000 0.812 0.956 0.897 0.844 0.50 1.000 0.625 0.893 0.769 0.676 0.70 1.000 0.562 0.865 0.720 0.616 0.80 1.000 0.500 0.833 0.667 0.556 0.90 1.000 0.438 0.795 0.609 0.493

💬 結果の読み方:閾値 0.30 で F0.5 = 0.956 が最大(Precision 重視)、 閾値 0.20 で F2 = 0.889 が最大(Recall 重視)、 閾値 0.30 で F1 = 0.897 が最大。 業務での β 選択 = 業務コストの非対称性を反映。 スパムフィルタなら F0.5(Precision 重視)、 がん検診なら F2(Recall 重視)。

🐍 Python コード 8:閾値最適化の自動化

🎯 このコードでやること:F1 / F0.5 / F2 のそれぞれを最大化する閾値を自動探索し、 比較する。

📥 入力データ:SSDSE-B-2026 (2023)

y, proba
 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
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import fbeta_score
from sklearn.preprocessing import StandardScaler

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)

y = (d['A1101'] > 2_000_000).astype(int).values
X = StandardScaler().fit_transform(d[['A4101','A1102']].values)
proba = LogisticRegression().fit(X, y).predict_proba(X)[:, 1]

thresholds = np.linspace(0.01, 0.99, 99)

best_results = {}
for beta in [0.5, 1.0, 2.0]:
    scores = []
    for t in thresholds:
        pred = (proba >= t).astype(int)
        s = fbeta_score(y, pred, beta=beta, zero_division=0)
        scores.append(s)
    scores = np.array(scores)
    idx = np.argmax(scores)
    best_results[beta] = (thresholds[idx], scores[idx])

print(f'{"β":<6}{"最適閾値":<14}{"スコア":<10}')
for beta, (t, s) in best_results.items():
    print(f'{beta:<6.1f}{t:<14.3f}{s:<10.3f}')

📤 実行結果

β 最適閾値 スコア 0.5 0.260 1.000 1.0 0.260 1.000 2.0 0.260 1.000

💬 結果の読み方:このタスクは完全分離可能なため、 すべての β で同じ閾値 0.26 が最適。 不完全分離のタスクでは β ごとに異なる閾値が選ばれる。 実務で β を選ぶ際は、 「FP のコスト ÷ FN のコスト」の比を計算し、 それに応じた β を決める。

🧪 校正された確率と Precision の関係

Precision は「予測陽性のうち真陽性の割合」ですが、 予測確率が校正されていれば 「予測確率 0.9 の案件群の真陽性率は約 0.9」 となるはず。 校正されていないと、 確率の値そのものが意思決定の根拠にならない。

🐍 Python コード 9:予測確率と実際の Precision の対応

🎯 このコードでやること:予測確率を 5 ビンに分け、 各ビン内の Precision(実際の陽性率)を測定。 校正性能を確認。

📥 入力データ:SSDSE-B-2026 (2023)

y, proba
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler

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)

y = (d['A1101'] > 2_000_000).astype(int).values
X = StandardScaler().fit_transform(d[['A4101','A1102']].values)
proba = LogisticRegression().fit(X, y).predict_proba(X)[:, 1]

# 5 ビンに分割
bins = np.linspace(0, 1, 6)
print(f'{"確率ビン":<20}{"件数":<8}{"陽性数":<8}{"実 Precision":<14}{"乖離":<10}')
for i in range(5):
    lo, hi = bins[i], bins[i+1]
    mask = (proba >= lo) & (proba < hi) if i < 4 else (proba >= lo) & (proba <= hi)
    n = mask.sum()
    if n == 0: continue
    pos = y[mask].sum()
    p_actual = pos / n
    p_mean = proba[mask].mean()
    print(f'[{lo:.2f}, {hi:.2f}){"":<8}{n:<8}{pos:<8}{p_actual:<14.3f}{p_actual-p_mean:+.3f}')

📤 実行結果

確率ビン 件数 陽性数 実 Precision 乖離 [0.00, 0.20) 23 0 0.000 -0.106 [0.20, 0.40) 12 4 0.333 +0.075 [0.40, 0.60) 2 2 1.000 +0.515 [0.60, 0.80) 1 1 1.000 +0.321 [0.80, 1.00) 9 9 1.000 +0.021

💬 結果の読み方:高確率ビン(≥0.8)は実 Precision がほぼ予測通り(乖離 +0.02)。 中間ビン(0.4-0.6 で乖離 +0.52 など)や低確率ビンで乖離が大きい(境界例の不確実性・件数の少なさ)。 一般に、 確率を業務閾値として直接使うなら、 Platt scaling か Isotonic regression で校正すると精度向上。 sklearn の CalibratedClassifierCV が便利。

📋 Precision チートシート

定義の一行まとめ

計算手順(sklearn 最短)

ステップ処理API
1モデル予測pred = model.predict(X)
2確率予測proba = model.predict_proba(X)[:, 1]
3Precisionprecision_score(y, pred)
4PR 曲線precision_recall_curve(y, proba)
5APaverage_precision_score(y, proba)
6F1f1_score(y, pred)
7分類レポートclassification_report(y, pred)

判断早見表

業務優先指標典型要件
医療スクリーニングRecall「Recall 99% を保つ Precision」
スパムフィルタPrecision「Precision 99.5% を保つ Recall」
検索エンジンPrecision@k「Top 10 の Precision」
不正検知Recall「FN を最小化」
レコメンドPrecision@k + Diversity「Top 20 で多様な推薦」
マーケティング DMPrecision + Lift「上位 K% の Lift」
創薬スクリーニングPrecision「上位候補で実験成功率」

❓ FAQ:Precision に関するよくある質問

Q1:Precision が 1.0 でも問題ありますか?
A:はい。 Precision = 1.0 は「予測陽性がすべて正解」だが、 「自信ある 1 件だけ予測」しても達成可能。 必ず Recall と併記。 Recall が 0.05 なら 95% の真陽性を見落としている。
Q2:Precision と Accuracy の違いは?
A:Accuracy = (TP+TN) / Total(全体の正解率)。 Precision = TP / (TP+FP)(陽性予測の正解率)。 不均衡データでは Accuracy が高くても Precision が低いことがある(例:全陰性予測で Accuracy 99% だが Precision = 0)。
Q3:multi-class で macro と weighted どちらを使うべき?
A:少数クラスを軽視したくないなら macro(クラス対等平均)、 サンプル数に比例した重み付けなら weighted(多数クラス優先)。 不均衡データで「全クラスを公平に評価」したいなら macro が一般的。
Q4:閾値 0.5 は適切ですか?
A:必ずしも適切ではない。 0.5 はロジスティック回帰の数学的デフォルトだが、 業務のコスト構造(FP vs FN)により最適閾値は変わる。 validation set で F1 や F-β 最大化で選定すべき。
Q5:Precision-Recall 曲線が右下に張り付くのはなぜ?
A:閾値を 0 に近づける(多くを陽性予測)と Precision がベースライン(陽性率)まで下がる。 PR 曲線の右端(Recall = 1)の Precision = 陽性率がベースライン。 ランダム分類器の PR 曲線は水平線になる。
Q6:AP と AUC の関係は?
A:AP は PR 曲線下面積(不均衡データで有用)、 AUC は ROC 曲線下面積(バランス取れたデータで有用)。 数学的には全く別の量で、 AP ≠ AUC。 陽性率が低いほど AP のベースラインは低い、 AUC のベースラインは常に 0.5。
Q7:Precision の信頼区間はどう計算する?
A:Wilson 区間(2 項分布のスコア区間)か、 Bootstrap が標準。 Wilson は statsmodels.stats.proportion.proportion_confint で計算可能。 Bootstrap は 1000 回リサンプリングして 95% 範囲を取る。

🐍 Python コード 10:Wilson 区間で Precision の CI を計算

🎯 このコードでやること:Precision = TP / (TP + FP) の Wilson 95% CI を計算し、 サンプル数による不確実性を把握。

📥 入力データ:SSDSE-B-2026 (2023)

y, pred(コード 1 と同じ)
 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
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import precision_score, confusion_matrix
from sklearn.preprocessing import StandardScaler
from statsmodels.stats.proportion import proportion_confint

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)

y = (d['A1101'] > 2_000_000).astype(int).values
X = StandardScaler().fit_transform(d[['A4101','A1102']].values)
proba = LogisticRegression().fit(X, y).predict_proba(X)[:, 1]

# 閾値 0.5 と 0.3 の 2 ケースで比較
for t in [0.3, 0.5, 0.7]:
    pred = (proba >= t).astype(int)
    cm = confusion_matrix(y, pred)
    tp, fp = cm[1,1], cm[0,1]
    n = tp + fp
    p = tp / n if n > 0 else 0.0
    if n > 0:
        ci_lo, ci_hi = proportion_confint(tp, n, alpha=0.05, method='wilson')
        print(f'閾値 {t}: TP={tp}, FP={fp}, Precision={p:.3f} (95% CI: [{ci_lo:.3f}, {ci_hi:.3f}])')
    else:
        print(f'閾値 {t}: 予測陽性なし')

📤 実行結果

閾値 0.3: TP=14, FP=0, Precision=1.000 (95% CI: [0.785, 1.000]) 閾値 0.5: TP=11, FP=0, Precision=1.000 (95% CI: [0.741, 1.000]) 閾値 0.7: TP=9, FP=0, Precision=1.000 (95% CI: [0.701, 1.000])

💬 結果の読み方:閾値 0.3 で Precision = 1.0(TP=14)、 95% CI は [0.785, 1.000](幅 0.215)。 サンプルが少ないため点推定 1.0 でも真の Precision は 0.79-1.00 と幅広い。 必ず CI を併記し、 「Precision = 1.00 (95% CI: 0.79-1.00)」のように報告。

🎓 Precision 演習問題

本ページの内容を定着させるため、 自分の手を動かす演習を 7 つ用意。

  1. SSDSE-B-2026 で「世帯数 100 万超」を陽性とし、 Precision・Recall・F1 を計算。 閾値 0.3 / 0.5 / 0.7 で比較。
  2. 同データで陽性率を変えて(人口 500 万超、 200 万超、 50 万超)、 Precision の挙動の違いを観察。
  3. 多クラス分類(3 クラス)で macro / micro / weighted の差を最小化する条件を考察。
  4. F-β スコアで β = 0.3, 1, 3 のそれぞれで最適閾値を選定し、 結果を比較。
  5. Bootstrap で Precision の 95% CI を 1000 回計算。 サンプルサイズによる CI 幅の違いを確認。
  6. Precision@k と閾値ベース Precision がいつ一致し、 いつズレるかを実例で示す。
  7. 校正されていないモデルを Isotonic Regression で再校正し、 Precision の改善を確認。

🐍 Python コード 11:演習 1 の解答例

🎯 このコードでやること:演習 1(世帯数 100 万超を陽性として閾値別に評価)の実装。

📥 入力データ:SSDSE-B-2026 (2023)

A1102 = 日本人人口(世帯数の代替)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import precision_score, recall_score, f1_score
from sklearn.preprocessing import StandardScaler

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)

# 陽性: 日本人人口 100 万超 (世帯数代替)
y = (d['A1102'] > 1_000_000).astype(int).values
X = StandardScaler().fit_transform(d[['A1101','A4101']].values)
proba = LogisticRegression().fit(X, y).predict_proba(X)[:, 1]

print(f'陽性数: {y.sum()}, 陰性数: {(1-y).sum()}')
print(f'\n{"閾値":<8}{"Precision":<12}{"Recall":<10}{"F1":<8}')
for t in [0.3, 0.5, 0.7]:
    pred = (proba >= t).astype(int)
    p = precision_score(y, pred, zero_division=0)
    r = recall_score(y, pred)
    f = f1_score(y, pred, zero_division=0)
    print(f'{t:<8.1f}{p:<12.3f}{r:<10.3f}{f:<8.3f}')

📤 実行結果

陽性数: 36, 陰性数: 11 閾値 Precision Recall F1 0.3 0.766 1.000 0.867 0.5 0.766 1.000 0.867 0.7 1.000 0.722 0.839

💬 結果の読み方:日本人人口 100 万超(36 県)を予測する例。 総人口と日本人人口は強相関だが完全一致ではなく、 低閾値では Precision=0.77、 閾値 0.7 で Precision=1.0(Recall=0.72)。 教訓:陽性ラベルと特徴量が極めて近い情報を持つと閾値調整で容易に高 Precision に届く。 実務では特徴量と陽性ラベルに 強い相関がある場合は target leakage を疑う

🌐 Precision の本質:「対応リストの当たり率」

Precision は 「あなたが対応すると決めた案件のうち、 本当に対応価値があったものの割合」。 顧客対応リスト、 医療検査陽性者リスト、 検索結果リスト、 マーケ送付リスト—— どれも「リストを見る人の視点」での評価。 ROC AUC が「順位の妥当性」を測るのに対し、 Precision は「実際にアクションを起こす対象群の品質」を測る。 業務的にはこちらの方が直感的なケースが多い。

📚 関連用語・派生概念(最終リンク集)

📐 ボーナス:マルチラベル Precision

マルチラベル分類(1 サンプルが複数ラベルを持つ)の Precision は次のように定義されます。

$$ \mathrm{Precision}_{\text{multi-label}} = \frac{1}{N} \sum_{i=1}^{N} \frac{|y_i \cap \hat{y}_i|}{|\hat{y}_i|} $$

🔬 数式を言葉で読み解く(マルチラベル)

🧠 「Precision を理解した」と言える 5 つの条件

  1. 白紙に Precision の定義(TP/(TP+FP))と Recall との違いを書ける
  2. 不均衡データで Accuracy が高くても Precision が低い例を作れる
  3. F-β スコアの β 値を業務コストから導出できる
  4. 多クラス Precision の macro / micro / weighted の違いを言える
  5. Precision の信頼区間を Bootstrap または Wilson で計算できる

🧮 ベイズ的視点による Precision

Precision はベイズの定理を使うと「事後確率」として解釈できます。 これは医療検査などで頻出する「陽性的中率(PPV)」と同じ概念です。

📐 ベイズ定理による Precision の表現

$$ \mathrm{Precision} = P(Y=1 \mid \hat{Y}=1) = \frac{P(\hat{Y}=1 \mid Y=1) \cdot P(Y=1)}{P(\hat{Y}=1)} = \frac{\mathrm{Sens} \cdot \pi}{\mathrm{Sens} \cdot \pi + (1-\mathrm{Spec}) \cdot (1-\pi)} $$

記号意味
$\pi = P(Y=1)$陽性の事前確率(有病率・基底比)
$\mathrm{Sens} = P(\hat{Y}=1 \mid Y=1)$感度 = Recall
$\mathrm{Spec} = P(\hat{Y}=0 \mid Y=0)$特異度
$1 - \mathrm{Spec}$偽陽性率 (FPR)

🔬 数式を言葉で読み解く(ベイズ Precision)

🐍 Python コード 12:稀な事象における Precision の数学的限界

🎯 このコードでやること:陽性率(基底比)を変えると、 同じ感度・特異度でも Precision がどう変化するかをシミュレートする。

📥 入力データ:SSDSE-B-2026 (2023) の実際の陽性率を使う

感度 0.95、 特異度 0.95 を固定し、 陽性率を変えた場合の Precision
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import pandas as pd
import numpy as np

# 実際のデータで陽性率の幅を確認
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)

print('SSDSE-B-2026 (2023) の陽性率実例:')
for thresh, name in [
    (5_000_000, '人口 500 万超'),
    (3_000_000, '人口 300 万超'),
    (2_000_000, '人口 200 万超'),
    (1_000_000, '人口 100 万超'),
    (500_000,   '人口  50 万超'),
]:
    pi = (d['A1101'] > thresh).mean()
    print(f'  {name:<12}: 陽性率 π = {pi:.4f}')

# 感度・特異度を固定して陽性率を変える
sens = 0.95
spec = 0.95
print(f'\n感度 = {sens}, 特異度 = {spec} を固定、 陽性率を変えると Precision はこう変化:')
print(f'{"陽性率π":<12}{"Precision (PPV)":<18}{"FP 数 (1000 人検査時)":<25}')
for pi in [0.001, 0.005, 0.01, 0.05, 0.10, 0.30, 0.50]:
    prec = (sens * pi) / (sens * pi + (1 - spec) * (1 - pi))
    fp_per1k = (1 - spec) * (1 - pi) * 1000
    print(f'{pi:<12.4f}{prec:<18.4f}{fp_per1k:<25.1f}')

📤 実行結果

SSDSE-B-2026 (2023) の陽性率実例: 人口 500 万超 : 陽性率 π = 0.1915 人口 300 万超 : 陽性率 π = 0.2128 人口 200 万超 : 陽性率 π = 0.3404 人口 100 万超 : 陽性率 π = 0.7872 人口 50 万超 : 陽性率 π = 1.0000 感度 = 0.95, 特異度 = 0.95 を固定、 陽性率を変えると Precision はこう変化: 陽性率π Precision (PPV) FP 数 (1000 人検査時) 0.0010 0.0187 50.0 0.0050 0.0872 49.8 0.0100 0.1610 49.5 0.0500 0.5000 47.5 0.1000 0.6786 45.0 0.3000 0.8906 35.0 0.5000 0.9500 25.0

💬 結果の読み方:感度 95%、 特異度 95% の 「優秀な検査」 でも、 陽性率が 0.1% なら Precision はわずか 1.87%。 1000 件中 50 件の FP が出てくる。 これが「稀少事象検出の根本的難しさ」。 医療で「がん検診を全員に施しても、 陽性者の大半は実は陰性」というのはこの数学。 業務で稀少事象を検出するなら、 事前スクリーニング で陽性率を上げる工夫が必須。

💰 コスト感度学習(Cost-Sensitive Learning)

FP コストと FN コストが大きく違うとき、 単純な Precision/Recall 最大化ではなく、 期待損失最小化を目指します。

$$ \min_{f} \mathbb{E}_{(X, Y)} \left[ C_{FP} \cdot \mathbb{1}[f(X)=1, Y=0] + C_{FN} \cdot \mathbb{1}[f(X)=0, Y=1] \right] $$

🐍 Python コード 13:コスト感度閾値選定

🎯 このコードでやること:FP コスト、 FN コストを与え、 期待損失を最小化する閾値を選定する。

📥 入力データ:SSDSE-B-2026 (2023)

陽性 = 人口 300 万超、 特徴量 = 出生数
 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
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler

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)

y = (d['A1101'] > 3_000_000).astype(int).values
X = StandardScaler().fit_transform(d[['A4101']].values)
proba = LogisticRegression().fit(X, y).predict_proba(X)[:, 1]

# シナリオ A: FP = 1000, FN = 1000 (対等)
# シナリオ B: FP = 1000, FN = 10000 (見逃しが重い)
# シナリオ C: FP = 10000, FN = 1000 (誤報が重い)
scenarios = [
    ('A 対等',         1000, 1000),
    ('B FN 重視',       1000, 10000),
    ('C FP 重視',      10000, 1000),
]

thresholds = np.linspace(0.01, 0.99, 99)
print(f'{"シナリオ":<14}{"FP cost":<10}{"FN cost":<10}{"最適閾値":<10}{"期待損失":<10}')
for name, cfp, cfn in scenarios:
    losses = []
    for t in thresholds:
        pred = (proba >= t).astype(int)
        fp = ((pred==1) & (y==0)).sum()
        fn = ((pred==0) & (y==1)).sum()
        loss = cfp * fp + cfn * fn
        losses.append(loss)
    losses = np.array(losses)
    idx = np.argmin(losses)
    print(f'{name:<14}{cfp:<10}{cfn:<10}{thresholds[idx]:<10.3f}{losses[idx]:<10}')

📤 実行結果

シナリオ FP cost FN cost 最適閾値 期待損失 A 対等 1000 1000 0.180 0 B FN 重視 1000 10000 0.180 0 C FP 重視 10000 1000 0.180 0

💬 結果の読み方:本タスクは完全分離可能なため、 すべてのシナリオで 最適閾値 0.18・損失 0 を達成。 完全分離では閾値に幅があり、 コスト非対称でも同じ点が選ばれてしまう。 実務では完全分離不可能なケースが多く、 その場合はコスト構造が閾値を大きく動かす。

🌍 業界別コスト感度の典型値

業界FP コストFN コスト典型 β
がん検診不要な精密検査 (1万円)がん見逃し (生命)β > 5 (Recall 重視)
スパムフィルタ正規メール紛失 (機会損失)スパム表示 (軽微)β < 0.5 (Precision 重視)
不正検知正規取引拒否 (顧客損失)不正取引許可 (損害)β ≈ 2-3
創薬スクリーニング無効化合物の追加実験有望候補の見逃しβ ≈ 0.5
クレジット審査機会損失 (利息)貸倒損失 (元本)β ≈ 0.3
マーケ DM送付コスト (100 円)機会損失 (購入額)β ≈ 0.5

🧪 Precision とサンプリングバイアス

テストデータの陽性率と本番データの陽性率が違うと、 Precision はずれます。 これを prior shift と呼びます。

🐍 Python コード 14:prior shift の影響

🎯 このコードでやること:学習データの陽性率を維持しつつ、 テスト時に陽性率が変わると Precision がどう変化するかをシミュレート。

📥 入力データ:SSDSE-B-2026 (2023)

学習: 陽性率 0.3、 テスト: 陽性率を変動
 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
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler

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)

# 学習データを構成 (陽性率 0.3 程度)
y_train = (d['A1101'] > 2_000_000).astype(int).values
X_train = StandardScaler().fit_transform(d[['A4101']].values)
scaler = StandardScaler().fit(d[['A4101']])

model = LogisticRegression().fit(X_train, y_train)

# テストデータ (人為的にラベル定義を変えて陽性率変動)
print(f'学習時の陽性率: {y_train.mean():.3f}')
print(f'\n{"テスト陽性率":<15}{"Precision":<12}{"Recall":<10}{"備考":<30}')
for thresh, name in [
    (5_000_000, '陽性率低'),
    (3_000_000, '陽性率中'),
    (2_000_000, '陽性率高'),
]:
    y_test = (d['A1101'] > thresh).astype(int).values
    X_test = scaler.transform(d[['A4101']])
    pred = model.predict(X_test)
    pi = y_test.mean()
    if pred.sum() > 0:
        p = ((pred==1) & (y_test==1)).sum() / pred.sum()
    else:
        p = 0.0
    if y_test.sum() > 0:
        r = ((pred==1) & (y_test==1)).sum() / y_test.sum()
    else:
        r = 0.0
    print(f'{pi:<15.3f}{p:<12.3f}{r:<10.3f}{name:<30}')

📤 実行結果

学習時の陽性率: 0.340 テスト陽性率 Precision Recall 備考 0.191 0.900 1.000 陽性率低 0.213 1.000 1.000 陽性率中 0.340 1.000 0.625 陽性率高

💬 結果の読み方:学習時と同じ陽性率(0.340)なら Precision = 1.0、 陽性率が下がると Precision も下がる(0.191 で 0.900)。 これは 「同じ予測器でも、 運用環境の陽性率が低いと真陽性予測の品質が下がる」 という現実を示す。 医療検査機を別地域に持っていく、 季節性のあるマーケに使う、 などで発生。 対策:定期的な再校正と prior の調整

📐 ベイズ調整による Precision 修正

本番陽性率 $\pi_{\text{new}}$ が学習時 $\pi_{\text{train}}$ と違うとき、 確率を補正できます。

$$ \hat{p}_{\text{new}}(X) = \frac{\hat{p}(X) \cdot (\pi_{\text{new}} / \pi_{\text{train}})}{\hat{p}(X) \cdot (\pi_{\text{new}} / \pi_{\text{train}}) + (1 - \hat{p}(X)) \cdot ((1-\pi_{\text{new}}) / (1-\pi_{\text{train}}))} $$

🔬 数式を言葉で読み解く(prior shift 補正)

🏁 最終まとめ:Precision を使いこなす 10 の心得

  1. 必ず Recall と一緒に見る:Precision = 1.0 でも Recall = 0.01 なら無能
  2. 不均衡データでは Accuracy より信頼:Accuracy は楽観的になる
  3. 陽性率(基底比)を必ず確認:Precision のベースラインは陽性率
  4. 業務コストから β を選ぶ:FP/FN コスト比に応じた F-β
  5. 閾値選定は validation set で:同じデータで学習・閾値選定は過学習
  6. CI を必ず併記:小サンプルでは点推定だけは不誠実
  7. multi-class は macro/micro/weighted を意識:自動デフォルトを盲信しない
  8. Precision@k も評価する:業務件数制約があるなら必須
  9. prior shift に備える:定期的な再評価・再校正
  10. ベイズ定理を覚えておく:稀少事象の数学的限界を理解する

📜 Precision・Recall の歴史と用語の混乱

Precision と Recall は分野ごとに異なる名前で呼ばれることがあります。 用語の混乱を整理します。

📚 分野別の用語対応表

分野Precision の別名Recall の別名備考
情報検索 (IR)PrecisionRecall1960 年代の文献検索評価に由来
医療診断陽性的中率 (PPV)感度 (Sensitivity)米国 CDC ガイドラインで標準
機械学習 (ML)PrecisionRecallIR 由来、 最も一般的
統計学陽性的中率真陽性率 (TPR)仮説検定の文脈
信号検出理論(あまり使わない)Hit Rate心理物理学で発祥
レーダー工学Detection ProbabilityDetection Rate第二次大戦の用語

🕰️ 概念の歴史的展開

年代事項
1940sレーダー信号検出で「真陽性 / 偽陽性」概念が成立
1950s心理物理学で「信号検出理論」が体系化
1960s情報検索 (Cleverdon 1966) で Precision/Recall を正式定義
1970s医療診断分野で「感度・特異度」が広まる
1990s機械学習で標準指標として定着
2000sF1 スコア (van Rijsbergen 1979) が爆発的に普及
2010s不均衡データ問題で PR-AUC が注目
2020s公平性・校正性能の評価指標として再評価

📐 Precision の統計的検定

「2 つのモデルの Precision に有意差があるか」を判定するには、 McNemar 検定(同一テストセット上)または Fisher の正確検定(小サンプル)が使われます。

🐍 Python コード 15:McNemar 検定で 2 モデル比較

🎯 このコードでやること:ロジスティック回帰とランダムフォレストの予測結果を比較し、 McNemar 検定で「予測の差が有意か」を判定。

📥 入力データ:SSDSE-B-2026 (2023)

y, X、 2 モデルで予測比較
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
from statsmodels.stats.contingency_tables import mcnemar

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)

y = (d['A1101'] > 2_000_000).astype(int).values
X = StandardScaler().fit_transform(d[['A4101','A1102']].values)

pred_lr = LogisticRegression().fit(X, y).predict(X)
pred_rf = RandomForestClassifier(n_estimators=50, random_state=0).fit(X, y).predict(X)

# McNemar 検定: 一方だけ正解の数を比較
b = ((pred_lr == y) & (pred_rf != y)).sum()  # LR 正解、 RF 誤り
c = ((pred_lr != y) & (pred_rf == y)).sum()  # LR 誤り、 RF 正解
print(f'LR 正解 & RF 誤り: {b}')
print(f'LR 誤り & RF 正解: {c}')

table = [[0, b], [c, 0]]
result = mcnemar(table, exact=True)
print(f'\nMcNemar 検定 p 値: {result.pvalue:.4f}')
print(f'  → p < 0.05 なら有意差ありと判断')
print(f'  本ケースは {"有意差あり" if result.pvalue < 0.05 else "有意差なし"}')

📤 実行結果

LR 正解 & RF 誤り: 0 LR 誤り & RF 正解: 5 McNemar 検定 p 値: 0.0625 → p < 0.05 なら有意差ありと判断 本ケースは 有意差なし

💬 結果の読み方:一方のみ正解のケースが 0 対 5 と偏るが、 サンプルが少ないため McNemar 検定では有意差なし(p=0.0625 > 0.05)。 実務で「モデル A と B どちらが優秀か」と問われたら、 McNemar 検定で「予測の差が偶然の範囲内か」を判定するのが正攻法。 Accuracy や Precision の数値差を単純比較するだけでは不十分。

🔗 完結リンク集(最終版)

🌟 Precision に関するベストプラクティス集

実務で Precision を扱う際の「これだけは守る」べき原則を、 失敗事例と共に提示します。

原則 1:陽性率(基底比)を最初に確認する

データを受け取ったら、 まず陽性率を計算してください。 陽性率 50% と 1% では、 同じ Precision = 0.6 でも意味が全く違います。

失敗事例:「Precision 60% のモデルができました」と報告したが、 陽性率が 80% で、 実は ランダム予測(陽性率と同じ)より悪い モデルだった。 陽性率を先に確認していれば気づけた。

原則 2:単一指標で判断しない

Precision、 Recall、 F1、 AUC、 PR-AUC、 Accuracy をすべて並べて確認。 1 つの指標が好結果でも、 他が悪化していたら要注意。

失敗事例:閾値を高く設定して Precision を 0.95 に上げたが、 Recall が 0.05 に低下。 「クライアントは大満足」だったが、 実際は 95% の機会損失。 F1 を見ていれば検知できた。

原則 3:CI(信頼区間)を必ず報告する

Precision の点推定だけ報告するのは統計的に不誠実。 Bootstrap か Wilson 区間で 95% CI を併記しましょう。

失敗事例:「Precision 90%」だけ報告したが、 サンプル 20 件のため CI は [0.68, 0.98]。 実際には 不確実性が大きい。 CI 込みなら誤ったビジネス判断を防げた。

原則 4:閾値選定は別データで

同じデータで学習と閾値選定すると過学習。 必ず train / validation / test に分割し、 validation で閾値を決定。

失敗事例:全データで Precision 最大化する閾値を選んだら、 本番投入で Precision が 0.4 まで低下。 過学習の典型。 validation set で選定していれば 0.7 程度の本番性能が出せた。

原則 5:定期的な再評価とドリフト監視

本番環境では時間と共にデータ分布が変わる(concept drift)。 月 1 回は Precision を測定し、 閾値の見直しを検討。

🏁 深掘り解説の総まとめ

本ページでは、 Precision(適合率)を次の観点から掘り下げました:
  • 📐 数式と数学的本質(ベイズ定理による表現)
  • 🧮 SSDSE-B-2026 での実値計算(複数閾値での比較)
  • 🐍 Python での実装(10+ コードブロック)
  • ⚠️ 落とし穴と典型的失敗パターン
  • 🌐 関連指標との関係マップ
  • 🏭 業界別ベストプラクティス
  • 🎓 演習問題と自己チェック
これらをすべて手を動かして実装した時、 Precision を 「知っている」から「使える」 へと変えることができます。

🧭 もう一段深く: Precision は「基準率(有病率)」で動く

このページの他ブロックは主に「閾値を動かすと Precision が動く」話でした。 ここでは角度を変え、 閾値も分類器もそのまま固定したのに、 対象集団の陽性割合(基準率 π)が変わるだけで Precision が上下するという、 実務で最も誤解されやすい性質を実データで確認します。 対をなす Recall はこの影響を全く受けません。

💡 直感 — Precision はベイズの「事後確率」

Precision とは「陽性と予測されたという証拠を見た後で、 本当に陽性である確率」= 事後確率 P(真陽性 | 予測陽性) です。 ベイズの式で分解すると、 分類器の性能(尤度)だけでなく 事前確率=基準率 π が入り込みます。

【基準率を明示した Precision】
$$\text{Precision}(\pi)=\frac{\text{TPR}\cdot \pi}{\text{TPR}\cdot \pi + \text{FPR}\cdot(1-\pi)}$$
TPR=真陽性率(=Recall)、 FPR=偽陽性率。 分子は「本当に陽性の中で当てた分」、 分母に「陰性を誤検出した分」が π を通じて効く。

ここで Recall=TPR は π を含まない(陽性だけを分母にする量なので、 陰性が何個いようと不変)。 一方 Precision は分母に FPR·(1−π) を持つため、 陰性が相対的に増える(π が下がる)ほど偽陽性が効いて下がる。 これが「Recall は動かないのに Precision だけ動く」の正体です。

⚠️ 落とし穴(重要) — 基準率フォールバシー(本番で Precision が崩壊する)

SSDSE-B-2026(2023 年・47 都道府県)で、 「総人口 500 万人超」を陽性「転入者数(A5101)≥ 5 万人」で予測する分類器を作ると(本ページ上部の実値計算と同じ設定)、 混同行列は実測で TP=8 / FP=1 / FN=1 / TN=37。 ここから測った値は次のとおりで、 いずれも実データの測定値です。

実測(π=9/47=0.191): Recall(TPR)=8/9=0.8889 FPR=1/38=0.0263 Precision=8/9=0.8889 唯一の偽陽性(FP)=京都府[R26000] / 唯一の偽陰性(FN)=北海道[R01000]

次に、 この分類器の TPR・FPR はそのまま固定し(実測値)、 適用先集団の基準率 π だけを差し替えたときの Precision を上の式で計算します。 π の値のうち 0.191 以外は「もし有病率がこうだったら」という架空の基準率です(TPR/FPR は実測、 π のみ仮想)。

基準率 π(陽性割合)Recall(=TPR・不変)Precision読み方
0.01(架空・激レア)0.8890.254陽性予測の 3/4 が外れ
0.05(架空)0.8890.640まだ 4 割が誤検出
0.10(架空)0.8890.790実用ライン付近
0.191(実測・47 県)0.8890.889実データの基準点
0.30(架空)0.8890.935陽性が多いほど当たりやすい
0.50(架空・均衡)0.8890.971均衡データでは高く見える

結論: Recall 列は 0.889 で一定なのに、 Precision は π=0.01 で 0.254、 π=0.50 で 0.971 と 約 4 倍も動く。 実務での帰結は重大です — 50:50 に整えたテストセットで Precision=0.97 と報告したモデルを、 陽性 1% の本番に載せると Precision は 0.25 に崩壊する(Recall は不変なので「性能は変わっていないはず」と錯覚しやすい)。 これが「基準率フォールバシー」。 論文・レポートで Precision を書くときは 評価データの π を必ず併記し、 本番の π と一致させるのが鉄則です。

🚀 発展 — 有病率補正と医療スクリーニングの PPV

上の式は、 ある π で測った Precision を別の π へ「翻訳」できることも意味します。 まず実測 Precision と Recall から尤度比 FPR/TPR を復元し、 目標の π を代入すれば本番想定の Precision が予測できる(prior shift 補正)。 医療統計ではこの Precision がそのまま PPV(陽性的中率)で、 「検査の感度・特異度は同じでも、 有病率の低い集団で陽性なら実は多くが偽陽性」という健診の基本原則そのものです。 だからこそ不均衡データでは、 π に無関係な ROC 曲線/AUC ではなく、 π を織り込む PR 曲線・PR-AUC で評価し、 特異度(=1−FPR)を併記するのが定石になります。

🔗 関連ページ