論文一覧に戻る 📚 用語解説(ジャストインタイム型データサイエンス教育)
分散
Variance (σ²)
「平均からのズレ」の2乗の平均。データの広がりを測る。標準偏差の2乗。
記述統計σ²variancevar

🔖 キーワード索引(拡張)

分散と関連する重要用語:

標本分散 s² 不偏分散 標準偏差 SD 変動係数 CV 分散・共分散行列 分散の加法性 バイアス・バリアンス分解 分母 n vs n−1 外れ値の影響 スケール非不変 numpy.var (ddof) pandas .var() scipy.stats.tvar statsmodels DescrStatsW

💡 30秒で分かる結論

🍰 まずはやさしく

データの広がりを表す物差しです。

中心からどれくらい離れているかを知るために使います。

テストの点数がクラスでバラバラかを確認する時に便利です。

分散の意味と計算の基本について読みましょう。

📖 もっと詳しく

分散(variance)は、 データの「広がり(散らばり)」を測る量です。 平均だけでは「中心」しか分からないので、 散らばりを定量化するために使います。

計算:「各データ点と平均との差」を 2乗して、 平均したもの。 2乗するのは正負を打ち消して、 散らばりだけを取り出すため。

単位の問題:元の単位の2乗(cm² など)になるため、 直感的に使いにくい。 そこで平方根を取って元の単位に戻したのが 標準偏差。 実用ではこちらの方が好まれます。

応用:(i) 回帰の R² は「分散の分解」、 (ii) ANOVA(分散分析)は「群間分散 vs 群内分散」、 (iii) PCA は「分散最大の方向」を探す。 分散は統計の中核概念です。

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

🍰 まずはやさしく

データの散らばりを測るための言葉です。

分析の中でデータの広がりを正しく扱うために使います。

都道府県ごとのデータを比べる時に役立ちます。

定義や数式などの詳しい解説を順番に読みましょう。

論文中に 「分散」として登場する用語。

分散 とは:「平均からのズレ」の2乗の平均。データの広がりを測る。標準偏差の2乗。

本ページでは「variance」を扱う。 統計データ分析コンペティション (2026) の教材で、 SSDSE-B-2026 (47 都道府県 × 複数年 × 100 超列) の実データを使った再現可能な学習を目指す。

「variance」は統計・データサイエンスの体系における重要概念のひとつ。 本ページは「定義・直感・数式・実装・落とし穴・関連手法」の 6 視点で構成され、 各視点は独立して読めるが順序通り読むと体系的な理解が得られる。

🎨 直感で掴む — 分散は「ばらつきの大きさ」

🍰 まずはやさしく

データのばらつきを数字にしたものです。

平均だけでは分からない広がり方を掴むために使います。

部活の練習時間が人によって違うかを見る時に似ています。

なぜ計算に2乗を使うのかという理由を読みましょう。

分散(variance)は、 データの散らばり具合を1つの数字で表す指標。 平均から各データがどれくらい離れているか、 を2乗してから平均したものです。

偏差の可視化

各バーが東北6県の食料費。 赤線が平均、 オレンジ矢印が各県から平均までの距離(偏差)。 この偏差を集約したのが分散です。

なぜ「2乗」するのか?

二乗の意味

左:偏差はプラスとマイナスが混在し、 合計はゼロになります(重心の性質)。 そのままでは「ばらつき」を表せません。
右:偏差を2乗するとすべて非負になり、 「平均からの距離の大きさ」を測れます。 この平均が分散。

3つの分散レベル

分散と広がり

同じ平均(μ=50)でも、 分散が大きいほどデータは広がります。 分散は不確実性の大きさとも解釈できます。

💡 分散は幾何学的には「平均からの距離の2乗の平均」、 物理学的には「慣性モーメント」、 確率論的には「期待値の2次モーメント」と多角的に理解できます。

🎮 触って理解する — 分散シミュレータ

数直線上の点をドラッグして動かしたり、 空いた所をクリック(タップ)して点を追加すると、 平均・分散・標準偏差がリアルタイムで更新されます。 各点から平均への偏差を線で、 偏差の二乗正方形の面積で描きます。 「分散 = 偏差二乗(正方形の面積)の平均」を目で体感してください。

自由度の切替:
データ数 n = 5 平均 = 50.00 偏差平方和 Σ(xᵢ−x̄)² = 500.00 自由度 = 5 分散 σ² = 100.00 標準偏差 σ = 10.00
💡 点を平均から遠ざけると、 偏差は比例だが正方形の面積(=偏差²)は二乗で急増します。 これが「外れ値1個で分散が跳ね上がる」理由です。

この図で分かること

📚 もっと深く — 定義から共分散への拡張まで

分散の定義(ひとことで)

分散とは「各データが平均からどれだけ離れているか」を偏差の二乗の平均で測った散布度。 母分散は $$ \sigma^2 = \frac{1}{N}\sum (x_i-\mu)^2 $$、 不偏分散は $$ s^2 = \frac{1}{n-1}\sum (x_i-\bar{x})^2 $$。

なぜ「二乗」するのか — 絶対値との対比

偏差 (xᵢ − x̄) はそのまま足すと必ず 0 になる(平均の定義より)。 符号を消す方法は主に 2 つ:絶対値を取るか、二乗するか。

この「二乗ゆえの外れ値感度」を避けたいときは、 絶対値ベースの頑健な散布度(MAD・IQR)を使う。 詳しくは ロバスト統計 を参照。

母分散と標本分散 — n−1 の理由(ベッセル補正)

標本から母分散を推定するとき、 分母を n にすると系統的に過小評価される($$ \mathbb{E}[s^2_n] = \frac{n-1}{n}\sigma^2 < \sigma^2 $$)。 これは偏差を「未知の母平均 μ」ではなく「標本から計算した x̄」で測るため、 平方和が必ず最小になってしまうから。 分母を n−1(=自由度)にすると期待値が母分散に一致する(不偏推定量)。 これをベッセル補正と呼ぶ。 上のシミュレータで「母分散/不偏分散」を切り替えると、 n が小さいほど両者の差が大きい(比は n/(n−1))ことが確認できる。

標準偏差との関係

分散は単位が「元の単位の二乗」(万人²・千円² など)になり解釈しづらい。 その平方根を取って元の単位に戻したのが 標準偏差(SD = √分散)。 実務の報告・可視化では SD が好まれる。 シミュレータの紫の破線正方形(一辺 = SD)は、 色付き正方形の面積の平均(=分散)にちょうど等しく、 「SD は分散を長さに戻したもの」であることを示している。

外れ値への感度

偏差を二乗するため、 平均から大きく離れた 1 点が分散に与える影響は距離の二乗で効く。 例えば 47 都道府県の総人口は、 東京都(突出値)を含めると SD ≈ 280 万人、 除くと ≈ 224 万人と大きく変わる。 外れ値が疑われるデータでは、 分散・SD の代わりに IQR や MAD といった頑健な指標を併用する。 → ロバスト統計

共分散への拡張

分散は「1 変数の偏差 × 同じ偏差」の平均だが、 これを「x の偏差 × y の偏差」の平均に一般化すると 共分散 $$ \mathrm{Cov}(X,Y) = \frac{1}{n}\sum (x_i-\bar{x})(y_i-\bar{y}) $$ になる。 実際 $$ \mathrm{Cov}(X,X) = \mathrm{Var}(X) $$ であり、 分散は共分散の特殊ケース。 2 変数の和の分散には共分散項が現れる($$ \mathrm{Var}(X+Y)=\mathrm{Var}(X)+\mathrm{Var}(Y)+2\,\mathrm{Cov}(X,Y) $$)。 共分散を標準偏差で割って無次元化すると相関係数、 多変数に並べると分散共分散行列となり、 PCA やポートフォリオ理論の基礎になる。

📐 数式と読み方

🍰 まずはやさしく

分散を計算するためのルールです。

ばらつきの大きさを正確に求めるために使います。

スマホの利用時間の平均からのズレを計算するイメージです。

記号の意味と計算式の読み方を詳しく読みましょう。

① 母分散(population variance)

$$ \sigma^2 = \mathbb{E}[(X - \mu)^2] = \frac{1}{N} \sum_{i=1}^{N} (x_i - \mu)^2 $$

② 不偏分散(sample variance)

$$ s^2 = \frac{1}{n - 1} \sum_{i=1}^{n} (x_i - \bar{x})^2 $$

記号読み方意味
σ²シグマの二乗母分散
エスの二乗不偏分散(標本ベース)
(xᵢ - x̄)²エックスアイ マイナス エックスバー の二乗偏差の二乗
E[·]期待値確率変数の重み付き平均

🔬 数式を言葉で読み解く — ③ 計算用の別形式

展開すると:

$$ s^2_n = \overline{x^2} - \bar{x}^2 = \frac{1}{n} \sum x_i^2 - \bar{x}^2 $$

二乗の平均 − 平均の二乗」とも呼ばれます。 計算量が少なく、 ストリーミングデータでも使いやすい。

🔧 分散の性質 — 線形変換とどう動くか

① 定数を足しても分散は変わらない

$$ \text{Var}(X + c) = \text{Var}(X) $$

全データに同じ数を足すと、 全部が平行移動するだけで「散らばり」は変わらない。

② 定数倍すると分散はその二乗倍

$$ \text{Var}(aX) = a^2 \cdot \text{Var}(X) $$

2倍すると分散は4倍、 3倍すると分散は9倍。 ばらつきも2乗で広がる。

③ 独立な場合の和の分散

$$ \text{Var}(X + Y) = \text{Var}(X) + \text{Var}(Y) \quad (X, Y \text{ 独立}) $$

独立な変数の和の分散は、 それぞれの分散の和。 大数の法則の根拠でもあります。

④ 一般の場合(共分散項あり)

$$ \text{Var}(X + Y) = \text{Var}(X) + \text{Var}(Y) + 2 \text{Cov}(X, Y) $$

独立でない場合、 共分散の項が追加されます。 ポートフォリオ理論や分散分析の基礎。

⑤ 標本平均の分散

$$ \text{Var}(\bar{X}) = \frac{\sigma^2}{n} $$

サンプルサイズを n 倍にすると、 平均のばらつきは 1/n に減る。 これが「サンプルを多く取れば平均が安定する」の理論的根拠。

📏 標準偏差との関係

分散の平方根標準偏差(standard deviation):

$$ \sigma = \sqrt{\sigma^2}, \quad s = \sqrt{s^2} $$

分散は単位が「元の単位の2乗」(千円² など)になるため、 解釈しにくいことがあります。 平方根を取った標準偏差は元のデータと同じ単位を持つので、 直感的。

統計量 単位 直感性 数学的扱いやすさ
分散 σ²元の単位の二乗(千円²)低い高い(線形性、 加法性)
標準偏差 σ元の単位(千円)高い低い(加法性なし)

🎯 分散分析 (ANOVA) — 分散の分解

分散の最大の応用が分散分析(Analysis of Variance)。 全体の分散を「群間の差による分散」と「群内の偶然の分散」に分けて、 「群差は本物か?」を検定します。

分散の分解

$$ \text{総分散} = \text{群間分散} + \text{群内分散} $$

$$ \sum_i (x_i - \bar{x})^2 = \sum_g n_g (\bar{x}_g - \bar{x})^2 + \sum_g \sum_i (x_{gi} - \bar{x}_g)^2 $$

F検定量

$$ F = \frac{\text{群間分散} / df_1}{\text{群内分散} / df_2} $$

F が大きい → 群間差が大きい → 群差は有意。 t検定の多群版とも言えます。

使用例

🤖 機械学習での分散

① バイアス-バリアンス分解

機械学習モデルの予測誤差は3つに分解されます:

$$ E[(y - \hat{f}(x))^2] = \text{Bias}^2 + \text{Variance} + \sigma^2_\epsilon $$

過学習=Variance が大(複雑モデルは学習データに敏感)、 過小学習=Bias が大。

② PCA(主成分分析)

「分散を最大化する方向」を順に見つけ、 データの主要な変動を捉える次元削減手法。 分散がそのまま「情報量」として使われる。

③ Variance Threshold(特徴選択)

分散がほぼゼロの特徴量(全データで値が同じ)は予測に寄与しないので除外する前処理。

このコードでやること: 3 特徴量のうち分散が threshold=0.01 以下の列を自動除外する sklearn の VarianceThreshold の最小動作例。

📥 入力データ例:

X = [[0, 2, 0.9], # 列0は常に0 → 分散=0で除外 [0, 3, 0.8], [0, 4, 0.7]]
 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.feature_selection import VarianceThreshold

import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]        # 2023 年の 47 都道府県
# 総人口・65歳以上人口・消費支出を、桁を揃えて 3 列の行列にする
X = np.column_stack([
    df['A1101'].astype(float).values / 1e4,   # 総人口(万人)
    df['A1303'].astype(float).values / 1e4,   # 65歳以上人口(万人)
    df['L3221'].astype(float).values / 1e3])  # 消費支出(千円)

selector = VarianceThreshold(threshold=0.01)
X_filtered = selector.fit_transform(X)
print('各列の分散      :', np.round(selector.variances_, 3))
print('残った列の数    :', X_filtered.shape[1], '/', X.shape[1])
📤 実行結果: X_filtered の shape が (3, 2) に縮小(分散ゼロの列0が除去される)

💬 分散が 0.01 以下の特徴量(全サンプルでほぼ同じ値)は予測に寄与しないため自動除去される。 前処理で不要な特徴量を削る標準的な手順。

④ Batch Normalization

各層の出力を「平均0、 分散1」に標準化する深層学習の必須技術。 「分散」が出てくるたびに必要なのが正規化。

📐 ddof パラメータ — 標本分散と不偏分散の決定的な違い

分散の計算で最初に詰まるのが「分母を n にするか n-1 にするか」問題。 NumPy / pandas / SciPy で ddof (Delta Degrees Of Freedom) という引数で制御する。 既定値が NumPy=0pandas=1 と異なるため、 同じデータでも違う値が出るのが要注意ポイント。

🔬 数式を言葉で読み解く — n-1 の意味

標本分散の定義は s² = Σ(xᵢ - x̄)² / (n - ddof)。 数式を言葉で読み解くと、 「観測値とその平均との差を二乗して足し、 自由度で割る」。 ここで自由度とは「自由に動ける情報の数」のこと。 標本平均 x̄ を計算するのに 1 個分の情報を使っているため、 残りの「真に独立な情報」は n - 1 個になる。 ddof=1 (= n-1 で割る) とすると、 推定された分散の期待値が母分散 σ² と一致する (不偏推定量)。 一方 ddof=0 では系統的に過小評価される。

関数既定 ddof既定の意味変更方法
numpy.var(x)0標本分散 (n で割る)ddof=1
pandas.Series.var()1不偏分散 (n-1 で割る)ddof=0
statistics.variance(x)1不偏分散statistics.pvariance
scipy.stats.tvar(x)1不偏分散なし (固定)
Excel VAR.S1不偏分散VAR.P で標本分散

🔬 数式を言葉で読み解く

分散 σ² = E[(X − μ)²] の各記号を日本語に翻訳する。

分散の式は「データが平均からどれだけ広がっているか」を二乗距離の平均で測る。 SSDSE-B-2026 A1101 では分散 ≈ 7.7 × 104 (万人²)、 標準偏差 ≈ 277 万人で「47 県の人口は平均 265 万人 ± 277 万人」と要約される。

母分散 vs 不偏分散の使い分け:

🧮 計算ステップ — 東北6県の分散を手計算

🧮 計算ステップ — 東北6県の分散を手計算

データと偏差・偏差の二乗

都道府県 食料費 xᵢ 偏差 (xᵢ - x̄) 偏差の二乗 (xᵢ - x̄)²
青森県77.899-2.9258.5556
岩手県81.997+1.1731.3759
宮城県83.835+3.0119.0661
秋田県78.124-2.7007.2900
山形県84.105+3.28110.7650
福島県78.984-1.8403.3856
合計 484.944 ≈ 0 40.4382

平均 x̄ = 80.824 千円。

標本分散(÷ n)

$$ s^2_n = \frac{1}{n} \sum_{i=1}^{n} (x_i - \bar{x})^2 $$

計算:40.4382 ÷ 6 = 6.7397

不偏分散(÷ n-1)— 推論用

$$ s^2 = \frac{1}{n - 1} \sum_{i=1}^{n} (x_i - \bar{x})^2 $$

計算:40.4382 ÷ 5 = 8.0876

東北6県の食料費の分散は約 6.74〜8.09 (千円²)。 平方根を取れば標準偏差 ≈ 2.84 千円。

🎲 なぜ n-1 で割るのか? — Bessel補正

「ばらつきを n 個で割って平均すれば、 1個あたりのばらつきになる」が直感的。 でも統計学ではn-1 で割るのが標準。 なぜでしょうか。

標本分散はバイアスを持つ

「÷n」で計算した標本分散 s²_n は、 母集団全体の分散 σ²より小さくなる傾向があります。

$$ \mathbb{E}[s^2_n] = \frac{n - 1}{n} \sigma^2 \quad < \sigma^2 $$

つまり「÷n」だと、 平均的に母分散より (n-1)/n 倍だけ過小評価します。 これを是正するため、 (n-1)/n の逆数 n/(n-1) を掛けて補正:

$$ s^2 = \frac{n}{n-1} \cdot s^2_n = \frac{1}{n - 1} \sum (x_i - \bar{x})^2 $$

これが不偏分散。 期待値が母分散 σ² に一致する(不偏性)。

不偏分散の収束

上の図は1000回のサンプリングで分散を計算した平均値。 赤線(÷n)は常に真値より下にあり、 緑線(÷(n-1))は真値に正しく一致。

直感的説明:自由度

n 個のデータがあって、 平均 x̄ を計算すると1つの「制約」ができます(偏差の合計がゼロ)。 残りの「自由に動ける情報の数」は n-1 個。 これが自由度と呼ばれる概念。

自由度で割ることで、 「情報の量に合わせた平均」になります。

どちらを使うべきか?

🧮 SSDSE-B 実値計算 — 47都道府県の主要変数で分散を計算

SSDSE-B-2026 の主要 6 変数で、 ① 標本分散、 ② 不偏分散、 ③ 標準偏差、 ④ 変動係数(CV)、 ⑤ Welford のオンライン計算 を実演する。

このコードでやること: SSDSE-B-2026 の 47 都道府県データ(2023 年)から「総人口・15歳未満人口・65歳以上人口・合計特殊出生率・年平均気温・消費支出」6 変数について、母分散・不偏分散・標準偏差・変動係数を一括計算する。

📥 入力データ(SSDSE-B-2026、2023 年 抜粋):

Prefecture A1101(総人口万) A1301(15歳未満万) A1303(65歳以上万) A4103(出生率) B4101(気温) L3221(消費支出千円) 北海道 509.2 51.4 168.1 1.06 11.0 296.9 青森県 118.4 11.8 41.7 1.23 12.6 263.4 ...(47 県分)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np
import pandas as pd

# 先頭列名は "SSDSE-B-2026"(=年度)。2 行目の日本語ラベルは skiprows=[1] で除外し、
# コード列名(A1101 等)で読み込んでから 2023 年だけを抽出する。
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]

# (コード, 表示名, 単位変換) 人口系は万人、消費支出は千円に換算して比較する
feats = [('A1101', '総人口(万人)',      1e4),
         ('A1301', '15歳未満人口(万人)', 1e4),
         ('A1303', '65歳以上人口(万人)', 1e4),
         ('A4103', '合計特殊出生率',      1),
         ('B4101', '年平均気温(℃)',       1),
         ('L3221', '消費支出(千円)',      1e3)]

for code, name, u in feats:
    x = df[code].astype(float).values / u
    mu, sd = x.mean(), x.std(ddof=1)
    print(f'{name:<16} 平均={mu:8.2f}  σ²={x.var(ddof=0):10.2f}  '
          f's²={x.var(ddof=1):10.2f}  SD={sd:7.2f}  CV={sd/mu:6.3f}')
📤 実行結果: 総人口(万人) 平均= 264.58 σ²= 76597.77 s²= 78262.94 SD= 279.76 CV= 1.057 15歳未満人口(万人) 平均= 30.15 σ²= 952.85 s²= 973.57 SD= 31.20 CV= 1.035 65歳以上人口(万人) 平均= 77.08 σ²= 4711.70 s²= 4814.13 SD= 69.38 CV= 0.900 合計特殊出生率 平均= 1.29 σ²= 0.02 s²= 0.02 SD= 0.13 CV= 0.103 年平均気温(℃) 平均= 16.80 σ²= 4.11 s²= 4.19 SD= 2.05 CV= 0.122 消費支出(千円) 平均= 295.86 σ²= 570.53 s²= 582.94 SD= 24.14 CV= 0.082

💬 消費支出の CV ≈ 0.082(世帯あたり支出は全国的に均質)に対し、総人口の CV ≈ 1.057(東京都が突出して大きい)と最大。「ばらつきを比較する」には絶対値の分散ではなく、単位無次元の CV が有効。

典型的な観察例: 総人口の CV は 1.0 を超え(東京都がずば抜けて大きい)、 消費支出の CV は 0.08 程度と均質。 「ばらつきを比較する」には絶対値の分散ではなく、 単位無次元の CV を使うのが定石。 また ddof=0(母分散)と ddof=1(不偏分散)の差は n=47 では約 2.2% で実用上ほぼ無視できるが、 教科書通り「サンプルから母数を推定」する文脈では必ず ddof=1 を使う。

Welford のオンラインアルゴリズム(数値安定)

このコードでやること: SSDSE-B-2026(2023 年)の消費支出を Welford アルゴリズムで計算し、numpy の一括計算と完全一致することを確認する。

📥 入力データ(SSDSE-B-2026 消費支出、47 県分):

df['L3221'].values / 1e3 = [296.9, 263.4, ...] (単位: 千円、47 件)
 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

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
x = df['L3221'].astype(float).values / 1e3   # 消費支出(二人以上の世帯)を千円に

def welford(xs):
    n, mean, M2 = 0, 0.0, 0.0
    for v in xs:
        n += 1
        delta = v - mean
        mean += delta / n
        M2   += delta * (v - mean)
    return mean, M2/n, M2/(n-1)   # 母分散, 不偏分散

mu, var_pop, var_samp = welford(x)
print(f'Welford : μ={mu:.2f}, σ²={var_pop:.2f}, s²={var_samp:.2f}')
print(f'numpy   : μ={x.mean():.2f}, σ²={x.var(ddof=0):.2f}, '
      f's²={x.var(ddof=1):.2f}')
📤 実行結果: Welford : μ=295.86, σ²=570.53, s²=582.94 numpy : μ=295.86, σ²=570.53, s²=582.94

💬 Welford アルゴリズムの μ=295.86、σ²=570.53、s²=582.94 が numpy と完全一致。 ストリーミング計算でも numpy の一括計算と同じ結果が得られる。

分散の加法性(独立確率変数の和)

このコードでやること: SSDSE-B-2026 の「15歳未満人口」と「65歳以上人口」は相関があるため Var(a+b) ≠ Var(a)+Var(b) となる。共分散項の補正がなければ分散の加法性が成立しないことを実データで確認する。

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

a = df['A1301']/1e4 (15歳未満人口、万人) b = df['A1303']/1e4 (65歳以上人口、万人、47 件)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 15歳未満 + 65歳以上 = 「非生産年齢人口」とすると Var の加法性は崩れる
# (両者は同じ県内のサンプルなので相関がある)
a = df['A1301'].astype(float) / 1e4
b = df['A1303'].astype(float) / 1e4
print(f'Var(a)   = {a.var(ddof=1):.2f}')
print(f'Var(b)   = {b.var(ddof=1):.2f}')
print(f'Var(a+b) = {(a+b).var(ddof=1):.2f}')
print(f'2*Cov(a,b) = {2 * a.cov(b):.2f}')
print(f'分散の加法性チェック: '
      f'Var(a)+Var(b)+2Cov(a,b) = '
      f'{a.var(ddof=1) + b.var(ddof=1) + 2*a.cov(b):.2f}')
📤 実行結果(典型値): Var(a) = 973.57 Var(b) = 4814.13 Var(a+b) = 10065.49 2*Cov(a,b) = 4277.79 分散の加法性チェック: Var(a)+Var(b)+2Cov(a,b) = 10065.49

💬 Var(a)+Var(b) = 5787.70 に対し Var(a+b) = 10065.49。 差は 2×Cov(a,b) = 4277.79 で完全一致。 15歳未満と65歳以上は都市部で共に多い傾向があり、強い正の共分散を持つため加法性が単純には成立しない。

🧮 SSDSE-B-2026 47 県人口で分散を実計算 — ddof による差

このコードでやること: SSDSE-B-2026 の 47 県人口について、 ddof=0 (標本分散) と ddof=1 (不偏分散) の差を実値で示し、 同じデータでも値がどう変わるかを確認する。

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

SSDSE-2026 都道府県 A1101 総人口(万人) R01000 北海道 509.2 R02000 青森県 118.4 ... R47000 沖縄県 146.8 (47 県分)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import pandas as pd
import numpy as np
from scipy import stats

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
pop = df.groupby('Prefecture')['A1101'].first() / 1e4

n = len(pop)
mean = pop.mean()
ss = ((pop - mean) ** 2).sum()      # 偏差平方和

print(f'n           = {n}')
print(f'mean        = {mean:.3f}')
print(f'Σ(x-x̄)²    = {ss:.1f}')
print()
print(f'np.var (ddof=0, n  で割る)  = {np.var(pop, ddof=0):.2f}  → σ̂² (バイアス有)')
print(f'np.var (ddof=1, n-1 で割る) = {np.var(pop, ddof=1):.2f}  → s² (不偏)')
print(f'pandas .var()  (既定 ddof=1) = {pop.var():.2f}')
print(f'pandas .var(ddof=0)         = {pop.var(ddof=0):.2f}')
print(f'比 ddof1/ddof0 = {np.var(pop,ddof=1)/np.var(pop,ddof=0):.6f}  (= n/(n-1) = {n/(n-1):.6f})')

📤 実行例:

n = 47 mean = 264.581 Σ(x-x̄)² = 3600095.2 np.var (ddof=0, n で割る) = 76597.77 → σ̂² (バイアス有) np.var (ddof=1, n-1 で割る) = 78262.94 → s² (不偏) pandas .var() (既定 ddof=1) = 78262.94 pandas .var(ddof=0) = 76597.77 比 ddof1/ddof0 = 1.021739 (= n/(n-1) = 1.021739)

💬 結果の読み方: 同じデータでも ddof=0 と ddof=1 で 1665 (約 2.2%) の差が出る。 比は n/(n-1) = 47/46 ≒ 1.0217 という単純な比例関係。 N が大きくなるとこの差はゼロに収束するため、 ビッグデータでは無視できる。 ただし「47 都道府県は標本ではなく母集団そのもの」という見方を取るなら ddof=0 が正しく、 「47 県は何らかの母集団からの標本」と見るなら ddof=1。 設定で意味が変わる典型例。

🧮 分散分解 — 全分散 = 群内分散 + 群間分散 (ANOVA の出発点)

分散には「分解できる」という強力な性質がある。 任意のグループ分けに対し、 全分散 = 群内分散の加重平均 + 群間分散 という関係 (分散分解定理) が成立する。 これは ANOVA (分散分析) や混合効果モデルの基礎であり、 R² (決定係数) の意味も「全分散のうちモデルが説明する分散の割合」として理解できる。

🔬 数式を言葉で読み解く — Var(X) = E[Var(X|G)] + Var(E[X|G])

数式を言葉で読み解くと、 「全分散は『各グループ内のばらつきの平均』と『各グループの平均同士のばらつき』の和」。 前者を「群内分散 (within-group variance)」、 後者を「群間分散 (between-group variance)」と呼ぶ。 この分解は、 グループ分けが「データの違いをどれだけ説明するか」を 1 つの指標で測れることを意味する。 群間 / 全 = R² (1 元配置 ANOVA の場合) となる。

このコードでやること: SSDSE-B-2026 の都道府県人口を 8 地方ブロックに分け、 全分散・群内分散・群間分散の 3 つを分解計算し、 「地方の違いが人口のばらつきをどれだけ説明するか」を R² 風指標で出す。

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

都道府県 A1101(総人口 万人) 地方ブロック 北海道 509.2 北海道 青森県 118.4 東北 ... 東京都 1408.6 関東 (47 県、8 地方ブロックに分類)
 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
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
pop = (df.groupby('Prefecture')['A1101'].first() / 1e4).reset_index()

region_map = {'北海道':'北海道',
    '青森県':'東北','岩手県':'東北','宮城県':'東北','秋田県':'東北','山形県':'東北','福島県':'東北',
    '茨城県':'関東','栃木県':'関東','群馬県':'関東','埼玉県':'関東','千葉県':'関東','東京都':'関東','神奈川県':'関東',
    '新潟県':'中部','富山県':'中部','石川県':'中部','福井県':'中部','山梨県':'中部','長野県':'中部','岐阜県':'中部','静岡県':'中部','愛知県':'中部',
    '三重県':'近畿','滋賀県':'近畿','京都府':'近畿','大阪府':'近畿','兵庫県':'近畿','奈良県':'近畿','和歌山県':'近畿',
    '鳥取県':'中国','島根県':'中国','岡山県':'中国','広島県':'中国','山口県':'中国',
    '徳島県':'四国','香川県':'四国','愛媛県':'四国','高知県':'四国',
    '福岡県':'九州沖縄','佐賀県':'九州沖縄','長崎県':'九州沖縄','熊本県':'九州沖縄','大分県':'九州沖縄','宮崎県':'九州沖縄','鹿児島県':'九州沖縄','沖縄県':'九州沖縄',
}
pop['region'] = pop['Prefecture'].map(region_map)

total_mean = pop['A1101'].mean()
ss_total = ((pop['A1101'] - total_mean) ** 2).sum()

# 群内 (within) と 群間 (between) の平方和
ss_within = 0.0
ss_between = 0.0
for r, sub in pop.groupby('region'):
    ss_within += ((sub['A1101'] - sub['A1101'].mean()) ** 2).sum()
    ss_between += len(sub) * (sub['A1101'].mean() - total_mean) ** 2

print(f'SS_total   = {ss_total:>12.1f}')
print(f'SS_within  = {ss_within:>12.1f}')
print(f'SS_between = {ss_between:>12.1f}')
print(f'within + between = {ss_within + ss_between:>12.1f}')
print(f'R² (between/total) = {ss_between/ss_total:.4f}')

📤 実行例:

SS_total = 3600095.2 SS_within = 2261918.7 SS_between = 1338176.5 within + between = 3600095.2 R² (between/total) = 0.3717

💬 結果の読み方: 全分散 3,600,095 を 2,261,919 (群内) + 1,338,177 (群間) に綺麗に分解できた (完全に一致)。 R² ≒ 0.37 は、 「47 県人口のばらつきのうち約 37% が地方ブロックの違いで説明できる」と解釈する。 残り 63% は「同じ地方の中での県間のばらつき」(例: 東京と神奈川の差) によるもの。 グルーピングが「強い説明力を持つか」を 1 つの数字で示せる強力な道具。

📊 等分散性検定 — Levene 検定と Bartlett 検定

t 検定や ANOVA の前提のひとつが「群間で分散が等しい (等分散性)」。 これを確かめる検定が Levene 検定 (頑健、 非正規でも使える) と Bartlett 検定 (正規前提、 検出力が高い)。 帰無仮説はどちらも「全群の分散が等しい」。

このコードでやること: SSDSE 47 県人口を 8 地方ブロックに分け、 各ブロック内分散が等しいかを Levene 検定と Bartlett 検定で検証する。

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

都道府県 A1101(総人口 万人) 地方ブロック 北海道 509.2 北海道(1 県) 青森県 118.4 東北(6 県) 宮城県 226.4 東北 東京都 1408.6 関東(7 県) 大阪府 876.3 近畿(7 県) (8 地方ブロックに分類し群ごとに分散を比較)
 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
import pandas as pd
from scipy import stats

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
pop = (df.groupby('Prefecture')['A1101'].first() / 1e4).reset_index()

region_map = {'北海道':'北海道',
    '青森県':'東北','岩手県':'東北','宮城県':'東北','秋田県':'東北','山形県':'東北','福島県':'東北',
    '茨城県':'関東','栃木県':'関東','群馬県':'関東','埼玉県':'関東','千葉県':'関東','東京都':'関東','神奈川県':'関東',
    '新潟県':'中部','富山県':'中部','石川県':'中部','福井県':'中部','山梨県':'中部','長野県':'中部','岐阜県':'中部','静岡県':'中部','愛知県':'中部',
    '三重県':'近畿','滋賀県':'近畿','京都府':'近畿','大阪府':'近畿','兵庫県':'近畿','奈良県':'近畿','和歌山県':'近畿',
    '鳥取県':'中国','島根県':'中国','岡山県':'中国','広島県':'中国','山口県':'中国',
    '徳島県':'四国','香川県':'四国','愛媛県':'四国','高知県':'四国',
    '福岡県':'九州沖縄','佐賀県':'九州沖縄','長崎県':'九州沖縄','熊本県':'九州沖縄','大分県':'九州沖縄','宮崎県':'九州沖縄','鹿児島県':'九州沖縄','沖縄県':'九州沖縄',
}
pop['region'] = pop['Prefecture'].map(region_map)

# サンプルサイズ 2 以上の群だけ抽出 (北海道は 1 県のみで除外)
groups = [g['A1101'].values for r, g in pop.groupby('region') if len(g) >= 2]
group_names = [r for r, g in pop.groupby('region') if len(g) >= 2]

# 群ごとの分散表
for name, g in zip(group_names, groups):
    print(f'{name:>6} n={len(g):>2}  var(ddof=1)={g.var(ddof=1):>10.1f}')

print()
W, p_l = stats.levene(*groups, center='median')
print(f'Levene W = {W:.3f},  p = {p_l:.4f}  (center=median, Brown-Forsythe)')
T, p_b = stats.bartlett(*groups)
print(f'Bartlett T = {T:.3f}, p = {p_b:.4f}')

📤 実行例:

中国 n= 5 var(ddof=1)= 8266.9 中部 n= 9 var(ddof=1)= 45504.5 九州沖縄 n= 8 var(ddof=1)= 19197.7 四国 n= 4 var(ddof=1)= 834.1 東北 n= 6 var(ddof=1)= 2717.9 近畿 n= 7 var(ddof=1)= 84017.9 関東 n= 7 var(ddof=1)= 201705.3 Levene W = 2.784, p = 0.0238 (center=median, Brown-Forsythe) Bartlett T = 33.286, p = 0.0000

💬 結果の読み方: 群ごとの分散が 834 (四国) から 201,705 (関東) まで 240 倍以上の差。 Levene 検定 p=0.024、 Bartlett 検定 p<0.001 でいずれも「等分散ではない」が示される。 → この後 ANOVA するなら Welch ANOVA (stats.f_oneway ではなく pingouin.welch_anova) を使うのが妥当。 t 検定なら Welch t を選ぶ。 「分散が違うときに通常の t/F を使うと第一種の過誤率が膨らむ」のがポイント。

このコードでやること: SSDSE-B-2026 の東北6県(青森・岩手・宮城・秋田・山形・福島)の食料費(千円)を使い、 数式 → 実数値代入 → Step ごとの展開 → Python 再現 → 一致確認を完全に示す。

数式(不偏分散):

$$ s^2 = \frac{1}{n-1} \sum_{i=1}^{n} (x_i - \bar{x})^2 $$

📥 入力データ(SSDSE-B-2026 東北6県 食料費 単位:千円):

都道府県 食料費 xᵢ 青森県 77.899 岩手県 81.997 宮城県 83.835 秋田県 78.124 山形県 84.105 福島県 78.984

東北6県食料費(千円)を Python リスト形式で表すと: [77.899, 81.997, 83.835, 78.124, 84.105, 78.984](青森・岩手・宮城・秋田・山形・福島の順)。

Step 1: 平均 x̄ を計算する

x̄ = (77.899 + 81.997 + 83.835 + 78.124 + 84.105 + 78.984) / 6 = 484.944 / 6 = 80.824 千円

Step 2: 各偏差 (xᵢ − x̄) と偏差の二乗 (xᵢ − x̄)² を計算する

青森: 77.899 − 80.824 = −2.925 → (−2.925)² = 8.5556 岩手: 81.997 − 80.824 = +1.173 → (+1.173)² = 1.3759 宮城: 83.835 − 80.824 = +3.011 → (+3.011)² = 9.0661 秋田: 78.124 − 80.824 = −2.700 → (−2.700)² = 7.2900 山形: 84.105 − 80.824 = +3.281 → (+3.281)² = 10.7650 福島: 78.984 − 80.824 = −1.840 → (−1.840)² = 3.3856

Step 3: 偏差の二乗和 Σ(xᵢ − x̄)² を求める

Σ(xᵢ − x̄)² = 8.5556 + 1.3759 + 9.0661 + 7.2900 + 10.7650 + 3.3856 = 40.4382

Step 4: 不偏分散(÷ n−1 = ÷5)と標本分散(÷ n = ÷6)を算出する

不偏分散 s² = 40.4382 / 5 = 8.0876 (千円²) 標本分散 σ̂² = 40.4382 / 6 = 6.7397 (千円²) 標準偏差 s = √8.0876 ≈ 2.843 (千円)

このコードでやること: 上記の手計算 Step 1〜4 を numpy で再現し、結果が完全一致することを確認する。

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

# SSDSE-B-2026 東北6県 食料費(千円)
x = np.array([77.899, 81.997, 83.835, 78.124, 84.105, 78.984])

# Step 1: 平均
print(f'Step1  x̄  = {x.mean():.3f}')   # 80.824

# Step 2: 偏差の二乗和
ss = np.sum((x - x.mean()) ** 2)
print(f'Step2  Σ(x-x̄)² = {ss:.4f}')   # 40.4382

# Step 3: 不偏分散・標本分散・標準偏差
print(f'Step3  不偏分散 s²  = {x.var(ddof=1):.4f}')   # 8.0876
print(f'Step3  標本分散 σ̂² = {x.var(ddof=0):.4f}')   # 6.7397
print(f'Step3  標準偏差 s   = {x.std(ddof=1):.3f}')    # 2.843

📤 実行例:

Step1 x̄ = 80.824 Step2 Σ(x-x̄)² = 40.4382 Step3 不偏分散 s² = 8.0876 Step3 標本分散 σ̂² = 6.7397 Step3 標準偏差 s = 2.843

💬 手計算 Step 1〜4 と Python 出力が完全一致。 「食料費のばらつき」は標準偏差 ≈ 2.84 千円 — 東北各県の食料費は平均 80.8 千円から ±2.84 千円の範囲でばらついていることが分かる。

🐍 Python での計算

このコードでやること: numpy と pandas の分散計算(ddof=0/1 の違い)、行列の列ごと分散、共分散行列の対角成分取得を実演する。

📥 入力データ(東北6県 食料費 SSDSE-B-2026 抜粋):

arr = [77.90, 82.00, 83.83, 78.12, 84.11, 78.98] (千円、青森〜福島)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np
import pandas as pd
np.random.seed(0)   # 実行のたびに同じ結果が出るようにする

# numpy のデフォルトは ÷n(標本分散)
arr = np.array([77.90, 82.00, 83.83, 78.12, 84.11, 78.98])
print(np.var(arr))         # 標本分散(÷n)
print(np.var(arr, ddof=1)) # 不偏分散(÷(n-1))

# pandas のデフォルトは ÷(n-1)(不偏分散)
s = pd.Series(arr)
print(s.var())             # 不偏分散
print(s.var(ddof=0))       # 標本分散

# 行列の分散(軸指定)
mat = np.random.randn(100, 5)
print(np.var(mat, axis=0))   # 各列の分散

# 共分散行列(分散は対角成分)
cov_mat = np.cov(mat, rowvar=False)
diag = np.diag(cov_mat)      # 各列の分散
📤 実行結果(実測) 6.746422222222208 # np.var(arr): 標本分散(ddof=0) 8.095706666666649 # np.var(arr, ddof=1): 不偏分散 8.095706666666649 # s.var(): pandas既定は不偏分散 6.746422222222208 # s.var(ddof=0): 標本分散 各列の分散: [1.01713631 0.91514001 1.00204315 1.06991421 0.9361394] # 正規乱数なので概ね 1 に近い ※ このブロックの arr は小数第 2 位に丸めた値なので、 手計算のブロック(77.899, 81.997, …)の 6.7397 とはわずかに違う。

💬 numpy のデフォルト(ddof=0)と pandas のデフォルト(ddof=1)が異なる点が最重要。 東北6県データでは標本分散 6.74、不偏分散 8.09 と約 20% の差が出る(n=6 と小さいため)。

ストリーミング計算(Welford's algorithm)

データを1個ずつ受け取る場合の安定アルゴリズム:

このコードでやること: データをオンラインで 1 個ずつ受け取りながら分散を数値安定に計算する Welford アルゴリズムの実装と動作確認。

📥 入力データ:

xs = [1, 2, 3, 4, 5] (シンプルな確認用データ、不偏分散の真値 = 2.5)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class StreamingVariance:
    def __init__(self):
        self.n = 0
        self.mean = 0.0
        self.M2 = 0.0

    def update(self, x):
        self.n += 1
        delta = x - self.mean
        self.mean += delta / self.n
        delta2 = x - self.mean
        self.M2 += delta * delta2

    def variance(self, unbiased=True):
        if self.n < 2: return float('nan')
        return self.M2 / (self.n - 1 if unbiased else self.n)

# 使用例
sv = StreamingVariance()
for x in [1, 2, 3, 4, 5]:
    sv.update(x)
print(sv.variance())  # 2.5(÷(n-1))
📤 実行結果: 2.5

💬 Welford アルゴリズムで [1,2,3,4,5] の不偏分散 2.5 が得られた(np.var([1,2,3,4,5], ddof=1) と完全一致)。 一括計算不要でメモリ効率が高く、数値的にも安定している。

🚧 分散の落とし穴

1️⃣ ÷n か ÷(n-1) か — 文脈で判断

numpy のデフォルトと pandas のデフォルトが違う点に注意。 ddof(delta degrees of freedom)パラメータで明示的に指定するのが安全。

2️⃣ 単位の混乱

分散は元の単位の二乗。 「分散 = 500千円²」は直感的でない。 解釈するときは標準偏差(√500 ≈ 22千円)に変換すべき。

3️⃣ 外れ値の影響

2乗するため、 外れ値1個が分散を大きく押し上げます。 外れ値ありデータでは MAD(中央絶対偏差)や IQR を併用するとよい。

4️⃣ 異なる単位の変数を比較できない

身長(cm)と体重(kg)の分散を直接比較するのは無意味。 変動係数 (CV = σ/μ) で無次元化するのが標準。

5️⃣ サンプルサイズが小さいと不安定

n=5 程度では分散の推定値は大きくブレます。 信頼区間(χ²分布ベース)を計算するか、 ブートストラップで分布を見るのが安全。

📜 分散の歴史

分散は単なる記述統計を超え、 推論・予測・最適化など現代統計学の中核に。

🐍 Python 実装バリエーション — numpy / pandas / scipy / statsmodels

1. numpy / pandas — 既定値の違いに注意

このコードでやること: numpy と pandas の var() 関数のデフォルト ddof の違いを最小例で確認する。

📥 入力データ: x = [1, 2, 3, 4, 5](不偏分散の真値 = 2.5、母分散 = 2.0)

1
2
3
4
5
6
7
import numpy as np
import pandas as pd
x = np.array([1, 2, 3, 4, 5])
print('numpy.var ddof=0:', np.var(x))           # 2.0  (母分散)
print('numpy.var ddof=1:', np.var(x, ddof=1))   # 2.5  (不偏分散)
print('pandas .var()   :', pd.Series(x).var())  # 2.5  (既定 ddof=1)
print('pandas .var(ddof=0):', pd.Series(x).var(ddof=0))  # 2.0
📤 実行結果: numpy.var ddof=0: 2.0 numpy.var ddof=1: 2.5 pandas .var() : 2.5 pandas .var(ddof=0): 2.0

💬 numpy のデフォルトは ddof=0(母分散 2.0)、pandas のデフォルトは ddof=1(不偏分散 2.5)で異なる。 ライブラリを混在させるときは必ず ddof を明示すること。

2. scipy.stats.tvar — トリム分散

このコードでやること: SSDSE-B-2026 の総人口データで、両端 10% をトリムした頑健な分散と MAD を計算する。

📥 入力データ: df['A1101'].values/1e4(47 都道府県、万人単位)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import numpy as np
import pandas as pd
from scipy import stats

# このブロックだけで完結させる(上のブロックの df に依存しない)
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]        # 2023 年の 47 都道府県
x = df['A1101'].astype(float).values / 1e4   # 万人単位
# 両端 10% をトリムした分散
print('トリム分散 :', stats.tvar(x, limits=(np.percentile(x, 10),
                                            np.percentile(x, 90))))
print('MAD       :', stats.median_abs_deviation(x))
print('MAD→SD換算:', stats.median_abs_deviation(x) * 1.4826)
トリム分散 : 18917.454249249247 MAD : 62.30000000000001 MAD→SD換算: 92.36598000000001

💬 通常の SD≈280 万人に対し MAD を SD 換算すると 92 万人。 東京都の外れ値が通常の分散/SD を 3 倍近くに膨らませていることが確認できる。

3. statsmodels.stats.weightstats — 重み付き分散

このコードでやること: 各都道府県の消費支出を総人口で重み付けして、全国の重み付き分散を計算する。人口の大きい県に多くの重みを置いた全国加重平均に相当する。

📥 入力データ: df['L3221'].values(消費支出)と df['A1101'].values(総人口=重み)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import pandas as pd
from statsmodels.stats.weightstats import DescrStatsW

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
cons = df['L3221'].astype(float).values / 1e3   # 消費支出(千円)
pop  = df['A1101'].astype(float).values / 1e4   # 総人口(万人、重み)

ds = DescrStatsW(cons, weights=pop, ddof=1)
print('重み付き平均:', round(ds.mean, 2))
print('重み付き分散:', round(ds.var, 2))
print('重み付き SD :', round(ds.std, 2))
print('単純平均    :', round(cons.mean(), 2), ' 単純分散:', round(cons.var(ddof=1), 2))
📤 実行結果: 重み付き平均: 304.16 重み付き分散: 603.8 重み付き SD : 24.57 単純平均 : 295.86 単純分散: 582.94

💬 人口加重平均 304.16 千円は単純平均 295.86 千円より高い。消費支出が多い都市部の県は人口も多いため、加重すると全国平均が上がる。重み付き分散 603.80 で「人口按分した真の全国格差」が計算できる。

4. numpy.cov — 分散共分散行列

このコードでやること: SSDSE-B-2026 の「総人口・65歳以上人口・消費支出」3 変数の分散共分散行列と相関行列を計算し、分散が対角成分に現れることを確認する。

📥 入力データ: df[['A1101','A1303','L3221']](47 県 × 3 変数、人口は万人・消費支出は千円に換算)

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

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
X = np.column_stack([
    df['A1101'].astype(float).values / 1e4,   # 総人口(万人)
    df['A1303'].astype(float).values / 1e4,   # 65歳以上人口(万人)
    df['L3221'].astype(float).values / 1e3])  # 消費支出(千円)

S = np.cov(X.T, ddof=1)
print('分散共分散行列:'); print(np.round(S, 1))
print('相関行列    :'); print(np.round(np.corrcoef(X.T), 3))
📤 実行結果: 分散共分散行列: [[78262.9 19235.4 2246.2] [19235.4 4814.1 537.3] [ 2246.2 537.3 582.9]] 相関行列 : [[1. 0.991 0.333] [0.991 1. 0.321] [0.333 0.321 1. ]]

💬 分散共分散行列の対角成分([0,0]=78262.9 が総人口 s²、[2,2]=582.9 が消費支出 s²)が各変数の不偏分散と一致している。相関行列は分散で標準化したもので、総人口と65歳以上人口は r=0.991 と強く相関する一方、消費支出との相関は r≈0.33 と弱い。

5. ブートストラップで分散の 95%CI

このコードでやること: scipy.stats.bootstrap で消費支出の分散の 95% 信頼区間を経験的に推定する。n=47 と小さいため、χ² 分布の仮定が怪しい場面でのロバストな代替手段。

📥 入力データ: df['L3221'].values(47 件、不偏分散 ≈ 582.94 千円²)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import numpy as np, pandas as pd
from scipy.stats import bootstrap

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
x = df['L3221'].astype(float).values / 1e3   # 消費支出(千円)

def var_unbiased(a, axis=0):
    return np.var(a, axis=axis, ddof=1)

res = bootstrap((x,), var_unbiased, n_resamples=5000, confidence_level=0.95)
print(f'点推定  不偏分散 = {var_unbiased(x):.2f}')
print(f'分散の 95%CI = {res.confidence_interval}')
📤 実行結果(乱数のため実行ごとに多少変動): 点推定 不偏分散 = 582.94 分散の 95%CI = ConfidenceInterval(low=392.9, high=964.7)

💬 ブートストラップ 5000 回で得た分散の 95% 信頼区間は [393, 965](千円²)。点推定 582.94 を中心に非対称に広がる。n=47 と小さいため χ² 分布前提の区間推定より頑健で、非正規データや小標本では scipy.stats.bootstrap が安全な代替手段。

6. オンライン分散(Welford 法、 大規模対応)

このコードでやること: WelfordVariance クラスを定義し、SSDSE-B-2026(2023 年)の消費支出データを 1 件ずつ update() で逐次処理することで、一括計算と同じ不偏分散が得られることを確認する。メモリに全データを載せられないストリーミング処理に対応した数値安定アルゴリズムの実装例。

📥 入力データ: df['L3221'].values(SSDSE-B-2026、47 件、不偏分散 ≈ 582.94 千円²)

 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

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

class WelfordVariance:
    def __init__(self):
        self.n = 0
        self.mean = 0.0
        self.M2 = 0.0
    def update(self, x):
        self.n += 1
        delta = x - self.mean
        self.mean += delta / self.n
        self.M2 += delta * (x - self.mean)
    @property
    def variance(self):
        return self.M2 / (self.n - 1) if self.n > 1 else float('nan')

w = WelfordVariance()
for v in df['L3221'].astype(float).values / 1e3:   # 消費支出(千円)
    w.update(v)
print(round(w.variance, 2))
📤 実行結果: 582.94

💬 WelfordVariance クラスで消費支出 47 件を逐次処理した結果 582.94 は、np.var(df['L3221']/1e3, ddof=1) = 582.94 と完全一致。ストリーミング処理でもバッチ計算と同じ精度が得られることを確認できる。

🐍 stratified variance (層別分散) — サンプリング設計を反映した分散

調査票や行政統計では「単純無作為標本」ではなく「層化抽出 (例: 都道府県別、 年齢層別)」を行うことが多い。 このとき「層内分散の加重平均」が真の母分散の良い推定値となる。 通常の np.var はサンプリング設計を考慮しないため、 過大評価することがある。

このコードでやること: SSDSE 47 県人口を 8 地方ブロックに層化したとみなし、 単純分散と層別分散 (= 群内分散の加重平均) を比較する。 「群間のばらつきを除いた純粋な層内ばらつき」が層別分散だ。

📥 入力データ(SSDSE-B-2026 A1101 総人口、47 件):

都道府県 A1101(万人) 地方ブロック(層) 北海道 509.2 北海道(層 1 / n=1) 青森県 118.4 東北 (層 2 / n=6) ... 沖縄県 146.8 九州沖縄(層 8 / n=8) (単純分散 vs 層別加重平均分散を比較)
 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
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
pop = (df.groupby('Prefecture')['A1101'].first() / 1e4).reset_index()

region_map = {'北海道':'北海道',
    '青森県':'東北','岩手県':'東北','宮城県':'東北','秋田県':'東北','山形県':'東北','福島県':'東北',
    '茨城県':'関東','栃木県':'関東','群馬県':'関東','埼玉県':'関東','千葉県':'関東','東京都':'関東','神奈川県':'関東',
    '新潟県':'中部','富山県':'中部','石川県':'中部','福井県':'中部','山梨県':'中部','長野県':'中部','岐阜県':'中部','静岡県':'中部','愛知県':'中部',
    '三重県':'近畿','滋賀県':'近畿','京都府':'近畿','大阪府':'近畿','兵庫県':'近畿','奈良県':'近畿','和歌山県':'近畿',
    '鳥取県':'中国','島根県':'中国','岡山県':'中国','広島県':'中国','山口県':'中国',
    '徳島県':'四国','香川県':'四国','愛媛県':'四国','高知県':'四国',
    '福岡県':'九州沖縄','佐賀県':'九州沖縄','長崎県':'九州沖縄','熊本県':'九州沖縄','大分県':'九州沖縄','宮崎県':'九州沖縄','鹿児島県':'九州沖縄','沖縄県':'九州沖縄',
}
pop['region'] = pop['Prefecture'].map(region_map)

simple = pop['A1101'].var(ddof=0)

# 層別分散 = Σ (n_h / N) * s_h²
N = len(pop)
strat = 0.0
for r, sub in pop.groupby('region'):
    if len(sub) >= 2:
        strat += (len(sub)/N) * sub['A1101'].var(ddof=0)

print(f'単純分散 (全 47 県を 1 群と見たとき)        = {simple:.1f}')
print(f'層別分散 (8 地方ブロックを層化抽出と見たとき) = {strat:.1f}')
print(f'比 strat / simple                           = {strat/simple:.3f}')
print(f'差 simple - strat (= 群間分散 / N)          = {simple - strat:.1f}')

📤 実行例:

単純分散 (全 47 県を 1 群と見たとき) = 76597.8 層別分散 (8 地方ブロックを層化抽出と見たとき) = 48125.9 比 strat / simple = 0.628 差 simple - strat (= 群間分散 / N) = 28471.8

💬 結果の読み方: 層別分散は単純分散の約 63 %。 つまり「地方ブロックで層化すると、 群間分散 (28472) を除いた純粋な層内ばらつきだけが残る」。 サンプリング設計上、 層化抽出を行うと推定値の分散が小さくなり、 同じ精度を少ないサンプル数で達成できる (層化抽出の効率性)。 これは社会調査・市場調査でしばしば「層別抽出の標本サイズ計算」として用いられる重要な計算だ。

🎯 理解度チェック — 分散

「分散」と一口に言っても、 母分散 / 標本分散 / 不偏分散 / 群内・群間分散 と多義語的に使われる。 ここでは 分散の本質と現場での使い分け を 5 問で確認する。

  1. Q1. 標本分散の式 $s^2 = \frac{1}{n}\sum (x_i - \bar{x})^2$ と不偏分散 $u^2 = \frac{1}{n-1}\sum (x_i - \bar{x})^2$ の違いは何か? なぜ $n-1$ で割るのか?
  2. Q2. 分散 $\sigma^2$ と標準偏差 $\sigma$ の単位はどう違うか? SSDSE-B-2026 の県別人口の分散を答案で書くとき、 単位は「人」「人²」のどちらか?
  3. Q3. $\mathrm{Var}(aX+b)$ を $a, b, \mathrm{Var}(X)$ で表すと? 定数 $b$ は分散に影響しないことを直感的に説明できるか?
  4. Q4. 二つの独立変数 $X, Y$ について $\mathrm{Var}(X+Y)$ はどうなる? $X, Y$ が独立でない場合の補正項は?
  5. Q5. SSDSE-B-2026 を 8 地方ブロックで層化したとき、 全体分散 = 層内分散 + 層間分散 と分解できる。 もし層内分散がほぼゼロで層間分散だけが大きいデータだったら、 何を意味するか?

解答の要点(自己採点用)

  • A1: 標本分散は系統的に母分散を 過小評価 する (バイアス)。 $n-1$ で割ると不偏推定量になる ($\bar{x}$ を使ったことで失う自由度 1 つを差し引く)。
  • A2: 分散の単位は元の単位の 2 乗。 県別人口の分散は「人²」 (解釈しづらいので普通は √ をとって標準偏差「人」で表記)。
  • A3: $\mathrm{Var}(aX+b) = a^2 \mathrm{Var}(X)$。 定数 $b$ は全体を平行移動するだけで「ばらつき」は変わらないから影響しない。
  • A4: 独立なら $\mathrm{Var}(X+Y) = \mathrm{Var}(X) + \mathrm{Var}(Y)$。 独立でないなら $+ 2\mathrm{Cov}(X,Y)$ を加える。
  • A5: 「同じ地方の県は似ているが、 地方間では大きく違う」状態。 例: 関東 vs 四国の所得格差。 層化抽出の効率が極めて高い設計シグナル。

🛠 5 分でできる自分で確かめる課題

SSDSE-B-2026 の「消費支出(二人以上の世帯)」を 47 都道府県分とりだし、 (1) NumPy np.var(x, ddof=0)np.var(x, ddof=1) の差を計算、 (2) 8 地方ブロックでグループ化した groupby().var() を表示、 (3) 層内分散の平均と層間分散を計算し、 全体分散 ≈ その合計になることを確認してみよう。 数式の分散分解が 実データで成立する ことを体感できる。

⚠️ 分散の落とし穴 — 6 つの典型ミス

① ddof=0 と ddof=1 を取り違える

numpy.var() の既定は ddof=0(母分散)、 pandas Series.var() の既定は ddof=1(不偏分散)と、 主要ライブラリでデフォルト値が違う。 これに気づかず numpy と pandas を混在させると、 nが小さいデータで結果が 5〜10% 単位でズレる。 学術論文や検定で使うのは「サンプルから母数を推定する」目的なので ddof=1(n-1 で割る)が正しい。 必ず明示する習慣をつけ、 既定任せにしない。

② 単位の二乗を忘れる

「身長(cm)」の分散は cm² であり、 元データと単位が違う。 そのため分散の絶対値は直感的に解釈しにくく、 報告は標準偏差(SD = √分散、 単位は元と同じ)で行うのが原則。 「年収のばらつきが分散 1.2 × 10⁹ 円²」と書くより「SD ≈ 35,000 円」の方がずっと意味が分かる。 査読でも SD で報告するよう要求されることが多い。

③ 外れ値で分散が劇的に膨らむ

分散は偏差を二乗するため、 外れ値 1 個で大きく動く。 47 都道府県で東京を含めると総人口の SD が約 280 万人、 外すと約 224 万人、 と大きく変わる。 ロバストな散布度として IQR(四分位範囲)、 MAD(中央絶対偏差)が代替に使える。 MAD = median(|x - median(x)|) を SD に変換するなら ×1.4826(正規分布前提)。 外れ値の処理を明示せずに分散を比較しない。

④ 加法性を「独立でない変数」にも適用する

「Var(X+Y) = Var(X) + Var(Y)」は X と Y が独立(または無相関)のときだけ成立。 相関がある場合は Var(X+Y) = Var(X) + Var(Y) + 2·Cov(X,Y) が正しい。 共分散項を忘れて足し算するとポートフォリオのリスクを誤算する。 SSDSE データでも「15歳未満人口 + 65歳以上人口」は強い正の相関を持ち、 共分散項を無視した単純加算では合わない。 リスク管理・ファイナンスでは共分散項が本質。

⑤ 「分散が小さい = 推定が正確」と誤解する

分散はデータのばらつきを測るが、 推定量の精度を測るのは「標準誤差(SE = SD/√n)」。 SE = SD ではない。 サンプルサイズ n を増やせば SE は √n に反比例して下がるが、 SD(母集団の真のばらつき)は変わらない。 「分散が大きい / 小さい」と「推定が不確実 / 確実」は別概念で、 両者を混同するのは初学者の典型ミス。

⑥ スケールが違う変数同士の分散を直接比較する

「総人口の分散」と「失業率の分散」を直接比較しても意味がない(単位が違う)。 単位無次元の比較には変動係数 CV = SD/Mean を使う。 ただし平均が 0 に近い変数(収益率など)では CV が爆発するので、 z-スコア化(標準化)して比較するか、 ロバストな統計(中央値・MAD)を使う方が安全。 PCA や Lasso 等の機械学習でスケーリングが必須なのも同じ理由。

🗺️ 概念マップ — 3つの視点で体系を理解する

分散 がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。

📍 体系階層のパス

🌐 統計・データサイエンス記述統計ばらつき分散

📌 分散の手法系譜(静的マップ)

「分散」を中心に、前提・派生・応用の手法系譜を静的 SVG で示す。

平均値 前提 分散 σ² 記述統計の核 標準偏差 SD = √分散 IQR・MAD 並列 共分散 2変数へ 相関係数 r ÷SD PCA 分散最大化 ANOVA 分散分解 R²(決定係数) 分散比率 バイアス-バリアンス分解 機械学習へ 前提 並列 応用先 発展

① 🔗 関係マップ — 「他の手法とどう繋がっているか」

中心に 分散 を置き、 そこから 平均・標準偏差・共分散・R²・PCA・相関係数 など 計 14 個の用語へ、 前提・兄弟・発展形といった関係を矢印で結んでいます。 線がどちら向きかを見れば、 先に読むべき用語あとから読む用語が分かります。 ノードはドラッグで動かせ、 ホイールでズーム、 クリックでその用語のページへ遷移します。

凡例:現在の用語上位カテゴリ兄弟(並列)前提発展形応用先2階層先

② ⭕ 包含マップ — 「どのカテゴリに含まれているか」

大きな円が小さな円を包含する Circle Packing 図。 「分散」は緑色でハイライト

📍現在地:統計・データサイエンス

③ 🌳 ツリーマップ — 「面積で見るボリューム比較」

長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「分散」は緑色でハイライト

🎯 3つのマップの使い分け

マップ 分かること こんな時に見る
🔗 関係マップ手法間の横の関係(前提→発展→応用)「次に何を学べばよい?」 学習順序の判断
⭕ 包含マップ分類体系の入れ子構造(上位⊃下位)「この手法はどんなジャンルに属する?」
🌳 ツリーマップ分野の規模比較(面積=ボリューム)「データサイエンス全体の俯瞰像」

💡 3 つの図は同じ位置関係を別の角度から描いています。 関係マップは 分散隣に何が並ぶかを、 包含マップとツリーマップは 統計・データサイエンスばらつき → 分散 という入れ子の位置を示します。 「ばらつきには他に何があったか」を思い出したいときは包含マップを、 「次に何を読むか」を決めたいときは関係マップを開いてください。

🔗 隣接手法への橋渡し

「分散」は単独で完結する指標ではなく、 隣接する手法・概念と連携することで真価を発揮する。

方向 手法・概念 接続の意味
前提 (上流) 平均値代表値 分散の計算には平均 x̄ が必要。 平均を理解していないと偏差の意味が取れない。
並列 (横) 標準偏差IQRMAD 同じ「散布度」ファミリー。 分散は 2乗単位で数学的扱いやすさ最優先。 外れ値に頑健にしたいなら IQR・MAD を使う。
発展 (下流) 標準偏差 = √分散 単位を元に戻すだけで直感的になる。 報告・可視化では標準偏差が標準形式。
応用1 共分散相関係数 「2変数の分散の掛け算」が共分散。 それを各変数の標準偏差で割ると相関係数 r ∈ [−1,1] になる。
応用2 分散分析 ANOVA 全分散を群内・群間に分解して F 比を計算。 「分散の分解」がアルゴリズムの核。
応用3 主成分分析 PCA 「分散を最大化する方向」を逐次的に求める次元削減手法。 分散が「情報量」として直接使われる。
応用4 バイアス・バリアンス分解 モデル予測誤差の「Variance」成分 = モデルが学習データに依存してぶれる度合い。 過学習の定量指標。
応用5 R²(決定係数) R² = 1 − 残差分散/全分散。 回帰モデルが「全分散のうち何割を説明したか」を示す。
応用6 標準化(z-スコア) 「(値 − 平均) / 標準偏差」で平均0・分散1に変換する前処理。 機械学習のスケール問題を解決する。

💡 分散を起点にすると「相関 → 回帰 → PCA → ANOVA → 機械学習」という統計学の大きな流れが1本の糸で繋がる。 分散こそが現代統計の基礎概念と言える。

🌳 手法選択フロー — 分散・散布度の使い分け

「データのばらつきを測りたい」という状況で何を選ぶかを、 条件ごとに整理する。

状況・条件 推奨手法 理由
正規分布に近い連続データ、 外れ値なし 標準偏差(不偏分散の√) 単位が元データと同じで直感的。 最も普及している報告形式
外れ値が含まれる、 歪んだ分布 IQR または MAD 分散は偏差を2乗するため外れ値に過敏。 IQR・MAD は中央値ベースで頑健
単位の異なる複数変数を比較 変動係数 CV = σ/μ 無次元化された相対的ばらつき。 「人口」と「失業率」の散布度を同じ軸で比較できる
母集団の分散を標本から推定する(推論統計) 不偏分散(ddof=1) ÷n だと系統的に過小評価(バイアス)。 ÷(n-1) で不偏推定量になる
標本そのものを要約する(記述統計) 標本分散(ddof=0) 集めたデータそのものの散布度を示す。 機械学習の最尤推定もこちら
グループ間のばらつきと群内のばらつきを分離 ANOVA(分散分解) 全分散 = 群間 + 群内 の分解。 F 検定で「群差は有意か」を判断
複数変数間のばらつきの相互関係も知りたい 分散共分散行列 対角成分が各変数の分散、 非対角成分が共分散。 PCA の入力行列
大量データのストリーミング処理で分散を計算 Welford アルゴリズム 1 パスで数値安定に分散を更新できる。 一括計算より桁落ちに強い
分散の信頼区間を求めたい(n が小さい) χ² 分布 または ブートストラップ 正規分布前提なら (n-1)s²/σ² が χ²(n-1) に従う。 非正規ならブートストラップが安全

💡 実務の鉄則: 報告書・論文では「分散」ではなく「平均 ± SD(標準偏差)」の形で示す。 外れ値があるなら「中央値 (IQR)」に切り替える。 単位が異なる変数を比べるなら CV。 この3つを使い分けるだけで散布度の表現力が格段に上がる。

🧭 解説の深化 — 分散を「計算する」視点から捉え直す

ここまでのセクションは分散の定義と解釈(偏差二乗の平均・n−1・外れ値感度)を扱った。 この追補では角度を変えて、 恒等式「二乗の平均 − 平均の二乗」を軸に、 分散をコンピュータで実際に計算するときに何が起きるかを SSDSE-B-2026 の実測値で確かめる。 群間・群内の分解やσ収束は 散布度、 68-95-99.7 則やチェビシェフの不等式は 標準偏差 のページで扱っているため、 ここでは重複させない。

🎨 直感 — 分散は「二乗の平均」から「平均の寄与」を除いた残り

計算用の別形式 $$ s^2_n = \overline{x^2} - \bar{x}^2 $$ を、 「式変形の結果」ではなく意味として読んでみる。 二乗の平均 $\overline{x^2}$ には「データの大きさ(水準)」と「ばらつき」の両方が混ざっている。 そこから水準だけの成分 $\bar{x}^2$ を差し引くと、 純粋な「ばらつき」が残る——これが分散。

実測(SSDSE-B-2026、 2023 年・47 都道府県、 消費支出 L3221〔二人以上の世帯・円〕):

値(実測)読み方
平均 $\bar{x}$295,856.0 円水準(最大: 埼玉県 344,092 円、 最小: 愛媛県 223,423 円)
二乗の平均 $\overline{x^2}$88,101,317,933 円²水準+ばらつきの混合
平均の二乗 $\bar{x}^2$87,530,785,326 円²水準だけの成分
差=母分散 $s^2_n$570,532,607 円²純粋なばらつき(不偏分散 s² = 582,935,490 円²、 SD ≈ 24,144 円)

注目すべきは比率:ばらつき成分は $\overline{x^2}$ のわずか 0.65% しかない。 つまり「二乗の平均」の 99.3% 以上は平均(水準)の寄与で、 分散は巨大な 2 つの数のごく僅かな差として得られている。 この構造が、 次の「落とし穴」の伏線になる。

⚠️ 落とし穴(重要) — ショートカット公式は「桁落ち」で壊れる

「二乗の平均 − 平均の二乗」は 1 パスで計算できて便利だが、 上で見たとおりほぼ等しい巨大な数同士の引き算である。 浮動小数点では、 近い数を引くと上位の桁が打ち消し合い有効桁が失われる(桁落ち・catastrophic cancellation)。

💡 教訓:数式として等価でも計算手順として等価とは限らない。 ショートカット公式は「手計算・厳密計算」用、 コンピュータでは定義式(2 パス)か次の Welford 法を使う。

🚀 発展 — Welford の逐次更新:1 パスでも数値安定に

「2 パスは安定だがデータを 2 回読む必要がある。 ストリーミングでは 1 回しか読めない」——この両立を解くのが Welford のアルゴリズム(1962)。 平均 m と偏差平方和 M2 を 1 件ずつ更新する:

k 件目の値 x が届くたびに: delta = x − m m = m + delta / k # 平均の逐次更新 M2 = M2 + delta × (x − m) # 更新前後の偏差の積を加算 分散: 母分散 = M2/k , 不偏分散 = M2/(k−1)

実測: 消費支出 L3221 の 47 件を Welford 法で 1 パス処理すると不偏分散 582,935,490.2 円²—— numpy.var(ddof=1) と完全に一致する。 巨大な数同士の引き算が現れないため桁落ちが起きない。