全体では逆の結果になる?シンプソンのパラドックスを小さな架空データで理解する
AIを使いこなすClaudeで執筆し、GPTで自動校閲・修正しています。

結論から言うと、全体では案Bが勝っているのに、グループ別に見ると両方で案Aが勝っている、という逆転は実際に起こります。原因は計算ミスではなく、比べている2つの案で、グループの構成比が違うことです。全体の割合は「各グループの割合を、そのグループの件数で重みづけして混ぜたもの」なので、混ぜ方が違えば順位がひっくり返ります。 この現象がシンプソンのパラドックスです。この記事では、100件ずつの小さな架空データで逆転を再現し、自分のデータで見落とさないための手順メモと判断表を用意します。
説明用の例:全体ではBが勝ち、層別ではAが勝つ
以下は仕組みを説明するために私が作った架空の数字で、実測データではありません。2種類のメール件名(案A・案B)を、既存会員と新規会員に送ったとします。指標はクリック率(クリック数÷送信数)です。
- 既存会員、案:A、送信数:20、クリック数:9、クリック率:45%
- 既存会員、案:B、送信数:80、クリック数:32、クリック率:40%
- 新規会員、案:A、送信数:80、クリック数:16、クリック率:20%
- 新規会員、案:B、送信数:20、クリック数:3、クリック率:15%
層ごとに見ると、既存会員でも新規会員でもAのほうが高い結果です(45%対40%、20%対15%)。ところが層を区別せず合計すると次のようになります。
- A、送信数:20+80=100、クリック数:9+16=25、全体のクリック率:25%
- B、送信数:80+20=100、クリック数:32+3=35、全体のクリック率:35%
全体ではBが10ポイント高く、結論が逆になりました。
逆転する仕組み:率は件数で重みづけされている
全体のクリック率は、層別の率に「その案の送信先のうち各層が占める割合」を掛けて足したものです。
- 案A:既存20%、新規80%の構成。0.2×45% + 0.8×20% = 9% + 16% = 25%
- 案B:既存80%、新規20%の構成。0.8×40% + 0.2×15% = 32% + 3% = 35%
ここで効いているのは、「既存会員は、どの案でもクリックしやすい」という層の性質です。Bはたまたまクリックしやすい既存会員に多く送られ、Aは新規会員に多く送られました。件名の良し悪しとは別に、送り先の違いが全体の率を押し上げたり押し下げたりしています。
よくある誤りは、層別の率を単純平均して全体の率にすることです。この例の案Aなら (45%+20%)÷2=32.5% となり、実際の25%とずれます。全体の率は、必ず「クリック数の合計÷送信数の合計」で出します。
なお、統計局の「なるほど統計学園」は、相関係数だけでは関係性を判断しきれない場合があり、散布図で形を確認し、背景を注意深く分析するよう説明しています(複数の変数の関係性を見る|総務省統計局)。層別に分けることは、背景を調べる一つの方法です。
層別に分けて確かめる手順メモ
自分の集計表で、次の順に確認します。
- 全体の率を出す:案ごとに、成果の合計÷件数の合計を計算する。
- 層別の率を出す:疑わしい属性(新規/既存、年齢層、店舗、端末、期間など)で分けて、同じ計算をする。
- 構成比を出す:各案の件数のうち、各層が何%を占めるかを出す。ここが案どうしで大きく違えば要注意。
- 向きを比べる:全体の差の向きと、各層の差の向きが同じか確認する。逆なら逆転が起きている。
- 小さな層の件数を見る:この例の「新規×B」は20件中3件です。件数が少ない層の率は、数件の違いで大きく動く。
- 層の選び方を説明できるか確認する:次の節の表で、どちらを重視すべきか判断する。
層は1つに決め打ちせず、2〜3種類の属性で試すのが現実的です。ただし、分け方を変えながら都合のよい結果を探すと、別の誤りを招きます。分ける前に「この属性は成果に影響しそうか」「案の割り当てに偏りを生みそうか」を言葉にしておきます。
全体と層別、どちらを信じるか
逆転したとき、層別が常に正しいわけではありません。層の性質で判断します。
- 案を選ぶ前から決まっていて、成果にも影響する、例:既存/新規、年齢層、地域、基本の見方:層別の比較を重視する。割り当ての偏りが全体をゆがめている可能性が高い
- 案の結果として生じる、例:件名を見て開封した人だけの集計、案で変わった来店頻度、基本の見方:層別にすると、比べたい効果自体を削る恐れがある。全体の比較も残して検討する
- 判断がつかない、例:何で分けたか経緯が不明、基本の見方:結論を保留し、割り当てがどう決まったかを確認する
最も確実なのは、案をランダムに割り当てて偏りをなくすことです。すでに集計済みのデータでは、割り当ての経緯を確認することが人の仕事になります。
pandasとAIに任せる範囲
層別集計は、pandasのgroupbyで作れます。公式ユーザーガイドでは、データをグループに分け、各グループに関数を適用し、結果を結合する流れが説明されています(pandas User Guide: Group by)。as_index=Falseでグループ名を列のまま残せる点も同ページで確認できます。
下のコードは上の架空表を再現する例です。この記事の作成時点では実行しておらず、動作確認は読者の環境で行ってください。
import pandas as pd
df = pd.DataFrame({
"案": ["A", "A", "B", "B"],
"層": ["既存", "新規", "既存", "新規"],
"送信": [20, 80, 80, 20],
"クリック": [9, 16, 32, 3],
})
# 1. 全体:合計してから率を出す
total = df.groupby("案", as_index=False)[["送信", "クリック"]].sum()
total["率"] = total["クリック"] / total["送信"]
# 2. 層別の率
df["率"] = df["クリック"] / df["送信"]
# 3. 構成比(各案の送信数に占める層の割合)
df["構成比"] = df["送信"] / df.groupby("案")["送信"].transform("sum")
print(total)
print(df)手元の元データが1行1件の形式なら、groupby(["案","層"]).agg(送信=("クリック","size"), クリック=("クリック","sum"))で同じ形の表にできます。
AIに頼む場合の依頼文
AIには、コードの下書きと結果の読み取りを任せられます。暗算はミスの元なので、計算はコードで実行させ、出力を自分でも検算します。顧客名や個人を特定できる列は渡さず、層と件数だけの集計表か、説明用に置き換えた数字にします。
次の集計表で、全体の比較と層別の比較の結論が逆転していないか確認したいです。
【表】(案、層、件数、成果数。架空または匿名化済み)
(ここに表を貼る)
お願いしたいこと:
1. 案ごとの全体の率を「成果合計÷件数合計」で計算するpandasコード
2. 層別の率と、各案の層の構成比を出すコード
3. 全体と層別で差の向きが逆かどうかの判定
4. 逆転する場合、構成比の違いでどう説明できるか
計算は必ずコードを示して行い、暗算の数字だけで答えないでください。人が確認する点は3つです。(1)AIが出した率を、電卓か自分の実行結果で検算する。(2)その層が案の割り当てに関係していたか、割り当ての経緯を確認する。(3)件数が少ない層を、結論の根拠にしていないか見る。AIは構成比のずれを説明できますが、データに載っていない「なぜその割り当てになったか」は、現場の人にしか答えられません。

