論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
PPDACサイクル
PPDAC Cycle
リテラシー

🔖 キーワード索引

各語の説明がある章へのリンク:

Problem の書き方(答えが数字で書ける問い)Plan を先に書くPPDAC を 1 周(高齢化率と保健医療費)2 周目の Problem・Plan検出力(47 県で見つけられる相関)564 行と年度の絞り込み1 県の影響と信頼区間Data 段階の欠損・外れ値の点検段階別の失敗と対策PPDAC は螺旋CRISP-DM・OSEMN との違い人口減少の完全例

PPDAC サイクルは Problem→Plan→Data→Analysis→Conclusion の 5 段階で統計的な探究を回す枠組みで、 MacKay と Oldford(2000)が整理し、 ニュージーランドの統計教育で広まった。 このページでは SSDSE-B-2026 を使い、 「高齢化率が高い県ほど、 二人以上の世帯の保健医療費(県庁所在市の家計調査の値)は多いのか」という問いで PPDAC を 2 周回す。

💡 30秒で分かる結論 — PPDACサイクル

🍰 まずはやさしく

データ分析の地図のようなものです。

正しい手順で結論を出すために使います。

部活の課題をデータで解決する時に役立ちます。

分析の5つのステップについて読みましょう。

Problem→Plan→Data→Analysis→Conclusion の分析プロセス

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

🍰 まずはやさしく

世界中で使われている共通のルールです。

分析の流れを整理するために使います。

レポート作成で迷った時に役立ちます。

この仕組みの使い道を詳しく解説します。

日本の高校情報・統計教育、 ニュージーランドの統計カリキュラム、 国際統計学会(ISLP)でも採用される枠組み。 「問いから結論まで」を体系化する。

「データ分析コンペで何から手を付けたらいいか分からない」「分析が手戻り続き」 — そんなときに立ち返るフレーム。 統計検定2級・データサイエンス検定でも頻出。

Problem の書き方 — 悪い問いを良い問いに直す 3 例

PPDAC でいちばん差がつくのは最初の P です。実例で直し方を見ます。

よくある問い 何が困るか 直したもの
地方は元気がない? 「元気」が測れない。どの指標でも答えが作れてしまう 2013〜2023 年で、人口が減った県の数はいくつか
教育にお金をかけると学力が上がる? 因果を問うているのに、手元は横断データだけ 教育費と高校進学率の間に、県レベルで相関はあるか
どの県が住みやすい? 誰にとってかが無い。合成指標の重みで結論が変わる 子育て世帯にとっての保育所定員/待機児童数はどう分布するか

直した問いに共通するのは、「答えが数字で書ける」「答えが 1 つに決まる」の 2 点です。 そして 2 例目のように、問いを弱めることも正しい直し方です。 横断データで因果は言えないのだから、 「相関はあるか」まで問いを下げておくほうが、結論で嘘をつかずに済みます。

このページの例は、 🧮 で PPDAC を 3 周する。 1 周目で問いと答えの形を決め、 2 周目で支出の大きさ・年度・検出力・1 県の影響を確かめ、 3 周目で年度を変えて結論が崩れないかを試す。 どの周も、 前の周の Conclusion に書いた「言えないこと」が出発点になっている。

🎨 直感で掴む — PPDACサイクルとは何者か

🍰 まずはやさしく

料理のレシピのようなものです。

失敗せずに分析を進めるために使います。

買い物で予算と品物を決める感覚に似ています。

具体例を使って直感的に理解しましょう。

料理に例えると:

  • P (Problem):何を作りたい?(カレー)
  • P (Plan):レシピを決める(材料・手順)
  • D (Data):材料を集める
  • A (Analysis):調理する
  • C (Conclusion):味見・改善 → 次へ

料理が下手な人は P を飛ばして適当に材料を切り始めるが、 上手な人はゴールを決めてから動く。

📐 数式・定義

🍰 まずはやさしく

5つの段階を回すサイクルです。

問いから結論までを体系化するために使います。

スマホの利用時間を調べる時に役立ちます。

それぞれの段階の意味と流れを定義します。

PPDACサイクル(PPDAC Cycle):Problem→Plan→Data→Analysis→Conclusion の分析プロセス

【PPDAC サイクル】
$$ \text{P} \to \text{P} \to \text{D} \to \text{A} \to \text{C} \to (\text{ふたたび P へ}) $$
5段階を順に進み、 結論から新たな問題定義に戻る — スパイラル的に深掘り。

🔬 数式・定義を「言葉」で読み解く

📐 の矢印の 1 本 1 本が、 どの段階で何を決めるかを表す。 大事なのは最後の「ふたたび P へ」で、 C で書いた「言えないこと」が次の周の Problem になる。 このページの例では、 1 周目の C(高齢化率と保健医療費は r = −0.49 の負の相関)から、 2 周目の P(消費支出全体の大きさをそろえても負の関係は残るか)が生まれている。 さらに 3 周目では同じ問いを年度を変えて確かめ、 r が 2013 年度の −0.013 から 2023 年度の −0.490 まで揺れることが分かって、 C の書き方をもう一度直している。 矢印を 1 周するたびに、 問いは狭く、 結論は慎重になっていく。

🔬 記号・用語の読み解き

記号意味
Problem何を明らかにしたいか — 質問を具体化
Plan何を測る/どう集める/必要なサンプルは
Data実データ収集・整形・クリーニング
Analysis可視化・統計・モデリング
Conclusion解釈・報告・次への問い

🧮 SSDSE-B 実値で計算してみる

SSDSE-B-2026(47都道府県・2023 年・112 項目)を題材に、 PPDACサイクル に関係する変数を実値で確認します。 とくに東京・大阪・沖縄・秋田 など特徴ある県を比較すると、 用語の重みが体感できます。

都道府県総人口(千人)高齢化率(%)TFR転入率(‰)
東京14,08622.80.9928.88
大阪8,76327.71.1918.20
沖縄1,46823.81.6017.99
秋田91439.11.1010.94
全国 (人口は合計、 TFR は 47 県の単純平均)124,35329.11.2917.93

これらの値を PPDACサイクル の観点で読み解くと、 都道府県間の格差・特徴・関係性が浮かび上がります。 具体的な計算手順は次の「🐍 Python 実装」セクションで実演します。

例:「高齢化率が高い県ほど保健医療費は多いか?」(P)→ SSDSEから抽出計画(P)→ データ取得(D)→ 散布図・相関係数(A)→ 「r=−0.49、 予想と逆の負の相関」(C)→ 「消費支出に占める割合で見たら?」(次のP)。 下の「PPDAC を 1 周してみる」で実際に回す。

🧮 数式に値を入れて手で計算する: PPDAC 5 段階の工数

合成データで PPDAC サイクルの工数配分を計算する。

Step 1: 段階別工数 [人日]

段階工数比率
Problem50.10
Plan100.20
Data150.30
Analysis120.24
Conclusion80.16

Step 2: 集計

合計 = 5+10+15+12+8 = 50 人日 Data 最大 30%、 Plan + Data で 50%

🐍 Python で再現

1
2
3
4
5
import numpy as np
effort = np.array([5, 10, 15, 12, 8])
ratio = effort / effort.sum()
print(f"合計: {effort.sum()}")
print(f"比率: {ratio}")

📤 実行結果

合計: 50 比率: [0.1 0.2 0.3 0.24 0.16]

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

PPDAC を 1 周してみる — 「高齢化率が高い県ほど医療費は多いのか」

サイクルの説明を読むだけでは身につきません。実際に 1 周してみます。 題材は誰もが「そうだろう」と思う問い、 「高齢化率が高い県ほど、世帯の保健医療費は多いのではないか」です。 結論から言うと、この予想は裏切られます。そこがこの例のいちばんの学びどころです。

P — Problem(問いを、答えられる形にする)

「高齢化は医療費を押し上げるか」は大きすぎて測れません。 誰の・いつの・どの数字かまで落とします。 → 「2023 年度の 47 都道府県で、65 歳以上人口比率と 二人以上の世帯の月額保健医療費の間に相関はあるか」。 ここまで書けて初めて、次の Plan が決まります。

P — Plan(どう測るかを先に決める)

高齢化率は A1303 ÷ A1101 で作る。医療費は L322106 をそのまま使う。 関係は相関係数と回帰直線で見る。 分析を始める前に決めておくのが肝心で、 結果を見てから指標を選び直すと、都合のよい数字を拾うだけになります。

D — Data(データの素性を確かめる)

47 行。高齢化率は 22.8%〜39.1%(平均 31.6%)、 保健医療費は 11,052〜21,000 円(平均 14,423 円)。 欠損はありません。ここで範囲を見ておくと、あとで外れ値に驚かずに済みます。

A — Analysis(測る)

相関係数は r = −0.490(p = 0.0005)。 予想と逆で、しかもはっきり負です。 回帰直線は「保健医療費 = −315 × 高齢化率 + 24,361」。 高齢化率が 1 ポイント高い県ほど、世帯の医療費支出は月 315 円ほど少ないという関係です。

県 高齢化率 保健医療費(月)
秋田県(高齢化率 1 位)39.1%12,305 円
東京都(高齢化率 47 位)22.8%18,166 円

C — Conclusion(何が言えて、何が言えないか)

言えるのは「高齢化率が高い県ほど、二人以上世帯の保健医療費支出は少ない傾向がある」までです。 「高齢化しても医療費はかからない」とは言えません。理由は 3 つあります。

そして PPDAC はここで終わりません。 「消費支出に占める割合で見たらどうか」「世帯人員で割ったらどうか」という 次の Problem が生まれます。これが「サイクル」と呼ばれる理由です。 予想が外れたときこそ、いちばん良い次の問いが立ちます。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) L322106(保健医療費(二人以上の世帯)) 北海道 5,092,000 1,681,000 15,491 東京都 14,086,000 3,205,000 18,166 沖縄県 1,468,000 350,000 11,686 …(全 47 行)
 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
# ── PPDAC を 1 周する:問いを立て、データを見て、結論を書くまで ──
import pandas as pd
from scipy import stats

# Data: SSDSE-B-2026 の 2023 年度・47 都道府県
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()
df['高齢化率'] = df['A1303'] / df['A1101'] * 100          # 65歳以上人口 ÷ 総人口
df['保健医療費'] = df['L322106'].astype(float)             # 二人以上の世帯・月額

# Analysis: まず散らばりを見てから関係を測る
print(f"高齢化率   平均 {df['高齢化率'].mean():.1f}%  "
      f"範囲 {df['高齢化率'].min():.1f}〜{df['高齢化率'].max():.1f}")
print(f"保健医療費 平均 {df['保健医療費'].mean():,.0f} 円  "
      f"範囲 {df['保健医療費'].min():,.0f}〜{df['保健医療費'].max():,.0f}")

r, p = stats.pearsonr(df['高齢化率'], df['保健医療費'])
slope, intercept, _, _, _ = stats.linregress(df['高齢化率'], df['保健医療費'])
print(f'相関係数 r = {r:.3f}  (p = {p:.4f})')
print(f'回帰直線: 保健医療費 = {slope:.0f} × 高齢化率 + {intercept:,.0f}')

# Conclusion: 両端を名指しで見て、想定と違うことを確かめる
for pref in ['秋田県', '東京都']:
    row = df[df['Prefecture'] == pref].iloc[0]
    print(f"{pref}: 高齢化率 {row['高齢化率']:.1f}%  保健医療費 {row['保健医療費']:,.0f} 円")

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

高齢化率 平均 31.6% 範囲 22.8〜39.1 保健医療費 平均 14,423 円 範囲 11,052〜21,000 相関係数 r = -0.490 (p = 0.0005) 回帰直線: 保健医療費 = -315 × 高齢化率 + 24,361 秋田県: 高齢化率 39.1% 保健医療費 12,305 円 東京都: 高齢化率 22.8% 保健医療費 18,166 円

💬 読み方:上の A と C で書いた数字が、そのまま出ます。p = 0.0005 は「偶然ではない」としか言っていない点に注意してください。負の相関が「本物」であることと、その原因が何かは別の問題です。PPDAC の C は、数字が言っていないことを書き分ける場所でもあります。

PPDAC を 2 周目へ — C で生まれた問いを、段階ごとに実データで確かめる

1 周目の Conclusion は「高齢化率が高い県ほど、二人以上世帯の保健医療費支出は少ない傾向がある(2023 年度、r = −0.490)」でした。そして「所得(支出全体の大きさ)の効果が混ざっているのでは」「この結論はどこまで確かか」という次の問いが残りました。2 周目では、この問いを P → P → D → A → C の順に 1 つずつ確かめます。1 周目と同じ SSDSE-B-2026 の列だけを使います。

2 周目の Problem — 支出全体の大きさをそろえても、負の関係は残るか

1 周目の C で挙げた理由の 1 つは「高齢化率が低い県は都市部で支出全体が大きい」でした。これが本当なら、保健医療費を消費支出に占める割合に直すか、回帰で消費支出をそろえると、負の関係は弱まるか消えるはずです。問いを「2023 年度の 47 都道府県で、消費支出(L3221)の違いを考慮しても、高齢化率と保健医療費の負の関係は残るか」と書き直して確かめます。

🎯 このコードでやること:2023 年度の 47 都道府県で、高齢化率・保健医療費・消費支出の 3 組の相関と、保健医療費を消費支出で割った割合と高齢化率の相関を出す。さらに「保健医療費 ~ 高齢化率 + 消費支出」の回帰で、消費支出をそろえたときの高齢化率の係数を求める。

📥 入力例 SSDSE-B-2026(2023 年度の 47 行)から使う列 都道府県 A1101(総人口) A1303(65歳以上人口) L3221(消費支出) L322106(保健医療費) 秋田県 914,000 357,000 272,086 12,305 東京都 14,086,000 3,205,000 341,320 18,166 沖縄県 1,468,000 350,000 251,222 11,686
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
from scipy import stats
import statsmodels.formula.api as smf

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023].copy()
d['高齢化率'] = d['A1303'] / d['A1101'] * 100
d['保健医療費'] = d['L322106'].astype(float)            # 二人以上の世帯・月額(円)
d['消費支出'] = d['L3221'].astype(float)                 # 同じ世帯の消費支出の合計(円)
d['医療費割合'] = d['保健医療費'] / d['消費支出'] * 100   # 消費支出に占める保健医療費(%)

for a, b in [('高齢化率', '保健医療費'), ('高齢化率', '消費支出'), ('消費支出', '保健医療費'), ('高齢化率', '医療費割合')]:
    r, p = stats.pearsonr(d[a], d[b])
    print(f'r({a}, {b}) = {r:+.3f}  (p = {p:.4f})')

m = smf.ols('保健医療費 ~ 高齢化率 + 消費支出', data=d).fit()
print(f'消費支出をそろえた回帰: 高齢化率の係数 {m.params["高齢化率"]:.0f} 円/ポイント (p = {m.pvalues["高齢化率"]:.4f})'
      f'  消費支出の係数 {m.params["消費支出"]:.4f}')
print(f'医療費割合 平均 {d["医療費割合"].mean():.2f}%  範囲 {d["医療費割合"].min():.2f}〜{d["医療費割合"].max():.2f}%')
📤 実行例(実測) r(高齢化率, 保健医療費) = -0.490 (p = 0.0005) r(高齢化率, 消費支出) = -0.306 (p = 0.0363) r(消費支出, 保健医療費) = +0.603 (p = 0.0000) r(高齢化率, 医療費割合) = -0.392 (p = 0.0065) 消費支出をそろえた回帰: 高齢化率の係数 -216 円/ポイント (p = 0.0056) 消費支出の係数 0.0444 医療費割合 平均 4.87% 範囲 3.69〜6.10%

💬 消費支出と保健医療費の相関は +0.603 で、支出全体が大きい県ほど医療費も多いのは確かです。高齢化率と消費支出も r = −0.306 と負の関係があるので、1 周目の −0.490 の一部は「支出全体の大きさ」の経路を通っています。しかし割合(平均 4.87%、3.69〜6.10%)に直しても r = −0.392 と負の関係ははっきり残り、消費支出をそろえた回帰でも高齢化率の係数は −216 円/ポイント(p = 0.0056)です。1 周目の −315 円/ポイントのおよそ 3 分の 2 が残る計算で、「所得の違いだけで説明できる」という仮説は、この 2 つの列の範囲では支持されません。残る説明の候補(高齢者のみの世帯が多いと世帯あたりの支出が小さくなる、医療費の公的負担の違いなど)は SSDSE-B-2026 の列では測れないので、次の周では別のデータ(世帯人員や医療費の総額)を探す Plan が要ります。

2 周目の Plan — なぜ「年度を先に決める」のか

1 周目の Plan で「分析を始める前に決めておくのが肝心」と書きました。それがどれほど効くかを、年度の選び方で確かめます。SSDSE-B-2026 には 2012〜2023 年度の 12 年分があるので、同じ相関を年度ごとに計算し、12 年度をまとめた 564 行の相関とも比べます。

🎯 このコードでやること:2012〜2023 年度のそれぞれで、47 都道府県の高齢化率と保健医療費のピアソン相関と p 値を計算し、p < 0.05 になる年度を数える。12 年度の 564 行をまとめた相関も出す。

📥 入力例 SSDSE-B-2026 の全 564 行(2012〜2023 年度 × 47 都道府県): A1101・A1303・L322106
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd
from scipy import stats

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df['高齢化率'] = df['A1303'] / df['A1101'] * 100
df['保健医療費'] = df['L322106'].astype(float)

rows = []
for y, g in df.groupby('SSDSE-B-2026'):                  # 年度ごとに 47 都道府県で相関
    r, p = stats.pearsonr(g['高齢化率'], g['保健医療費'])
    rows.append((y, r, p))
    print(f'{y} 年度: r = {r:+.3f}  p = {p:.4f}  {"*" if p < 0.05 else ""}')
sig = [y for y, r, p in rows if p < 0.05]
print(f'p < 0.05 の年度: {len(sig)} / {len(rows)}({min(sig)}〜{max(sig)} 年度)')
r_all, p_all = stats.pearsonr(df['高齢化率'], df['保健医療費'])
print(f'12 年度の 564 行をまとめて: r = {r_all:+.3f}  p = {p_all:.4f}')
📤 実行例(実測) 2012 年度: r = -0.095 p = 0.5231 2013 年度: r = -0.013 p = 0.9334 2014 年度: r = -0.251 p = 0.0891 2015 年度: r = -0.143 p = 0.3368 2016 年度: r = -0.126 p = 0.3974 2017 年度: r = -0.320 p = 0.0286 * 2018 年度: r = -0.360 p = 0.0129 * 2019 年度: r = -0.365 p = 0.0116 * 2020 年度: r = -0.327 p = 0.0249 * 2021 年度: r = -0.406 p = 0.0047 * 2022 年度: r = -0.468 p = 0.0009 * 2023 年度: r = -0.490 p = 0.0005 * p < 0.05 の年度: 7 / 12(2017〜2023 年度) 12 年度の 564 行をまとめて: r = -0.024 p = 0.5692

💬 相関は 2012 年度 −0.095、2013 年度 −0.013 とほぼ 0 から、2023 年度 −0.490 まで年度によって大きく違い、p < 0.05 になるのは 12 年度中 7 年度(2017〜2023 年度)だけです。最新の 2023 年度は 12 年度の中でたまたま最も強い負の相関でした。もし「いちばん結果のはっきりした年度」を後から選んで報告すれば、それだけで結論を強く見せられてしまいます。1 周目で 2023 年度を使ったのは「最新年度で見る」と先に決めていたからで、その場合でも「2017 年度以降は一貫して負、それ以前ははっきりしない」という年度ごとの結果を添えるのが誠実な C です。12 年度の 564 行をまとめると r = −0.024(p = 0.5692)で関係は消えてしまいますが、これは次の Data の段で理由が分かります。

左: 2012〜2023 年度それぞれの高齢化率と保健医療費の相関と 95% 区間。2012 年度 −0.095 から 2023 年度 −0.490 へ強まり、2016 年度までの区間は 0 をまたぐ。破線は 564 行をまとめた r = −0.024。右: 564 行の散布図に 2012 年度と 2023 年度の点と回帰直線(どちらも右下がり)、564 行全体の回帰直線(ほぼ水平)を重ねた図

図の読み方: 左は年度ごとの相関と Fisher の z 変換による 95% 区間です。2016 年度までは区間が 0 をまたぎ、2017 年度以降は区間全体が負の側にあります。区間の幅はどの年度も ±0.25 前後あり、47 県の相関 1 つには大きな不確かさがあることも分かります。右は 564 行の散布図で、2012 年度(緑)と 2023 年度(青)はそれぞれ右下がりの直線なのに、2023 年度の点の雲は 2012 年度より右(高齢化率が高い)かつ上(医療費が多い)にずれています。この「年度ごとのずれ」が右上がりに並ぶため、564 行を 1 本の直線で当てると、県の間の右下がりを打ち消してほぼ水平(橙の破線)になります。図の作成スクリプトは code/glossary_figs/ppdac-cycle.py。

2 周目の Plan(続き)— 47 県でどの強さの相関なら見つけられるか

年度によって p < 0.05 になったりならなかったりするのは、相関が年ごとに本当に変わっているからとは限りません。47 県という件数そのものが、中くらいの相関を見つけるには小さい可能性があります。Plan の段で「この件数で、どの強さの関係なら見つけられるか(検出力)」を見積もっておくと、結果の読み方が変わります。

🎯 このコードでやること:47 件の相関の検定(有意水準 5%、両側)で、本当の相関の強さ |ρ| が 0.20〜0.49 のときに p < 0.05 になる確率(検出力)と、検出力 80% に必要な件数を Fisher の z 変換で近似計算する。

📥 入力例 件数 n = 47(都道府県の数)、|ρ| = 0.20, 0.30, 0.33, 0.40, 0.49(2 周目の年度ごとの r の範囲から選んだ値)
1
2
3
4
5
6
7
8
9
10
import numpy as np
from scipy import stats

# 47 県の相関の検定で、本当の相関が |ρ| のときに p < 0.05 になる確率(Fisher の z 変換による近似)
n = 47
for rho in [0.2, 0.3, 0.33, 0.4, 0.49]:
    z = np.arctanh(rho) * np.sqrt(n - 3)
    power = stats.norm.cdf(z - 1.96) + stats.norm.cdf(-z - 1.96)
    n80 = (1.96 + 0.8416) ** 2 / np.arctanh(rho) ** 2 + 3          # 検出力 80% に必要な件数
    print(f'|ρ| = {rho:.2f}: 検出力 {power:.3f}   検出力 80% に必要な件数 {np.ceil(n80):.0f}')
📤 実行例(実測) |ρ| = 0.20: 検出力 0.270 検出力 80% に必要な件数 194 |ρ| = 0.30: 検出力 0.537 検出力 80% に必要な件数 85 |ρ| = 0.33: 検出力 0.623 検出力 80% に必要な件数 70 |ρ| = 0.40: 検出力 0.802 検出力 80% に必要な件数 47 |ρ| = 0.49: 検出力 0.945 検出力 80% に必要な件数 31

💬 本当の相関が |ρ| = 0.33(2017〜2020 年度の r に近い強さ)なら、47 県で p < 0.05 になる確率は 0.623 しかなく、10 回調べれば 4 回近くは「有意でない」と出ます。|ρ| = 0.49(2023 年度の r)なら 0.945 です。検出力 80% を確保するには、|ρ| = 0.30 で 85 件、|ρ| = 0.20 なら 194 件が必要で、都道府県の数 47 はこれに届きません。つまり「2016 年度以前は有意でない」ことは「その年は関係がなかった」ことの証拠にはならず、件数が足りずに見逃している可能性があります。Plan の段で検出力を見積もっておけば、「有意でない年度」を「関係なし」と書いてしまう C の誤りを防げます。都道府県より細かい単位(市区町村)のデータを使うか、年度をまたいで年度の効果を分けたモデルで推定するのが、件数を増やす方法です。

2 周目の Data — 564 行はなぜ打ち消し合い、どの列が揺れやすいか

Data の段では「データの素性」を確かめます。ここでは 2 つを数字で見ます。1 つは、564 行をまとめると相関が消える理由を、年度の平均の動きで確かめること。もう 1 つは、列ごとに年度間の揺れの大きさを比べることです。保健医療費は家計調査(標本調査)にもとづく値なので、人口のような全数の数字よりも年ごとに大きく揺れることが予想されます。

🎯 このコードでやること:県ごとに年度の昇順に並べ直し、総人口・高齢化率・消費支出・保健医療費の前年度比の変化(絶対値)の中央値と 90% 点を比べる。年度ごとの全国平均(47 県の単純平均)の推移と、その 12 点どうしの相関も出す。

📥 入力例 SSDSE-B-2026 の全 564 行。CSV は新しい年度が先に並ぶので、Code と年度で並べ替えてから前年度比を計算する
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np
import pandas as pd
from scipy import stats

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df['高齢化率'] = df['A1303'] / df['A1101'] * 100
df = df.sort_values(['Code', 'SSDSE-B-2026'])            # 県ごとに年度の昇順(CSV は新しい年度が先)

# 前年度からの変化率(絶対値)の中央値: どの列が年ごとに大きく揺れるか
for col, name in [('A1101', '総人口'), ('高齢化率', '高齢化率'), ('L3221', '消費支出'), ('L322106', '保健医療費')]:
    ch = df.groupby('Code')[col].pct_change().abs() * 100
    print(f'{name:<6} 前年度比の変化(絶対値)の中央値 {ch.median():5.2f}%  90% 点 {ch.quantile(0.9):5.2f}%')

# 年度の平均と県の平均に分けると、564 行の相関がほぼ 0 になる理由が見える
yr = df.groupby('SSDSE-B-2026')[['高齢化率', 'L322106']].mean()
print(f'全国平均の推移: 高齢化率 {yr.iloc[0, 0]:.1f}% → {yr.iloc[-1, 0]:.1f}%  保健医療費 {yr.iloc[0, 1]:,.0f} → {yr.iloc[-1, 1]:,.0f} 円')
r_time, _ = stats.pearsonr(yr['高齢化率'], yr['L322106'])
print(f'年度の平均どうし(12 点)の相関 r = {r_time:+.3f}')
📤 実行例(実測) 総人口 前年度比の変化(絶対値)の中央値 0.59% 90% 点 1.10% 高齢化率 前年度比の変化(絶対値)の中央値 1.93% 90% 点 3.77% 消費支出 前年度比の変化(絶対値)の中央値 4.12% 90% 点 9.18% 保健医療費 前年度比の変化(絶対値)の中央値 7.23% 90% 点 21.01% 全国平均の推移: 高齢化率 25.6% → 31.6% 保健医療費 12,258 → 14,423 円 年度の平均どうし(12 点)の相関 r = +0.881

💬 前年度比の変化の中央値は、総人口 0.59%・高齢化率 1.93% に対して、消費支出 4.12%・保健医療費 7.23% と大きく、保健医療費は 1 割の県・年度で 21.01% 以上も動きます。家計の支出は標本の世帯から推計した値なので、1 年ごとの値には標本による揺れが大きく含まれます。年度ごとの相関が −0.013〜−0.490 と大きく変わったのも、この揺れで説明できる部分があります。また、全国平均で見ると 12 年間に高齢化率は 25.6% → 31.6%、保健医療費は 12,258 → 14,423 円とどちらも上がっており、年度の平均どうし 12 点の相関は +0.881 です。県の間では負、年度の間では正という逆向きの関係が 564 行に混ざるので、まとめた相関はほぼ 0 になります。Data の段で「行が何を 1 単位としているか(県か、県 × 年度か)」を確かめずに全行を使うと、このような打ち消しに気づけません。

2 周目の Analysis — 1 県で結論が変わらないか、区間はどれくらいか

Analysis の段では、1 つの数字で終わらせず、結論の頑健さを確かめます。2023 年度の 47 県で、順位にもとづくスピアマンの相関、1 県ずつ除いたときの相関の範囲、県を復元抽出するブートストラップの 95% 区間を計算します。

🎯 このコードでやること:2023 年度の高齢化率と保健医療費で、ピアソン r とスピアマン ρ、1 県ずつ除いたときの r の最小・最大(どの県を除いたときか)、ブートストラップ 2,000 回(seed 0)の 95% 区間と、r が 0 以上になった割合を出す。

📥 入力例 SSDSE-B-2026(2023 年度の 47 行): A1101・A1303・L322106
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
import pandas as pd
from scipy import stats

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)
x = d['A1303'] / d['A1101'] * 100
y = d['L322106'].astype(float)

print(f'ピアソン r = {stats.pearsonr(x, y)[0]:+.3f}   スピアマン ρ = {stats.spearmanr(x, y)[0]:+.3f}')
loo = np.array([stats.pearsonr(x.drop(i), y.drop(i))[0] for i in range(len(d))])   # 1 県ずつ除く
for i in [loo.argmin(), loo.argmax()]:
    print(f'{d.loc[i, "Prefecture"]}を除くと r = {loo[i]:+.3f}')
rng = np.random.default_rng(0)
boot = []
for _ in range(2000):                                    # 県を復元抽出するブートストラップ
    idx = rng.integers(0, len(d), len(d))
    boot.append(stats.pearsonr(x.iloc[idx], y.iloc[idx])[0])
lo, hi = np.percentile(boot, [2.5, 97.5])
print(f'ブートストラップ 95% 区間 [{lo:+.3f}, {hi:+.3f}]  0 以上になった割合 {np.mean(np.array(boot) >= 0):.3%}')
📤 実行例(実測) ピアソン r = -0.490 スピアマン ρ = -0.468 沖縄県を除くと r = -0.603 東京都を除くと r = -0.437 ブートストラップ 95% 区間 [-0.715, -0.185] 0 以上になった割合 0.350%

💬 スピアマン ρ = −0.468 はピアソン r = −0.490 とほぼ同じで、少数の極端な値だけで作られた相関ではありません。1 県ずつ除くと r は −0.603(沖縄県を除いたとき)から −0.437(東京都を除いたとき)の間で動き、どの 1 県を除いても負の関係は残ります。沖縄県は高齢化率 23.8% と低いのに保健医療費 11,686 円と少なく、全体の傾向から外れているので、除くと負の関係が強まります。東京都は高齢化率 22.8% で最も低く保健医療費 18,166 円と多いので、除くと少し弱まります。ブートストラップの 95% 区間は [−0.715, −0.185] で、0 以上になったのは 2,000 回中 0.350%(7 回)でした。2023 年度の中では負の関係は頑健ですが、区間の幅は 0.53 と広く、「r ≈ −0.5」ではなく「−0.2 〜 −0.7 のどこか」と読むのが適切です。

Analysis の中身を手で確かめる — 5 県で相関係数を計算する

1 周目・2 周目で何度も出てきた相関係数 $r = \dfrac{\sum (x_i-\bar x)(y_i-\bar y)}{\sqrt{\sum (x_i-\bar x)^2 \sum (y_i-\bar y)^2}}$ を、2023 年度の 5 県の値(高齢化率 $x$ %、保健医療費 $y$ 千円/月、小数第 1 位に丸めたもの)で手計算します。

県$x$$y$$x-\bar x$$y-\bar y$積
秋田県39.112.3+9.64−2.56−24.678
東京都22.818.2−6.66+3.34−22.244
沖縄県23.811.7−5.66−3.16+17.886
愛媛県34.211.1+4.74−3.76−17.822
埼玉県27.421.0−2.06+6.14−12.648
Step計算値
1. 平均$\bar x = 147.3 / 5$、$\bar y = 74.3 / 5$29.46、14.86
2. 偏差の積の和−24.678 − 22.244 + 17.886 − 17.822 − 12.648−59.508
3. 偏差の二乗和$\sum(x-\bar x)^2 = 92.93 + 44.36 + 32.04 + 22.47 + 4.24$、$\sum(y-\bar y)^2 = 6.55 + 11.16 + 9.99 + 14.14 + 37.70$196.032、79.532
4. 分母$\sqrt{196.032 \times 79.532} = \sqrt{15{,}590.8}$124.863
5. 相関係数−59.508 ÷ 124.863−0.4766

5 県のうち 4 県の積が負で、沖縄県だけが正(高齢化率も医療費も平均より低い)です。これは 2 周目の Analysis で「沖縄県を除くと負の関係が強まる」と出たことと同じ構造です。

🎯 このコードでやること:手計算と同じ 5 県の丸めた値で Step 1〜5 を numpy で再現し、scipy.stats.pearsonr と比べる。最後に丸める前の SSDSE-B-2026 の値で同じ 5 県の r を計算する。

📥 入力例 秋田県・東京都・沖縄県・愛媛県・埼玉県の 2023 年度の値 高齢化率(%): 39.1, 22.8, 23.8, 34.2, 27.4 保健医療費(千円/月): 12.3, 18.2, 11.7, 11.1, 21.0(元の値は 12,305・18,166・11,686・11,052・21,000 円)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
import pandas as pd
from scipy import stats

# 手計算と同じ 5 県の値(高齢化率 %、保健医療費 千円/月、小数第 1 位に丸めたもの)
names = ['秋田県', '東京都', '沖縄県', '愛媛県', '埼玉県']
x = np.array([39.1, 22.8, 23.8, 34.2, 27.4])
y = np.array([12.3, 18.2, 11.7, 11.1, 21.0])
dx, dy = x - x.mean(), y - y.mean()
print(f'平均 x̄ = {x.mean():.2f}  ȳ = {y.mean():.2f}')
print('偏差の積:', np.round(dx * dy, 3))
print(f'Σ偏差の積 = {np.sum(dx * dy):.3f}  Σdx² = {np.sum(dx**2):.3f}  Σdy² = {np.sum(dy**2):.3f}')
print(f'r(式どおり)= {np.sum(dx * dy) / np.sqrt(np.sum(dx**2) * np.sum(dy**2)):.4f}'
      f'   scipy.stats.pearsonr = {stats.pearsonr(x, y)[0]:.4f}')

# 丸める前の SSDSE-B-2026(2023 年度)の値で同じ 5 県
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[(df['SSDSE-B-2026'] == 2023) & df['Prefecture'].isin(names)].set_index('Prefecture').loc[names]
r5 = stats.pearsonr(d['A1303'] / d['A1101'] * 100, d['L322106'])[0]
print(f'丸める前の値で 5 県: r = {r5:.4f}')
📤 実行例(実測) 平均 x̄ = 29.46 ȳ = 14.86 偏差の積: [-24.678 -22.244 17.886 -17.822 -12.648] Σ偏差の積 = -59.508 Σdx² = 196.032 Σdy² = 79.532 r(式どおり)= -0.4766 scipy.stats.pearsonr = -0.4766 丸める前の値で 5 県: r = -0.4745

💬 平均 29.46・14.86、偏差の積の和 −59.508、二乗和 196.032・79.532、r = −0.4766 は Step 1〜5 と一致し、scipy.stats.pearsonr の −0.4766 とも同じです。丸める前の値で計算しても −0.4745 で、小数第 1 位への丸めは r をほとんど変えません。ただし、この 5 県は保健医療費が最も少ない愛媛県と最も多い埼玉県を含む「選んだ 5 県」なので、47 県の r = −0.490 に近いのはたまたまです。手計算で式の意味を確かめたら、結論には必ず全 47 県の値を使います。

2 周目の Conclusion — 1 周目の結論をどう書き直すか

段階2 周目で確かめたこと実データの結果結論への反映
Problem支出全体の大きさをそろえても負の関係は残るか割合でも r = −0.392、消費支出をそろえた係数 −216 円/ポイント(p = 0.0056)「所得の違いだけでは説明できない」と書ける
Plan年度を変えると結論は変わるかr は −0.013〜−0.490、p < 0.05 は 12 年度中 7 年度(2017〜2023 年度)「2017 年度以降は一貫して負」と期間を明記する
Data564 行をまとめてよいか、値はどれだけ揺れるかまとめると r = −0.024(年度の平均どうしは +0.881)、保健医療費の前年度比の中央値 7.23%単位は「年度ごとの 47 県」。1 年度の値は揺れが大きいと注記する
Analysis1 県や外れ値で結論が変わらないかρ = −0.468、1 県除くと −0.603〜−0.437、95% 区間 [−0.715, −0.185]向きは頑健、大きさの不確かさは区間で示す

こうして書き直した C は、「2017 年度以降の各年度で、高齢化率が高い県ほど二人以上世帯の保健医療費支出は少ない傾向があり(2023 年度 r = −0.490、95% 区間 −0.72〜−0.19)、消費支出の違いをそろえても残る。ただし 2016 年度以前ははっきりせず、家計の支出は年ごとの揺れが大きい。原因(世帯構成・公的負担など)はこのデータでは特定できない」となります。1 周目より長くなりましたが、どこまで言えて、どこから先が言えないかの境目がはっきりしました。そして「世帯人員や医療費の総額で見たらどうか」という 3 周目の Problem が、今度は必要なデータ(SSDSE-B-2026 には無い列)の形で具体的に見えています。

2 周目の Conclusion(続き)— 係数を「実感できる大きさ」に直して伝える

「−315 円/ポイント」は正しい数字ですが、読み手には大きいのか小さいのかが伝わりません。Conclusion では、係数を現実に起こりうる違いの幅に掛けて、元の単位で言い直します。ここでは高齢化率の四分位範囲(47 県の真ん中の半分の幅)を使い、1 周目と 2 周目の係数を並べます。

🎯 このコードでやること:2023 年度の高齢化率の 25% 点と 75% 点を求め、1 周目(高齢化率だけ)と 2 周目(消費支出もそろえる)の回帰の係数と 95% 信頼区間を、四分位の差の分だけの保健医療費の差(月額・平均に対する割合)に直す。

📥 入力例 SSDSE-B-2026(2023 年度の 47 行): A1101・A1303・L3221・L322106
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pandas as pd
import statsmodels.formula.api as smf

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023].copy()
d['高齢化率'] = d['A1303'] / d['A1101'] * 100
d['保健医療費'] = d['L322106'].astype(float)
d['消費支出'] = d['L3221'].astype(float)

q1, q3 = d['高齢化率'].quantile([0.25, 0.75])
m1 = smf.ols('保健医療費 ~ 高齢化率', data=d).fit()
m2 = smf.ols('保健医療費 ~ 高齢化率 + 消費支出', data=d).fit()
print(f'高齢化率の四分位: 25% 点 {q1:.1f}%  75% 点 {q3:.1f}%  差 {q3 - q1:.1f} ポイント')
for name, m in [('1 周目(高齢化率だけ)', m1), ('2 周目(消費支出もそろえる)', m2)]:
    b = m.params['高齢化率']; lo, hi = m.conf_int().loc['高齢化率']
    print(f'{name}: 係数 {b:.0f} 円/ポイント [95%CI {lo:.0f}, {hi:.0f}]'
          f' → 四分位の差では月 {b * (q3 - q1):,.0f} 円(平均 {d["保健医療費"].mean():,.0f} 円の {b * (q3 - q1) / d["保健医療費"].mean():+.1%})')
📤 実行例(実測) 高齢化率の四分位: 25% 点 30.0% 75% 点 34.0% 差 4.0 ポイント 1 周目(高齢化率だけ): 係数 -315 円/ポイント [95%CI -483, -147] → 四分位の差では月 -1,248 円(平均 14,423 円の -8.7%) 2 周目(消費支出もそろえる): 係数 -216 円/ポイント [95%CI -366, -67] → 四分位の差では月 -858 円(平均 14,423 円の -5.9%)

💬 高齢化率の 25% 点は 30.0%、75% 点は 34.0% で、47 県の真ん中の半分はわずか 4.0 ポイントの幅に収まっています。この差に係数を掛けると、1 周目の係数では月 −1,248 円(平均 14,423 円の −8.7%)、消費支出をそろえた 2 周目の係数では月 −858 円(−5.9%)です。「高齢化率が典型的に高い県と低い県で、二人以上世帯の保健医療費は月 900〜1,200 円ほど違う」と言い直すと、関係の大きさが実感できます。係数の 95% 信頼区間(2 周目で −366〜−67 円/ポイント)も同じように四分位の差に掛けると、月 −1,451〜−264 円と幅が広く、Analysis で見た不確かさがそのまま残ります。C では「関係の向き」「元の単位での大きさ」「その不確かさ」の 3 つを 1 文にそろえて書きます。

🧪 2 周目の理解度チェック

問題解答
Q1. 12 年度の 564 行で相関を取ると r = −0.024 だった。「高齢化率と保健医療費は無関係」と結論してよいか。よくない。年度ごとの 47 県では 2017 年度以降一貫して負だが、年度の平均どうしは +0.881 で、逆向きの 2 つの関係が混ざって打ち消している。行の単位(県 × 年度)を確かめ、年度ごとに見るか年度の効果を分けて分析する。
Q2. 2023 年度の r = −0.490 と 2013 年度の r = −0.013 のどちらを報告すべきか。Plan で決めた年度(例: 最新年度)を報告し、ほかの年度の結果も並べて示す。結果を見てから都合のよい年度を選ぶと、偶然の強さを結論にしてしまう。
Q3. 保健医療費を消費支出で割った割合でも r = −0.392 だった。この結果から言えることは。支出全体の大きさの違いだけでは負の関係を説明できない。ただし世帯人員や医療費の公的負担など、SSDSE-B-2026 に無い要因は調べられていないので、原因を特定したことにはならない。
Q4. 沖縄県を除くと r が −0.490 → −0.603 に強まった。沖縄県を外れ値として除いて報告してよいか。除く理由がデータの誤りでない限り、除かない。「沖縄県を除くと −0.603、東京都を除くと −0.437」のように、1 県で結論がどれだけ動くかを併記するのが適切。
Q5. 検出力の表で、47 県のまま |ρ| = 0.30 の関係を 80% の確率で見つけるには、何年度分の独立なデータが必要か(年度どうしを独立とみなした概算)。必要な件数は 85 なので、47 件の年度 2 つ分(94 件)で届く計算。ただし同じ県の別年度は独立ではない(⑦ の 564 行の例のように実質の件数は増えにくい)ので、年度を足すなら県クラスタの標準誤差などで評価する。
Q6. 5 県の手計算で、沖縄県の偏差の積だけが正だった。沖縄県を除いた 4 県で r を計算すると、負の関係は強まるか弱まるか。強まる。沖縄県は「高齢化率が低いのに医療費も少ない」点で、負の傾向に逆らう唯一の県だったので、除くと残りの積はすべて負になり、4 県の r は −0.800 になる。47 県で沖縄県を除いたときに r が −0.490 → −0.603 と強まったのと同じ。

3 周目 — 年度を変えて、 結論が崩れないか試す

🌐 の「3 周目以降は反証を試す」を実際に行う。 1・2 周目の結論はどちらも 2023 年度の 47 県だけから出したものなので、 Problem を「この負の関係は、 年度を変えても同じ強さで出るか」に置き直し、 Plan で「年度ごとに同じ計算をして並べる」と決めてから Data と Analysis に進む。

🎯 このコードでやること:1・2 周目で使った 2023 年度だけでなく、 2012〜2023 年度の 12 年分それぞれで、 47 県の高齢化率と保健医療費(金額と、 消費支出に占める割合)の相関係数を計算する。

📥 入力例 SSDSE-B-2026 全体 564 行(47 都道府県 × 2012〜2023 年度) 使う列: 総人口・65歳以上人口(→ 高齢化率)、 保健医療費(二人以上の世帯)、 消費支出(二人以上の世帯) ※ 家計の列は各県の県庁所在市の家計調査の値(月平均・円)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1)
df['高齢化率'] = df['65歳以上人口'] / df['総人口'] * 100
df['保健医療費の割合'] = df['保健医療費(二人以上の世帯)'] / df['消費支出(二人以上の世帯)'] * 100

rows = []
for y, g in df.groupby('年度'):          # 年度ごとに 47 県で相関を計算する
    rows.append({'年度': y,
                 'r(金額)': g['高齢化率'].corr(g['保健医療費(二人以上の世帯)']),
                 'r(割合)': g['高齢化率'].corr(g['保健医療費の割合'])})
t = pd.DataFrame(rows).set_index('年度').round(3)
print(t.to_string())
print('金額で負になった年度:', int((t['r(金額)'] < 0).sum()), '/ 12', '  割合で負になった年度:', int((t['r(割合)'] < 0).sum()), '/ 12')
print('r(金額)の範囲:', t['r(金額)'].min(), '〜', t['r(金額)'].max())
📤 実行例(実測) r(金額) r(割合) 年度 2012 -0.095 -0.205 2013 -0.013 0.013 2014 -0.251 -0.265 2015 -0.143 -0.134 2016 -0.126 -0.047 2017 -0.320 -0.229 2018 -0.360 -0.369 2019 -0.365 -0.334 2020 -0.327 -0.257 2021 -0.406 -0.356 2022 -0.468 -0.379 2023 -0.490 -0.392 金額で負になった年度: 12 / 12 割合で負になった年度: 11 / 12 r(金額)の範囲: -0.49 〜 -0.013

💬 金額の相関は 12 年度すべてで負だが、 強さは 2013 年度の −0.013 から 2023 年度の −0.490 まで大きく揺れる。 2023 年度の −0.490 は 12 年分で最も強い値で、 2012〜2016 年度は −0.25 より弱い。 割合で見ても 2013 年度だけ +0.013 と符号が変わり、 残る 11 年度は負。 「負の関係がある」という向きは崩れなかったが、 「r = −0.49 の中程度の相関」は、 たまたま一番強く出た年度の値を切り取った書き方になる。 Conclusion には年度による幅(−0.01〜−0.49)と、 近年ほど強まっていることを書くのが正確である。

段階3 周目で決めたこと・分かったこと
Problem2023 年度の負の相関(r = −0.49)は、 年度を変えても同じ強さで出るか
Plan12 年度それぞれで同じ 47 県・同じ 2 つの指標(金額と割合)の相関を出し、 並べて比べる。 年度を選んで報告しない
Data564 行を年度ごとに 47 行ずつに分ける。 家計の列は県庁所在市の値であることを明記する
Analysis金額で −0.013〜−0.490、 割合で +0.013〜−0.392。 向きはほぼ一定、 強さは年度で大きく揺れる
Conclusion「高齢化率の高い県ほど県庁所在市の世帯の保健医療費は少ない傾向があり、 近年ほどはっきりしている(2012〜2016 年度は弱い)」と書き直す。 次の Problem は「近年強まった理由は何か」

🐍 Python 実装

以下は PPDACサイクル を SSDSE-B-2026 で扱うときの典型コード。 encoding='cp932' は政府統計の Shift-JIS 対応。 skiprows=1 は 1 行目の英語コード行(A1101 など)を飛ばし、2 行目の日本語見出しを列名にする定石。

Data 段階の点検 — 欠損と外れ値を数える

📥 入力例(SSDSE-B-2026 全体:564 行 × 112 列 = 47 都道府県 × 2012〜2023 年) 年度 地域コード 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) … 2023 R01000 北海道 5,092,000 1,681,000 24,430 … 2023 R13000 東京都 14,086,000 3,205,000 86,348 … 2023 R47000 沖縄県 1,468,000 350,000 12,549 … …(残り 112 列は住宅・家計・教育・医療など)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import pandas as pd
import numpy as np

# PPDACサイクル に関わる前処理の典型パターン
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()   # 2023年に統一

# ① 欠損値の確認
print('欠損数:')
print(df.isna().sum().sort_values(ascending=False).head(10))

# ② 数値変換(文字列のカンマ・%を除去して数値化)
def to_num(s):
    if isinstance(s, str):
        try:
            return float(s.replace(',', '').replace('%', ''))
        except ValueError:
            return s
    return s
df = df.apply(lambda col: col.map(to_num))

# ③ 外れ値検出(IQR)
q1 = df.quantile(0.25, numeric_only=True)
q3 = df.quantile(0.75, numeric_only=True)
iqr = q3 - q1
num = df.select_dtypes('number')
outlier_mask = ((num < q1 - 1.5*iqr) | (num > q3 + 1.5*iqr)).any(axis=1)
print('外れ値を含む行数:', outlier_mask.sum())
📤 実行例(実測) 欠損数: SSDSE-B-2026 0 Code 0 H1800 0 G7102 0 G7101 0 G5105 0 F3105 0 F3104 0 F3103 0 F3102 0 dtype: int64 外れ値を含む行数: 22

💬 2023 年度の SSDSE-B は欠損が 1 件も無く、上位 10 列ともに 0 なので、Data 段階の欠損処理はこのデータでは不要と確認できた。一方、IQR 基準で外れ値を 1 つでも含む行は 47 県中 22 県にのぼる。これは 110 の数値列のどれか 1 列で外れれば数える判定だからで、東京都は 92 列、大阪府は 86 列で外れ値になっている。人口の大きさに比例する実数の列が多いためで、県を除外するより人口あたりの比率に直してから外れ値を見直すのが Analysis 前の次の一手になる。

Analysis 段階の最小例 — 東日本と西日本の高齢化率を比べる

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) 北海道 5,092,000 1,681,000 東京都 14,086,000 3,205,000 沖縄県 1,468,000 350,000 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import pandas as pd
from scipy import stats

# PPDACサイクル 文脈での基本的な仮説検定
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()
df['aging']  = df['A1303'] / df['A1101'] * 100
df['region'] = df['Prefecture'].apply(lambda p: '東日本' if p in ['北海道','青森県','岩手県','宮城県','秋田県','山形県','福島県','茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県','新潟県','富山県','石川県','福井県','山梨県','長野県','岐阜県','静岡県','愛知県'] else '西日本')

east = df.loc[df['region']=='東日本', 'aging']
west = df.loc[df['region']=='西日本', 'aging']

t, p = stats.ttest_ind(east, west, equal_var=False)
print(f'東日本 平均高齢化率: {east.mean():.2f}%')
print(f'西日本 平均高齢化率: {west.mean():.2f}%')
print(f't = {t:.3f}, p = {p:.4f}')
print('判定:', '有意差あり' if p < 0.05 else '有意差なし')
📤 実行例(実測) 東日本 平均高齢化率: 31.18% 西日本 平均高齢化率: 31.97% t = -0.804, p = 0.4259 判定: 有意差なし

💬 東日本 23 都県の平均 31.18% と西日本 24 府県の平均 31.97% の差は 0.79 ポイントで、Welch の t 検定では p = 0.4259、差があるとは言えない。高齢化率は東京都 22.8% から秋田県 39.1% まで広がっており、東西の境目よりも県ごとの違いのほうがはるかに大きい。「東西で差がない」は区分の仕方の結果でもあるので、Conclusion では都市圏・非都市圏など別の区分でサイクルを回し直す余地を残して書く。

⚠️ よくある落とし穴

⚠️ PPDAC の落とし穴 — 段階別チェックリスト

段階よくある失敗対策
Problem願望のまま測定不能y / x を数式で定義
Problem範囲が広すぎる「47 県」「2023 年」など限定
Plan事前計画を文書化しない事前登録 / GitHub commit
Planサンプルサイズを考えない検出力分析 (前出)
Data欠損 / 外れ値を黙って削除削除ルールを Plan に書く
Data単位・型を混同dtype 確認 + 単位を併記
Analysis計画にない検定を追加追加分は exploratory と明示
Analysisp ハッキング (閾値超えを狙う)事前登録
Conclusion相関を因果と書く「相関」「予測」と書き分け
Conclusion限界を書かないLimitations を必須化
Conclusion過大一般化 (47 県 → 日本全体)範囲を明示

🌀 PPDAC は螺旋 — 1 周で終わらない

PPDAC は「1 周してハイ終わり」ではなく、 螺旋 として継続するのが理想です。 1 周目で見えた限界 (外生変数の不足、 因果同定の困難) を踏まえ、 2 周目では パネルデータ + 自然実験 や 機械学習 + 因果推論 (DML) を導入していきます。

周Problem の洗練手法のアップグレード
1何が相関しているか重回帰
2何が予測できるか機械学習 + 交差検証
3何が因果か差分の差分法 / IV / DML
4介入効果はどれくらいか構造モデル / RCT

各段階でいちばん多い失敗

PPDAC は「順番に回せばよい」と説明されがちですが、 実際には各段階に固有の失敗の型があります。先回りして知っておくと、手戻りが減ります。

段階 よくある失敗 直し方
Problem 問いが大きすぎて、どんな数字が出ても答えになる 「誰の・いつの・どの指標か」を書くまで先へ進まない
Plan 先にデータを見てから指標を決める 使う指標と手法を、データを開く前に文章で書き残す
Data 行数・欠損・単位を確かめずに分析へ進む 最初に必ず shape・describe・欠損数を出す
Analysis 図を描かずに相関係数だけ見る 散布図を先に見る。r が同じでも形はまるで違う
Conclusion 相関を原因のように書いてしまう 「言えること」と「言えないこと」を分けて書く

とくに Plan の失敗は自覚しにくいのが厄介です。 データを眺めてから「この指標が効きそうだ」と決めるのは自然に感じますが、 それは偶然の当たりを本物だと思い込むやり方です。 探索してよい段階と、決めた仮説を検証する段階を分けること。 PPDAC を 2 周するのが、この分離を実現する最も簡単な方法です。

🧪 実データで確かめる — 見てから選んだ「一番」は、 年度が変わると入れ替わる

上の表の Plan の失敗「先にデータを見てから指標を決める」が、 どのくらい結論を揺らすかを確かめる。

🎯 このコードでやること:Plan を決めずにデータを見て、 2018 年度で高齢化率との相関が一番強い費目を選ぶ。 その費目が、 選ぶのに使っていない 2019・2021・2023 年度でも一番強いままかを確かめる。

📥 入力例 SSDSE-B-2026(47 都道府県 × 2012〜2023 年度) 家計の 10 費目(食料費・住居費・光熱・水道費・家具・家事用品費・被服及び履物費・保健医療費・ 交通・通信費・教育費・教養娯楽費・その他の消費支出、 いずれも二人以上の世帯)を消費支出に占める割合(%)に直す
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1)
df['高齢化率'] = df['65歳以上人口'] / df['総人口'] * 100
cols = [c for c in df.columns if c.endswith('(二人以上の世帯)') and c != '消費支出(二人以上の世帯)']
for c in cols:                                   # 費目を消費支出に占める割合に直す
    df[c] = df[c] / df['消費支出(二人以上の世帯)'] * 100

def corr_by_year(y):
    g = df[df['年度'] == y]
    return g[cols].corrwith(g['高齢化率'])

explore = corr_by_year(2018)                     # 探索に使う年度
best = explore.abs().idxmax()
print(f'費目の数 = {len(cols)}')
print('2018 年度で |r| が最大の費目:', best, f'r = {explore[best]:.3f}')
print(explore.round(3).sort_values().to_string())
for y in [2019, 2021, 2023]:                    # 探索に使っていない年度で確かめる
    r = corr_by_year(y)
    print(f'{y} 年度: {best} r = {r[best]:.3f}(その年の最大は {r.abs().idxmax()} r = {r[r.abs().idxmax()]:.3f})')
📤 実行例(実測) 費目の数 = 10 2018 年度で |r| が最大の費目: その他の消費支出(二人以上の世帯) r = 0.457 被服及び履物費(二人以上の世帯) -0.426 教育費(二人以上の世帯) -0.422 保健医療費(二人以上の世帯) -0.369 食料費(二人以上の世帯) -0.349 教養娯楽費(二人以上の世帯) -0.323 住居費(二人以上の世帯) -0.307 家具・家事用品費(二人以上の世帯) 0.053 光熱・水道費(二人以上の世帯) 0.361 交通・通信費(二人以上の世帯) 0.423 その他の消費支出(二人以上の世帯) 0.457 2019 年度: その他の消費支出(二人以上の世帯) r = 0.496(その年の最大は 交通・通信費(二人以上の世帯) r = 0.536) 2021 年度: その他の消費支出(二人以上の世帯) r = 0.552(その年の最大は 教育費(二人以上の世帯) r = -0.561) 2023 年度: その他の消費支出(二人以上の世帯) r = 0.330(その年の最大は 教育費(二人以上の世帯) r = -0.669)

💬 2018 年度で |r| が一番大きいのは「その他の消費支出」(r = 0.457)だが、 2 位の被服及び履物費(−0.426)、 3 位の交通・通信費(0.423)との差はわずかで、 10 費目中 9 費目が |r| ≥ 0.3 に並ぶ。 選んだ費目は別の年度でも 0.330〜0.552 と正の相関を保つが、 「一番」の座は 2019 年度に交通・通信費(0.536)、 2021・2023 年度に教育費(−0.561・−0.669)へ移る。 データを見てから「一番効いている費目」を選んで報告すると、 その順位は偶然の揺れを含んでいる。 Plan の段階で「保健医療費を見る」と先に決めておけば、 この揺れに結論を左右されない。

🗺 概念マップ

PPDAC サイクル (Problem → Plan → Data → Analysis → Conclusion) を中心に、 上位概念 (探究学習・統計的問題解決)、 並列概念 (CRISP-DM・OSEMN・KDD プロセス)、 応用 (中高統計教育・データサイエンス入門・SSDSE 課題設計) を関係づけて整理する。

ppdac cycle リテラシー 統計的探究プロセス CRISP-DM 学術研究 実務応用 公的統計の活用

PPDAC サイクル (Problem → Plan → Data → Analysis → Conclusion) はニュージーランド統計教育で開発された統計的探究の枠組みで、 学術研究の論文構成、 実務応用のデータ分析プロジェクト、 公的統計を使った社会問題解決のすべてに共通する反復プロセスである。 Conclusion の段階で新たな Problem が生まれて再帰するため、 一度回したら終わりではなく螺旋状に深化する点が特徴で、 SSDSE-B-2026 を使った高校・大学のデータ探究学習でも標準フレームワークとして採用されている。

🔗 隣接手法への橋渡し

PPDAC サイクルは「問題発見 → 計画 → データ → 分析 → 結論」という反復プロセスで、 隣接する科学的方法論と多くを共有する。

PPDAC を一周回すと Conclusion から新たな Problem が生まれて螺旋的に深化する。 高校・大学のデータ探究学習や統計検定でも「考え方の標準形」として明示的に教えられている。

他のサイクルとの違い — PPDAC / CRISP-DM / OSEMN

似た枠組みがいくつもあります。どれが優れているかではなく、力点が違うと捉えると使い分けられます。

枠組み 出自 力点
PPDAC統計教育 問いを立てるところ。Problem に 2 文字ぶんの重みがある
CRISP-DM産業界のデータマイニング 業務適用と運用。Deployment まで含む
OSEMNデータサイエンス実務 手を動かす順序。取得・整形の比重が大きい

PPDAC が教育で好まれるのは、Problem と Plan を分けている点にあります。 「何を知りたいか」と「どう測るか」を別の段階として書かせることで、 測れる形に落とす訓練ができる。ここが他の枠組みより手厚いところです。

逆に PPDAC には運用の段階がありません。 分析結果をシステムに載せて回し続ける話をするなら CRISP-DM の語彙が要ります。 学習や単発の調査は PPDAC、継続運用を含むなら CRISP-DM、と使い分けるのが実際的です。

🌳 手法選択フロー

「PPDACサイクル」を扱う際の手法選択は、 状況に応じて以下のフローで判断すると迷いが減る。

  1. Step 1: 目的は記述か予測か?
    • 記述 (現状把握・要約) → 集計・可視化・要約統計量で全体像を掴む
    • 予測 (未知データへの推定) → モデル構築・検証フェーズへ移行
  2. Step 2: データの種類・規模は?
    • 数値データ・小〜中規模 → 「PPDACサイクル」やその拡張手法を直接適用
    • カテゴリデータ → カテゴリ専用の手法 (カテゴリ変数) と組み合わせ
    • 大規模・高次元 → 計算効率を考慮した派生手法 (アジャイル分析サイクル) を選択
  3. Step 3: 結果の解釈・共有は?
    • 専門家向け → 数値指標・統計検定で精緻に評価
    • 非専門家向け → 可視化・自然言語での要約を重視

このフローに沿って判断することで、 「PPDACサイクル」を中核とした適切な手法選択ができる。

最後に、レポートを書くときの実用的な助言をひとつ。 PPDAC の 5 段階を、そのまま見出しにしてしまうのが確実です。 「問い/測り方/使ったデータ/結果/言えること・言えないこと」と並べるだけで、 読み手はどこを疑えばよいかが分かります。 逆に、結果だけを並べたレポートは、読み手が前提を推測するしかなくなり、 質問が「その数字はどこから?」ばかりになります。 枠組みの価値は、自分の思考を整えることだけでなく、 他人が検証できる形で残すことにもあります。