Day 022 — 確率の基礎(条件付き確率・ベイズの定理)

2026-05-01 白 / Phase 1 理論 + コーディング 確率の基礎

📚 背景知識(読んでから問題へ)

条件付き確率とは?

「ある事象が起きたと分かっているとき、別の事象が起きる確率」のことです。

日常の例:

  • 「今日が雨だった(条件)なら、明日も雨が降る確率は?」
  • 「スパムメールだった(条件)なら、"無料"という単語が含まれている確率は?」

記号と式:

P(A|B) = P(AとBが同時に起きる確率) / P(Bが起きる確率)
       = P(A ∩ B) / P(B)

日本語で言うと: 「B が起きたという条件のもとで、A が起きる確率」


ベイズの定理とは?

条件付き確率を「逆向きに」計算する公式です。

直感的な理解:

  • 普通: 「スパムメール → "無料"が含まれる確率」(原因→結果)
  • ベイズ: 「"無料"が含まれている → スパムメールである確率」(結果→原因)

式(怖くない!):

P(スパム|"無料"という単語) = P("無料"|スパム) × P(スパム) / P("無料")

これは「後から新しい証拠を見て、最初の予測を更新する」考え方です。


Kaggleでの重要性

  • スパムフィルター: ナイーブベイズ分類器(最も有名なML手法の1つ)
  • 医療AI: 「陽性反応が出た → 実際に病気である確率」
  • A/Bテスト: ベイズ統計による意思決定

📝 問題

Part 1: 条件付き確率の計算(理論)

ECサイトの顧客データを分析しています。以下の情報が分かっています:

  • 全顧客の30%が「プレミアム会員」
  • プレミアム会員の60%が「先月購入あり」
  • 非プレミアム会員の20%が「先月購入あり」

Q1: プレミアム会員かつ先月購入ありの確率は?

Q2: 先月購入があった顧客のうち、プレミアム会員である確率は?(ベイズの定理を使う)


Part 2: Pythonで確率を計算する(コーディング)

以下のデータを使って、Q1・Q2をPythonで確認してください:

import pandas as pd
import numpy as np

np.random.seed(42)
n = 10000  # 顧客数

# データ生成
is_premium = np.random.choice([True, False], size=n, p=[0.3, 0.7])
purchased = np.where(
    is_premium,
    np.random.choice([True, False], size=n, p=[0.6, 0.4]),
    np.random.choice([True, False], size=n, p=[0.2, 0.8])
)

df = pd.DataFrame({'is_premium': is_premium, 'purchased': purchased})

タスク:

  1. P(premium AND purchased) を計算する
  2. P(premium | purchased) を計算する(ベイズの定理の確認)
  3. クロス集計表(pd.crosstab)を作成して、直感的に確認する

🔍 ヒント(段階的開示)

ヒント1(方向性)

条件付き確率は「絞り込み」のイメージ。「先月購入あり」の人だけに絞って、その中でプレミアム会員の割合を計算する。

ヒント2(アプローチ)

理論:

  • Q1: P(premium) × P(purchased|premium) = 0.3 × 0.6 = ?
  • Q2: ベイズ定理: P(premium|purchased) = P(purchased|premium) × P(premium) / P(purchased)
  • P(purchased) = P(purchased|premium)×P(premium) + P(purchased|非premium)×P(非premium)

コーディング:

  • df[条件][列名].mean() で条件付き確率を計算できる
  • pd.crosstab(df['is_premium'], df['purchased'], normalize='all') で全体比率が出る
ヒント3(コード骨格)
# P(premium AND purchased)
p_premium_and_purchased = (df['is_premium'] & df['purchased']).mean()

# P(premium | purchased) — "purchased"が True の行に絞る
p_premium_given_purchased = df[df['purchased'] == True]['is_premium'].mean()

# クロス集計
print(pd.crosstab(df['is_premium'], df['purchased'], margins=True))

模範解答

Part 1: 理論計算

Q1: P(premium AND purchased)

P(premium AND purchased)
= P(purchased | premium) × P(premium)
= 0.6 × 0.3
= 0.18(全顧客の18%)

Q2: P(premium | purchased) — ベイズの定理

まず P(purchased) を全確率の公式で計算:

P(purchased)
= P(purchased|premium)  × P(premium)  + P(purchased|非premium) × P(非premium)
= 0.6 × 0.3                           + 0.2 × 0.7
= 0.18 + 0.14
= 0.32

P(premium | purchased)
= P(purchased|premium) × P(premium) / P(purchased)
= 0.18 / 0.32
= 0.5625(約56.25%)

解釈: 「先月購入があった顧客」に絞ると、約56%がプレミアム会員。

全体のプレミアム会員比率(30%)より大幅に高い → プレミアム会員の方が購入意欲が高い!


Part 2: Pythonコード

import pandas as pd
import numpy as np

np.random.seed(42)
n = 10000

is_premium = np.random.choice([True, False], size=n, p=[0.3, 0.7])
purchased = np.where(
    is_premium,
    np.random.choice([True, False], size=n, p=[0.6, 0.4]),
    np.random.choice([True, False], size=n, p=[0.2, 0.8])
)

df = pd.DataFrame({'is_premium': is_premium, 'purchased': purchased})

# 1. P(premium AND purchased)
p_premium_and_purchased = (df['is_premium'] & df['purchased']).mean()
print(f"P(premium AND purchased) = {p_premium_and_purchased:.4f}")
# 期待値: 0.18

# 2. P(premium | purchased)
purchased_only = df[df['purchased'] == True]
p_premium_given_purchased = purchased_only['is_premium'].mean()
print(f"P(premium | purchased) = {p_premium_given_purchased:.4f}")
# 期待値: 0.5625

# 3. クロス集計表
cross = pd.crosstab(df['is_premium'], df['purchased'], margins=True)
print("\nクロス集計(件数):")
print(cross)

cross_norm = pd.crosstab(df['is_premium'], df['purchased'], normalize='all') * 100
print("\nクロス集計(%):")
print(cross_norm.round(2))

出力例(n=10,000なので理論値に近い):

▶ 出力を見る
P(premium AND purchased) = 0.1813
P(premium | purchased) = 0.5638

クロス集計(件数):
purchased      False  True   All
is_premium
False           5625  1372  6997
True            1213  1790  3003
All             6838  3162 10000

クロス集計(%):
purchased     False   True
is_premium
False         56.25  13.72
True          12.13  17.90

🪜 Step-by-Step 解説

1
データ生成の仕組みを理解する

np.where(condition, x, y) は「conditionがTrueならx、FalseならY」を返す。

プレミアム会員(True)と非会員(False)で異なる購入確率を設定している。

2
& 演算子で「AND条件」を計算
(df['is_premium'] & df['purchased']).mean()

TrueとTrueの組み合わせだけが True になる。.mean() でTrueの割合(確率)が出る。

3
条件で絞ってから .mean() を取る
df[df['purchased'] == True]['is_premium'].mean()

「購入あり」に絞った後、その中でのプレミアム会員の割合 = 条件付き確率。

4
クロス集計で視覚的に確認

pd.crosstab は2変数のクロス集計を作る。Kaggleのデータ探索(EDA)でよく使う。


📐 数学・統計の補足(文系向け)

ベイズの定理を「証拠の更新」として理解する:

  1. 最初の信念(事前確率): 「全員の中でプレミアム会員は30%」
  2. 新しい証拠: 「この人は先月購入した」
  3. 更新後の信念(事後確率): 「プレミアム会員である確率が56%に上がった」

これが「ベイズ更新」。機械学習全体の根底にある考え方です。


🏆 Kaggleでの実践的な使い方

  1. ナイーブベイズ分類器 (sklearn.naive_bayes.MultinomialNB): テキスト分類・スパム判定
  2. クロス集計EDA: カテゴリ変数の関係性を把握する最初のステップ
  3. ターゲットエンコーディング: カテゴリ特徴量を「目的変数の条件付き確率」に変換する手法(上位手法)

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
P(AB) = P(BA) と思ってしまう直感的にひっくり返しても同じに見えるP(雨傘を持っている) ≠ P(傘を持っている雨)
df['A'] == True を忘れてフィルタリングPandasのBoolean列の扱いに慣れていないdf[df['A']] でもOKだが == True で明示する方が分かりやすい
クロス集計の normalize 引数'all'/'index'/'columns' で違う条件付き確率には normalize='columns' (列ごとに正規化) が直感的

🚀 次のステップ

  • 発展: ナイーブベイズ分類器を sklearn で実装し、テキスト分類に応用する
  • 次回予告: 確率分布(正規分布・二項分布)— 「データのばらつき方の型」を学ぶ

🎯 自己評価

自分の回答

気づき・メモ