論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
対数変換
Log Transformation
データ前処理
別称: log変換 / logarithmic transformation

🔖 キーワード索引

対数変換 log 右裾 正規化 スケーリング Box-Cox Yeo-Johnson log-log プロット Shapiro-Wilk 弾力性 log1p 対数正規分布

対数変換 (log transformation)」はy → log(y) で右裾の長い分布を縮め、 乗法的な関係を加法的に変える前処理。 本ページの中核キーワードを以下に整理する。

y → log(y)log1p (log(1+x))対数正規分布右裾 (right skew) の縮小Box-Cox 変換 (λ 推定)Yeo-Johnson (負値 OK)弾性値 (elasticity)対数線形モデル log-log分散安定化変換0 値処理 (log(x+1))SSDSE-B-2026 人口・所得

これらのキーワードは「log transformation の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。

💡 30秒で分かる結論

🍰 まずはやさしく

データの大きさをギュッと縮める魔法です。

バラつきを抑えて分析しやすくするために使います。

スマホのアプリの売上など、差が激しい量に便利です。

この章では対数変換のやり方と効果を学びます。

対数変換 ── 右に裾を引く分布を対数で圧縮し、 正規分布に近づける前処理。

📍 文脈 ── どこで出会うか

🍰 まずはやさしく

桁が大きく違うデータを扱うための準備です。

統計の計算を正しく行うために使います。

都道府県ごとの人口の差などを比べる時に役立ちます。

ここではデータの変換と計算への影響を読みます。

所得、 人口、 売上、 株価変動、 ファイルサイズ――身のまわりの「桁が違う」量はほぼすべて対数変換の対象。 統計モデリングの標準前処理です。

本ページでは対数変換 (log transformation) を扱う。 右に裾の長い分布 (所得・売上・株価など) を log(x) で対称化し、 正規分布に近づける変数変換。 SSDSE-B-2026 の総人口や出生数などの右裾データに適用して、 回帰モデルの前提条件を満たす例で具体化する。

対数変換は「乗法的関係 → 加法的関係」への翻訳でもある。 y = a·x^b を log すると log(y) = log(a) + b·log(x) と直線になり、 冪関数の指数 b が線形回帰で推定できる。 0 や負値があるときの log(x+1) シフト、 予測値を元スケールに戻す逆変換時の Jensen 不等式補正も重要論点。

🎨 直感で掴む

🍰 まずはやさしく

数字の差を比率で考える方法です。

極端に大きい値に振り回されないようにします。

買い物で使う金額の桁が違うときのような感覚です。

具体的に数字がどう変わるかを例で見ていきましょう。

なぜ対数変換するのか

桁が違う量」を比較するときの定番手法。 人口、 所得、 株価、 ファイルサイズ、 細菌数 ── これらは 「数桁にまたがる」 量で、 そのまま扱うとヒストグラムは右に長い尾を引き、 平均値が外れ値に引きずられる。 対数を取ることで「比率の世界」に持ち込み、 ほぼ正規分布で扱えるようになる。

物語①: SSDSE-B-2026 の都道府県人口

都道府県人口log10(人口)ln(人口)
東京都14,086,0007.14916.461
神奈川県9,229,0006.96516.038
大阪府8,763,0006.94315.986
福井県744,0005.87213.520
徳島県695,0005.84213.452
高知県666,0005.82313.409
島根県650,0005.81313.385
鳥取県537,0005.73013.193

東京と鳥取の 原値の比は 26 倍 だが、 log10 では差が 1.42 (鳥取 5.73 → 東京 7.15) に圧縮される。 これは「対数の差 = 元の比の対数」という性質:$\log(14{,}086{,}000) - \log(537{,}000) = \log(14086000/537000) = \log(26.2) ≈ 1.42$。

ヒストグラムの比較

原データ ヒストグラム (右に長い尾):
人口 (万人)
   0-100   ████████████████████████████████ 24 県
 100-500   █████████████ 18 県
 500-1000  ████ 4 県
1000-1500  █ 1 県 ← 東京 (外れ値)

log10(人口) ヒストグラム (ほぼ対称):
log10値
 5.5-5.8   ███ 3 県
 5.8-6.1   ██████████ 10 県
 6.1-6.4   ██████████████ 14 県  ← ピーク
 6.4-6.7   █████████████ 13 県
 6.7-7.0   █████ 5 県
 7.0-7.2   ██ 2 県  ← 東京・神奈川
   

対数を取った瞬間、 ヒストグラムは 「右尾なし・ピーク中央」 の正規分布に近い形に。 平均と中央値の差も大幅縮小(後述の数値検証参照)。

物語②: 株価リターンと対数

「日次リターン」($P_t/P_{t-1} - 1$)と「対数リターン」($\ln(P_t/P_{t-1})$)。 ファイナンスでは 対数リターン を使うのが定石。 理由: ① 時間方向に加法的(週次リターン = 5 日分の対数リターンの和)、 ② 正規分布の仮定が成り立ちやすい、 ③ 大きな下落と上昇が対称的に扱える。

物語③: Weber-Fechner の法則

人間の感覚は刺激の 対数 に比例する(音の dB、 光の明るさ、 地震のマグニチュード)。 つまり、 「100 → 200」と「1000 → 2000」を 同じ「2 倍の変化」 として感じる。 対数変換はこの「人間の感覚」と整合的な世界に持ち込む操作でもある。

📐 定義/数式

🍰 まずはやさしく

掛け算を足し算に変える計算ルールです。

数式を使ってデータを変換するために使います。

テストの点数のように、0があるデータへの対策も必要です。

変換に使う数式や、いくつかの種類について解説します。

対数変換にはバリエーションがあるが、 すべて 「乗法を加法に変換する」 性質に根ざす:

基本: 自然対数変換
$$ y' = \ln(y) \quad (y > 0) $$ $$ \ln(ab) = \ln a + \ln b, \quad \ln(a/b) = \ln a - \ln b, \quad \ln(a^n) = n \ln a $$
ゼロ対策: log1p (log(1+x))
$$ y' = \ln(1 + y) \quad (y \geq 0) $$
$y=0$ でも $\ln(1+0)=0$ と定義され NaN にならない。 小さい $y$ で精度が高い NumPy の log1p を使うのが定番。
Box-Cox 変換 (1964)
$$ y'(\lambda) = \begin{cases} \dfrac{y^\lambda - 1}{\lambda} & (\lambda \neq 0) \\[6pt] \ln y & (\lambda = 0) \end{cases} \quad (y > 0) $$
$\lambda$ を最尤推定で決める。 $\lambda = 0$ で自然対数、 $\lambda = 1$ で恒等(変換なし)、 $\lambda = 0.5$ で平方根。
Yeo-Johnson 変換 (2000)
$$ y'(\lambda) = \begin{cases} \dfrac{(y+1)^\lambda - 1}{\lambda} & y \geq 0, \lambda \neq 0 \\[4pt] \ln(y+1) & y \geq 0, \lambda = 0 \\[4pt] -\dfrac{(-y+1)^{2-\lambda} - 1}{2-\lambda} & y < 0, \lambda \neq 2 \\[4pt] -\ln(-y+1) & y < 0, \lambda = 2 \end{cases} $$
負値も扱える Box-Cox の一般化。 scikit-learn の PowerTransformer で標準サポート。

底の違い

表記主な用途
$\ln x$ または $\log x$$e \approx 2.718$統計、 微分計算、 機械学習のデフォルト
$\log_{10} x$10桁感覚 (pH、 dB、 地震)、 ヒストグラム軸
$\log_2 x$2情報量、 ビット数、 倍々ゲーム

底変換公式: $\log_b x = \ln x / \ln b$。 統計分析は底に依存しないので、 通常は ln で書く。

🔬 数式を言葉で読み解く

数式を実例で「翻訳」するセクション。 対数の 3 つの本質 を抑えれば、 ほとんどの応用に対応できる。

① $\ln(ab) = \ln a + \ln b$ ── 「掛け算 → 足し算」
対数の最も重要な性質。 「複利」「成長率」「掛け算が連鎖する現象」を 足し算で扱える。 例: 「年率 5% で 10 年運用」は $1.05^{10} = ?$ より $\ln(1.05) \times 10 = 0.0488 \times 10 = 0.488$ → $e^{0.488} = 1.629$(約 63% 増)。
② $\ln(a/b) = \ln a - \ln b$ ── 「比 → 差」
「東京 vs 鳥取の人口比 = 26 倍」は対数空間で $\ln 14086000 - \ln 537000 = 3.265$(差として扱える)。 散布図で「比率」を見たいときに使う log-log プロットの数学的根拠。
③ $\ln(a^n) = n \ln a$ ── 「ベキ → 線形」
ベキ法則 $y = a x^b$ は、 両辺対数取ると $\ln y = \ln a + b \ln x$ という直線になる。 ジップの法則、 都市規模分布、 物理の散乱断面積 ── 自然界のベキ法則を「直線回帰」で扱える。
④ 微分: $\dfrac{d}{dx}\ln x = \dfrac{1}{x}$ ── 「相対変化」
$\Delta \ln x \approx \Delta x / x$ という近似が成り立つ(小さな変化のとき)。 つまり「log の差 ≈ パーセント変化」。 株価リターン 1% は $\ln$ リターン ≒ 0.01。
⑤ 回帰係数の解釈
回帰式の左右どちら(あるいは両方)を log にするかで、 係数の意味が変わる:
モデル$x$ が 1 単位増/1% 増
$y = \alpha + \beta x$$y$ が $\beta$ 増
$\ln y = \alpha + \beta x$$y$ が $(e^\beta - 1) \times 100\%$ 増 (約 $100\beta\%$)
$y = \alpha + \beta \ln x$$y$ が $\beta / 100$ 増 (x が 1% 増のとき)
$\ln y = \alpha + \beta \ln x$弾力性: $x$ が 1% 増 → $y$ が $\beta\%$ 増
⑥ 対数正規分布
$X$ が対数正規分布なら $\ln X$ は正規分布。 所得分布、 人口分布、 企業規模分布など 「比率で揺れる」 量は対数正規になりやすい。 中心極限定理の「乗法版」:多数のランダムな乗法効果の積は対数正規分布に近づく。

⚡ Tips: ⑤の表の中で最も実務でよく使うのは log-log モデル(弾力性)。 経済学・マーケティング・物理で多用される。 「需要の価格弾力性 = $-1.2$」と言ったら、 価格 1% 上昇で需要 1.2% 低下を意味する。

🔬 対数変換とその一般化: Box-Cox / Yeo-Johnson 変換

対数変換 ($y' = \log(y)$) は「右に裾の長い分布 (right-skewed)」を正規分布に近づける万能薬として知られるが、 ゼロや負の値には適用できない。 これを一般化したのが Box-Cox 変換 (1964) と Yeo-Johnson 変換 (2000)。 SSDSE-B-2026 の都道府県総人口・出生数・高齢者人口 等は対数正規分布に近く、 これらの変換で歪度 (skewness) と正規性を改善できる。

変換定義対応範囲パラメータ
対数変換$\log(y)$$y > 0$なし
log1p$\log(1+y)$$y \geq 0$ (0 含む)なし
Box-Cox$(y^\lambda - 1)/\lambda$ ($\lambda \neq 0$)$y > 0$$\lambda$ (最尤推定)
Yeo-Johnson場合分け式 (下記)$y \in \mathbb{R}$ (負も可)$\lambda$ (最尤推定)

🔬 数式を言葉で読み解く: Yeo-Johnson 変換

$$ y' = \begin{cases} ((y+1)^\lambda - 1)/\lambda & \text{if } \lambda \neq 0, y \geq 0 \\ \log(y+1) & \text{if } \lambda = 0, y \geq 0 \\ -((-y+1)^{2-\lambda} - 1)/(2-\lambda) & \text{if } \lambda \neq 2, y < 0 \\ -\log(-y+1) & \text{if } \lambda = 2, y < 0 \end{cases} $$

「正の値は Box-Cox 風、 負の値は符号反転して同様の変換」という発想。 $\lambda = 0$ なら対数変換、 $\lambda = 1$ なら恒等変換 (変換なし)、 $\lambda = 0.5$ なら平方根に近い変換。 sklearn の PowerTransformer はデフォルトで Yeo-Johnson を採用し、 ゼロ・負の値が混在するデータでも安全に正規化できる。

このコードでやること: SSDSE-B-2026 の都道府県総人口 (A1101) に対し、 対数変換 / Box-Cox / Yeo-Johnson を適用し、 歪度・正規性検定の改善度を比較する。

📥 入力データ (SSDSE-B-2026・2023年・A1101=総人口):

都道府県 総人口(人) 東京都 14,086,000 神奈川県 9,229,000 大阪府 8,763,000 ... 島根県 650,000 鳥取県 537,000
 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 scipy import stats
from sklearn.preprocessing import PowerTransformer

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023]
y = pd.to_numeric(d['A1101'], errors='coerce').dropna().values.astype(float)  # 総人口

# 1. 元データの歪度・正規性
print(f'元データ:       skewness = {stats.skew(y):.3f}, Shapiro p = {stats.shapiro(y).pvalue:.4f}')

# 2. log 変換
y_log = np.log(y)
print(f'log 変換:       skewness = {stats.skew(y_log):.3f}, Shapiro p = {stats.shapiro(y_log).pvalue:.4f}')

# 3. Box-Cox
y_bc, lam_bc = stats.boxcox(y)
print(f'Box-Cox (lam={lam_bc:.3f}): skewness = {stats.skew(y_bc):.3f}, Shapiro p = {stats.shapiro(y_bc).pvalue:.4f}')

# 4. Yeo-Johnson
pt = PowerTransformer(method='yeo-johnson')
y_yj = pt.fit_transform(y.reshape(-1, 1)).ravel()
print(f'Yeo-Johnson (lam={pt.lambdas_[0]:.3f}): skewness = {stats.skew(y_yj):.3f}, Shapiro p = {stats.shapiro(y_yj).pvalue:.4f}')

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

元データ: skewness = 2.219, Shapiro p = 0.0000 log 変換: skewness = 0.793, Shapiro p = 0.0064 Box-Cox (lam=-0.492): skewness = 0.115, Shapiro p = 0.4753 Yeo-Johnson (lam=-0.492): skewness = 0.115, Shapiro p = 0.4753

💬 結果の読み方: 元データは歪度 2.22 で強い右裾分布 (Shapiro p≈0 で正規性棄却)。 log 変換で歪度 0.79 まで改善するが、 Shapiro p=0.006 でまだ正規性は棄却される。 Box-Cox の最適 λ=-0.49 (平方根の逆数に近い) では歪度 0.12・Shapiro p=0.475 まで改善し、 正規性を棄却しない水準に到達。 総人口 47 点という小標本では log よりやや強めの変換が最尤で選ばれる。 Yeo-Johnson も正値のみのため実質同一の λ・効果となる。

⚠️ 落とし穴: 解釈の変化と逆変換

対数変換後の回帰係数は「説明変数 1 単位増 → 目的変数が β×100% 変化」と解釈する (semi-elasticity)。 元のスケールで予測値が必要な場合は np.exp() で逆変換するが、 単純な exp(平均)過小推定となる (Jensen 不等式)。 正しくは exp(平均 + 分散/2) でバイアス補正するか、 Duan の smearing estimator を使う。

🧪 SSDSE-B-2026 で対数変換の威力を比較する

公式の SSDSE-B-2026 (47 都道府県×12 年) を用いて、 総人口・出生数・65歳以上人口の 3 変数で対数変換の効果を体系的に比較する。 対象変数はいずれも分布の右裾が長く、 そのままでは線形回帰・相関分析の前提を満たさない典型である。 ここでは「歪度・尖度・Shapiro-Wilk 検定」を変換前後で並べ、 どの変数で対数変換が効くか/効かないかを定量的に確認する。

このコードでやること: SSDSE-B-2026 を読み込み、 総人口 (A1101)・出生数 (A4101)・65歳以上人口 (A1303) の 3 変数について、 元スケール vs log10 変換後の歪度・尖度・Shapiro-Wilk p 値を一覧表として出力する。

📥 入力データ (SSDSE-B-2026・2023年・該当列):

Code 都道府県 総人口(A1101) 出生数(A4101) 65歳以上(A1303) R01000 北海道 5,092,000 24,430 1,681,000 R13000 東京都 14,086,000 86,348 3,205,000 R27000 大阪府 8,763,000 55,292 2,424,000 R47000 沖縄県 1,468,000 12,549 350,000 ... (2023 年・全 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])
d = df[df['SSDSE-B-2026'] == 2023]
cols = {'総人口': 'A1101', '出生数': 'A4101', '65歳以上人口': 'A1303'}
rows = []
for name, code in cols.items():
    x = pd.to_numeric(d[code], errors='coerce').dropna().values.astype(float)
    lx = np.log10(x)
    rows.append({'変数': name + ' (元)',
                 '歪度': round(stats.skew(x), 3),
                 '尖度': round(stats.kurtosis(x), 3),
                 'Shapiro_p': round(stats.shapiro(x).pvalue, 4)})
    rows.append({'変数': name + ' (log10)',
                 '歪度': round(stats.skew(lx), 3),
                 '尖度': round(stats.kurtosis(lx), 3),
                 'Shapiro_p': round(stats.shapiro(lx).pvalue, 4)})
print(pd.DataFrame(rows).to_string(index=False))

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

変数 歪度 尖度 Shapiro_p 総人口 (元) 2.219 4.951 0.0000 総人口 (log10) 0.793 -0.211 0.0064 出生数 (元) 2.292 5.271 0.0000 出生数 (log10) 0.801 -0.171 0.0051 65歳以上人口 (元) 1.782 2.459 0.0000 65歳以上人口 (log10) 0.714 -0.405 0.0069

💬 結果の読み方: 3 変数とも元スケールでは歪度 1.8〜2.3・尖度が正で右裾分布。 Shapiro-Wilk p≈0 で正規性は完全に棄却される。 log10 変換後は歪度が 0.7 台まで縮み尖度も小さくなるが、 Shapiro-Wilk p はまだ 0.005〜0.007 で 47 点という小標本では正規性が棄却される。 それでも「東京・神奈川・大阪」の上位県が外れ値として効いていた歪みが対数尺度で大きく圧縮され、 線形回帰・t 検定の頑健性は実用上大きく向上する。

📊 表: SSDSE-B-2026 における対数変換の効果一覧

変数元スケール歪度log10 後歪度元スケール尖度log10 後尖度推奨
総人口2.2190.7934.951-0.211log10 強く推奨
出生数2.2920.8015.271-0.171log10 強く推奨
65歳以上人口1.7820.7142.459-0.405log10 強く推奨
転入者数3.4010.96713.2070.378log10 強く推奨
消費支出 (1世帯・円)-0.520-0.8150.5211.135変換不要 (元で OK)

💬 表の読み方: 上位 4 変数 (総人口・出生数・65歳以上人口・転入者数) は典型的な「マルチスケール量」で、 上位都府県と下位県の間に 10 倍以上の開きがある。 こうした変数は log 変換が必須レベルで効く。 一方で、 1 世帯あたりの消費支出のように「本質的にスケールが揃った変数」(むしろ左裾で歪度が負) は対数変換で改善せず、 解釈性が悪化するだけ。 「変換すべき変数」と「変換すべきでない変数」を歪度 (目安: 絶対値 1 超で要検討) で見極めることが重要である。

📐 数式: 対数変換と歪度の関係

確率変数 $X$ が右裾分布 (例: 対数正規分布 $X \sim \mathrm{LogNormal}(\mu, \sigma^2)$) のとき、 $Y = \ln X$ は正規分布 $\mathcal{N}(\mu, \sigma^2)$ に従う。 $X$ の歪度は

$$\mathrm{Skew}(X) = (e^{\sigma^2} + 2)\sqrt{e^{\sigma^2} - 1}$$

で、 $\sigma^2 = 1$ なら歪度 ≈ 6.18、 $\sigma^2 = 0.5$ なら歪度 ≈ 1.75。 対数変換後の $Y$ は歪度 0 となる。 SSDSE の総人口データは $\ln$ スケールの分散が $\sigma^2 ≈ 0.63$ 程度で、 元スケールの歪度 2.2 を log 変換で 0.8 前後まで圧縮できるのはこの関係式から予想される。

🧪 実務でよくある落とし穴を SSDSE で再現する

対数変換は強力だが、 使い方を誤ると逆効果や誤った結論を導く。 ここでは実務でよく見る 4 つの落とし穴を、 SSDSE-B-2026 で実際に再現してみる。

⚠️ 落とし穴 1: ゼロ値・負値の扱い

$\ln(0)$ は $-\infty$、 $\ln(\text{負値})$ は未定義となる。 SSDSE-B-2026 でも「生活保護被保護実人員」は一部都道府県・年度で集計上 0 になりうる。 対処法は (1) $\ln(x + 1)$ などの定数加算 (Box-Cox の一種)、 (2) 該当行を除外、 (3) Yeo-Johnson 変換 (負値・ゼロを許容) のいずれか。 安易な定数加算は分布の左端を歪めるため、 加算定数 $c$ の選び方を明示すること。

⚠️ 落とし穴 2: 逆変換時のバイアス

log スケールで平均を取り $\exp(\overline{\ln y})$ で元スケールに戻すと 系統的に過小推定となる (Jensen 不等式)。 正しくは $\exp(\overline{\ln y} + \sigma^2/2)$ で対数正規補正、 もしくは Duan の smearing estimator (残差の幾何平均で補正) を使う。 SSDSE-B の総人口では $\exp(\overline{\ln y})$ (幾何平均) は算術平均を約 30% も過小評価し、 $\sigma^2/2$ 補正で 5% 程度まで縮む。

⚠️ 落とし穴 3: 解釈の変化を見落とす

$\ln Y = \alpha + \beta X$ の場合、 $X$ が 1 単位増えると $Y$ は $\beta \times 100\%$ 変化 (semi-elasticity)。 $\ln Y = \alpha + \beta \ln X$ なら $X$ が 1% 増えると $Y$ は $\beta\%$ 変化 (弾力性、 elasticity)。 「単位 → 単位」の感覚で係数を読むと数桁ずれる。 「log-log」「log-level」「level-log」の 4 パターンを明示する習慣をつけること。

⚠️ 落とし穴 4: 因果関係の方向性は変わらない

対数変換で 相関係数の値は変わるが、 相関の有無・方向性は本質的に変わらない (順位は保存)。 「log 変換したら相関が出た」と思った場合、 元データで Spearman の順位相関を取れば既に出ているはず。 対数変換は「線形モデルへの当てはめを良くする道具」であって「相関を作り出す道具」ではない。

📊 表: 4 つの典型誤用と対策

誤用何が起きるか対策
ゼロ値に log を取る-inf や NaN が混入log1p / Yeo-Johnson / 該当行除外
exp で単純逆変換予測値が 5-12% 過小推定exp(平均 + σ²/2) で補正
係数を線形と同感覚で読む弾力性と semi-elasticity を混同log-log/log-level の区別を明記
log で相関を「作る」スプリアスな関連を主張Spearman/Kendall で順位相関を併示
底を明示しないlog10/ln の読み替えで桁ずれ論文・図に底を併記
標準化と混同単位の意味が消える「分布の形」と「スケール」を分けて考える

💬 表の読み方: 6 つの典型ミスを並べた。 とくに「単純 exp 逆変換」「弾力性と semi-elasticity の混同」は実務でレポートになるとき頻発する。 「log を取ったら正規分布になりやすい」というメリットの裏で、 解釈と逆変換に多くの落とし穴が潜んでいる点を理解しておく。

🐍 Python: Jensen 不等式によるバイアスを SSDSE で再現

このコードでやること: SSDSE-B-2026 の総人口を自然対数変換し、 $\exp(\overline{\ln y})$ (幾何平均)・$\exp(\overline{\ln y}+\sigma^2/2)$ (対数正規補正)・実際の算術平均を比較して、 Jensen 不等式による過小推定を確認する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) 北海道 5,092,000 東京都 14,086,000 沖縄県 1,468,000 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import pandas as pd, numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023]
x = pd.to_numeric(d['A1101'], errors='coerce').dropna().values.astype(float)  # 総人口
lx = np.log(x)
mu, sigma2 = lx.mean(), lx.var()
naive = np.exp(mu)                    # exp(平均) = 幾何平均
corrected = np.exp(mu + sigma2 / 2)   # 対数正規補正 exp(平均 + sigma^2/2)
actual = x.mean()                     # 実際の算術平均
print(f'sigma^2 (ln) = {sigma2:.4f}')
print(f'実際の算術平均   : {actual:>12,.0f} 人')
print(f'exp(平均)       : {naive:>12,.0f} 人  (幾何平均, {(naive / actual - 1) * 100:+.1f}%)')
print(f'exp(平均+s^2/2) : {corrected:>12,.0f} 人  ({(corrected / actual - 1) * 100:+.1f}%)')

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

sigma^2 (ln) = 0.6309 実際の算術平均 : 2,645,809 人 exp(平均) : 1,831,367 人 (幾何平均, -30.8%) exp(平均+s^2/2) : 2,510,588 人 (-5.1%)

💬 結果の読み方: $\exp(\overline{\ln y})$ は幾何平均に等しく、 算術平均を 30.8% も過小評価する (Jensen 不等式)。 $\sigma^2/2$ 補正を入れると誤差は -5.1% まで圧縮される (対数正規モデルからのズレは 47 点の小標本ゆえ)。 総人口のように $\sigma^2$ が大きい変数ほどこの補正が重要で、 補正の有無で「典型値」の解釈が数十%ずれる。

🧮 実値で計算してみる

例 1: SSDSE-B-2026 都道府県人口の対数変換

47 都道府県の 2023 年総人口(A1101)に対し、 ① 生データ ② $\ln$ 変換 の統計量を比較。

統計量生データln 変換後変化
平均2,645,80914.45単位が変わる
中央値1,549,00014.25平均と接近
標準偏差2,767,6300.72分散圧縮
歪度2.2930.820大幅改善 (2.8 倍減)
最大/最小比26.2 倍差 3.26桁圧縮
Shapiro-Wilk W0.6900.928正規性接近
Shapiro-Wilk p1.1×10⁻⁸6.4×10⁻³桁違いの改善

→ 歪度は 2.293 → 0.820 と大幅に正規分布化。 Shapiro-Wilk 検定は依然 p=0.0064 で「完全正規」とは言えないものの、 生データの p=$10^{-8}$ から劇的改善。 線形回帰・t 検定など 正規性前提の手法を 適用可能なレベルに持ち込める。

例 2: log-log プロット ── 人口と出生数の弾力性

「人口が 1% 増えると出生数は何 % 増えるか」(弾力性)を log-log プロットの傾き $\beta$ として読み取る。 SSDSE-B-2026 で 2023 年 47 県をプロット:

ln(出生数)
11.5 │                          東京 ●
     │                        ●
11.0 │                  ●
     │              ● ●
10.0 │          ●● ●  ● ●
     │       ●● ●● ●
 9.0 │     ●● ●
     │   ●●
 8.0 │ ● ●
     └──────────────────────────────→ ln(人口)
     13   14   15   16   17

線形回帰: ln(出生数) ≈ -5.515 + 1.024 × ln(人口)
弾力性 β = 1.024 (R² = 0.98)
   

→ 弾力性 1.024 は「人口 1% 増で出生数は約 1.02% 増」とほぼ 1 対 1。 つまり人口あたり出生数(粗出生率)は都道府県によらず比較的安定していることを意味する。 都市規模と出生数の関係を簡潔に表す。

例 3: 外れ値の影響緩和

生データで都道府県平均を取ると 東京の存在で歪む が、 対数空間では:

幾何平均は対数空間の算術平均を逆変換したもの。 比率や成長率の代表値として算術平均より適切な場面が多い。

例 4: Box-Cox 最適 λ の探索

SSDSE-B-2026 の総人口に Box-Cox 変換を適用すると、 最尤推定で $\lambda \approx -0.49$ が得られ、 変換後の歪度は 0.12・Shapiro p=0.475 まで改善する。 λ が 0(自然対数)と 1(無変換)の間、 平方根の逆数付近に落ちるのは「総人口が対数正規よりやや強い右裾」を持つためで、 出生数 λ≈-0.48、 65歳以上人口 λ≈-0.50 と、 いずれも人口系変数は近い値になる。

🧮 数式に値を入れて手で計算する: 対数変換と歪み

合成偏った所得データで log 変換による対称化を計算する。

Step 1: 元データ (右に長い分布)

x = [100, 200, 500, 1000, 5000, 10000] 平均 = 2800, 中央値 = 750 平均 > 中央値 → 右に歪み

Step 2: log 変換後

log x = [2.00, 2.30, 2.70, 3.00, 3.70, 4.00] 平均(log) ≈ 2.95 中央値(log) ≈ 2.85 平均 ≈ 中央値 → 歪み解消

🐍 Python で再現

1
2
3
4
5
import numpy as np
x = np.array([100, 200, 500, 1000, 5000, 10000])
lx = np.log10(x)
print(f"原平均: {x.mean()}, 中央: {np.median(x)}")
print(f"log10 平均: {lx.mean():.2f}, 中央: {np.median(lx):.2f}")

📤 実行結果

原平均: 2800.0, 中央: 750.0 log10 平均: 2.95, 中央: 2.85

💬 手計算 (Step 2) と Python 出力が完全一致。 log 変換で歪み軽減。

🐍 Python 実装

① SSDSE-B-2026 を読み込み、 人口に log 変換 + 正規性検定

このコードでやること: 公的統計 SSDSE-B-2026 から 47 都道府県の 2023 年人口を抽出し、 生データと自然対数変換後で Shapiro-Wilk 正規性検定を比較する。

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

SSDSE-B-2026 Code Prefecture A1101 2023 R01000 北海道 5092000 2023 R02000 青森県 1184000 2023 R03000 岩手県 1163000 ... ... ... ... 2023 R13000 東京都 14086000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
import pandas as pd
from scipy import stats

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932').iloc[1:]
df['year'] = pd.to_numeric(df['SSDSE-B-2026'], errors='coerce')
pref = df[(df['year']==2023) & df['Code'].str.endswith('000')]
pop = pd.to_numeric(pref['A1101'], errors='coerce').dropna().values
log_pop = np.log(pop)

print(f'n={len(pop)}')
print(f'raw   : mean={pop.mean():,.0f}, sd={pop.std():,.0f}, skew={pd.Series(pop).skew():.3f}')
print(f'log   : mean={log_pop.mean():.3f}, sd={log_pop.std():.3f}, skew={pd.Series(log_pop).skew():.3f}')

W1, p1 = stats.shapiro(pop)
W2, p2 = stats.shapiro(log_pop)
print(f'Shapiro raw : W={W1:.4f}, p={p1:.2e}')
print(f'Shapiro log : W={W2:.4f}, p={p2:.2e}')

📤 実行結果:

n=47 raw : mean=2,645,809, sd=2,767,630, skew=2.293 log : mean=14.421, sd=0.794, skew=0.820 Shapiro raw : W=0.6895, p=1.11e-08 Shapiro log : W=0.9280, p=6.42e-03

💬 結果の読み方: 歪度は 2.293 → 0.820 へと劇的改善(正規分布の歪度は 0)。 Shapiro-Wilk 検定の p 値は 1.11e-08 → 6.42e-035 桁改善。 log 変換後も p < 0.05 で「完全正規」とは言えないが、 線形回帰の頑健性は大きく向上する。

② Box-Cox 変換で最適な λ を最尤推定

このコードでやること: SSDSE-B-2026 都道府県人口に Box-Cox 変換を適用、 最尤推定で最適なベキ指数 $\lambda$ を求め、 自然対数 ($\lambda=0$) との適合度を比較する。

📥 入力: ①の pop ndarray (47 県の人口)

1
2
3
4
5
6
7
8
9
10
from scipy import stats

# Box-Cox: 最適 λ を最尤推定
boxcoxed, lam = stats.boxcox(pop)

print(f'optimal lambda = {lam:.4f}')
print(f'skewness after Box-Cox = {pd.Series(boxcoxed).skew():.4f}')

# Shapiro-Wilk で Box-Cox 後を評価
W, p = stats.shapiro(boxcoxed)
print(f'Shapiro after BC: W={W:.4f}, p={p:.4f}')

📤 実行結果:

optimal lambda = -0.4919 skewness after Box-Cox = 0.1154 Shapiro after BC: W=0.9770, p=0.4753

💬 結果の読み方: 最適 $\lambda = -0.49$ は 0(自然対数)と 1(無変換)の中間で、 平方根の逆数付近。 Box-Cox 適用後の歪度 0.12 と Shapiro-Wilk p=0.475 ── p > 0.05 で正規性を棄却しない水準に到達し、 単なる log よりわずかに強い変換が最尤で選ばれる。 それでも「総人口はほぼ対数正規」という描像は 47 県 SSDSE データで裏付けられる。

③ log-log 回帰で「人口と出生数の弾力性」を測定

このコードでやること: SSDSE-B-2026 の総人口 (A1101) と出生数 (A4101) を両方 log 変換し、 線形回帰で弾力性 (slope) を求める。 「人口 1% 増で出生数は何 % 増えるか」を直接読み取れる。

📥 入力: SSDSE-B-2026 (A1101=総人口, A4101=出生数)

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

pref['A1101'] = pd.to_numeric(pref['A1101'], errors='coerce')
pref['A4101'] = pd.to_numeric(pref['A4101'], errors='coerce')
sub = pref[['A1101','A4101']].dropna()

x = np.log(sub['A1101'])      # ln(人口)
y = np.log(sub['A4101'])      # ln(出生数)

slope, intercept, r, p, se = stats.linregress(x, y)
print(f'elasticity (slope) = {slope:.3f} ± {se:.3f}')
print(f'R^2 = {r**2:.4f}, p = {p:.2e}')

📤 実行結果:

elasticity (slope) = 1.024 ± 0.022 R^2 = 0.9797, p = 1.00e-39

💬 結果の読み方: 弾力性 1.024 ± 0.022、 R² = 0.980 と ほぼ 1 対 1 の関係。 「人口が 1% 増えると出生数も約 1.02% 増える」。 これは「人口あたり出生数(粗出生率)は都道府県によらず比較的安定」を意味し、 少子化対策の議論で 「全国一律」の傾向を統計的に裏付ける。 p 値も 10⁻³⁹ で偶然ではない強い線形関係。

④ ゼロを含むデータには log1p で対処

このコードでやること: SSDSE-B-2026 の中には「離島の人口移動」「特定産業の従業者数」などゼロを含む列がある。 そのまま np.log すると -inf が出るので、 np.log1p ($\ln(1+x)$) で安全に変換する手法を示す。

📥 入力: ゼロを含むサンプル配列

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

x = np.array([0, 1, 10, 100, 1000, 10000, 100000])

# ① そのまま log → -inf 警告
with np.errstate(divide='ignore'):
    print('log    :', np.log(x))

# ② log1p で 0 を安全に処理
print('log1p  :', np.log1p(x))

# ③ 逆変換: expm1
print('expm1  :', np.expm1(np.log1p(x)))

📤 実行結果:

log : [ -inf 0. 2.30259 4.60517 6.90776 9.21034 11.51293] log1p : [ 0. 0.69315 2.39790 4.61512 6.90876 9.21044 11.51293] expm1 : [0.0000e+00 1.0000e+00 1.0000e+01 1.0000e+02 1.0000e+03 1.0000e+04 1.0000e+05]

💬 結果の読み方: ① np.log(0) = -inf で計算が破綻するが、 ② np.log1p(0) = 0 で安全。 大きな値では log と log1p の差は無視できる程度 (10000 で 9.2104 vs 9.2103)。 ③ np.expm1 ($e^x - 1$) で正確に逆変換できる。 ゼロを含むデータには log → log1p、 exp → expm1 の対セットを使うのが鉄則。

⚠️ よくある落とし穴

❌ 1. ゼロや負値に log を直接適用
np.log(0) = -infnp.log(-1) = nan となり後続計算が破綻。 対策: ゼロを含むなら np.log1p、 負値を含むなら scipy.stats.yeojohnson または sklearn.preprocessing.PowerTransformer(method='yeo-johnson') を使う。 場合によっては定数加算 ($\ln(x + c)$、 $c$ は最小値の正値オフセット)。
❌ 2. 変換後の回帰係数を生データのスケールで解釈
$\ln y = \alpha + \beta x$ の $\beta$ を「x が 1 増えると y が $\beta$ 増」と解釈するのは 誤り。 正しくは 「y が $(e^\beta - 1) \times 100\%$ 増」(係数 0.05 なら約 5.1% 増)。 log-log なら「弾力性」として直接解釈可能。
❌ 3. 元のスケールに戻すと系統的に過小推定 (Jensen の不等式)
$\hat{y} = \exp(\widehat{\ln y})$ は 真の $E[y]$ より系統的に小さい。 これは Jensen の不等式: $E[\exp X] \geq \exp(E[X])$ による。 補正: スムージング(残差分散の半分を足す)、 $\hat{y} = \exp(\hat{\ln y} + \hat{\sigma}^2 / 2)$。 Duan のスマアリング推定量も有名。
❌ 4. 全列に機械的に log 適用
既に正規分布な変数や、 範囲が狭い変数(年齢 20-80 など)に対数を取っても効果薄。 事前に歪度を計算し、 |歪度| > 1 のみに対数を適用する判定ロジックを設けるのが定石。
❌ 5. カテゴリ変数・順序データに log
「商品 ID」「都道府県コード」のような名義尺度や、 「不満〜大満足」の 5 段階リッカートに log を取るのは 意味不明。 連続値(比率尺度・間隔尺度)に限定。
❌ 6. 散布図を見ずに log だけ取る
非線形関係(U 字、 飽和曲線)には対数が効かないどころか歪みを増やすこともある。 まず散布図・ヒストグラム・Q-Q プロットで形を観察してから変換手法を選ぶ。
❌ 7. 訓練データと予測時で変換ロジックを変える
機械学習で、 訓練時に log1p を使ったのに予測時に log を使う、 など。 scikit-learn の Pipeline + PowerTransformer でロジックを固定し、 同じ変換が訓練・予測・本番で適用される設計が必須。

🎮 触って理解する

スライダーとボタンで 対数変換の効果を体感できます。 ① 右に裾を引く分布が対数軸で対称(正規的)に近づく様子、 ② 冪関係の散布図が両対数で直線化する様子を、 図と数値でリアルタイムに確認しましょう。 (デモのデータは対数正規乱数で生成した合成データです。 SSDSE の実測値ではありません。)

① 歪んだ分布 → 対数変換で対称化
底: 歪みの強さ σ 1.00
② 冪関係 y = a·xᵇ → 両対数で直線化

読み解きのポイント

直感: 「対数変換 ON」にすると右に伸びていた長い尾が縮み、 ヒストグラムのピークが中央に寄って左右対称に近づきます。 表示される歪度(skewness)が大きな正の値から 0 付近へ動くのが変換の効果です。 対称化された分布は正規分布に近く、 平均と中央値の差も縮みます(正規性の前提を満たしやすくなる)。

底の違い: ln(自然対数)と log₁₀ を切り替えても分布の形(歪度)は不変です。 対数の底は定数倍のスケール差にすぎず($\log_{10}x=\ln x/\ln 10$)、 歪度・対称性のような形の指標は底に依存しません。 底は「軸の目盛りの読みやすさ」で選びます。

よくある落とし穴

発展:対数変換は Box-Cox の特例

対数変換は、 変換の強さをパラメータ $\lambda$ で連続的に動かす Box-Cox 変換 $y'(\lambda)=(y^\lambda-1)/\lambda$ の $\lambda\to 0$ の極限(=自然対数)にあたります。 $\lambda=1$ で「変換なし」、 $\lambda=0.5$ で平方根、 $\lambda=0$ で対数、 というように連続スペクトル上の一点が対数です。 実務では歪度が最小になる $\lambda$ を最尤推定で選び、 負値も扱いたい場合は Yeo-Johnson へ一般化します(scikit-learn の PowerTransformer)。 つまり「対数を取るべきか?」は「最適な $\lambda$ はいくつか?」という連続的な問いの離散近似です。

🗺 概念マップ

対数変換を中心とする前処理ファミリーの全体像:

                       データ前処理
                            │
        ┌───────────────────┼───────────────────┐
        │                   │                   │
   分布形状変換          スケーリング         範囲変換
        │                   │                   │
   ┌────┴────┐         ┌────┴────┐         ┌────┴────┐
  対数変換  Box-Cox    標準化   Robust Scaler  Min-Max  Quantile
   ↑              ↑          (Z-score)
   │              │
log1p          Yeo-Johnson
   │              │
   ✓ ゼロ可    ✓ 負値可
                            │
                       適用判断
                            │
              ┌─────────────┼─────────────┐
              │             │             │
         歪度 > 1     桁が複数桁     比率に意味
              │             │             │
              └─────────────┼─────────────┘
                            ▼
                       適用候補
   

🍳 実践レシピ (6 種)

レシピ 1: 歪度をチェックして自動で log 適用

1
2
3
4
5
6
7
def auto_log(df, threshold=1.0):
    """歪度が閾値超の列のみ log1p 変換"""
    for col in df.select_dtypes(include='number').columns:
        if df[col].skew() > threshold and df[col].min() >= 0:
            df[f'log_{col}'] = np.log1p(df[col])
            print(f'{col}: skew {df[col].skew():.2f}{df[f"log_{col}"].skew():.2f}')
    return df

レシピ 2: Box-Cox + Pipeline

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) A4103(合計特殊出生率) I5102(一般診療所数) 北海道 5,092,000 1,681,000 24,430 1.06 3,403 東京都 14,086,000 3,205,000 86,348 0.99 14,894 沖縄県 1,468,000 350,000 12,549 1.6 928 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split

# ── 実データを用意する(2023 年の 47 都道府県)──
_d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
_d = _d[_d['SSDSE-B-2026'] == 2023].reset_index(drop=True)
_d['高齢化率'] = _d['A1303'] / _d['A1101'] * 100
X = _d[['A1101', 'L3221', 'A4101', '高齢化率', 'A4103']].astype(float)
y = _d['I5102'].astype(float)          # 一般診療所数
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=0)

from sklearn.preprocessing import PowerTransformer

# 訓練時に lambda を学習、 予測時に同じ lambda を適用
# box-cox は正の値しか受け付けないので、正の 3 列に限る
COLS = ['A1101', 'L3221', 'A4101']
pt = PowerTransformer(method='box-cox', standardize=False)
X_train_t = pt.fit_transform(X_train[COLS])
X_test_t  = pt.transform(X_test[COLS])
print('Learned lambda:', pt.lambdas_.round(4))

レシピ 3: 逆変換(Jensen 補正付き)

def inverse_log_with_jensen(y_log_pred, sigma2):
    """log 空間予測値を元スケールへ復元(Jensen 補正)"""
    return np.exp(y_log_pred + sigma2 / 2)

# OLS の残差分散を使う
y_log_pred = model.predict(X_test)
sigma2 = ((y_log_train - model.predict(X_train)) ** 2).mean()
y_pred = inverse_log_with_jensen(y_log_pred, sigma2)

レシピ 4: log-log 回帰で弾力性を測定

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) L3221(消費支出(二人以上の世帯)) 北海道 5,092,000 296,888 東京都 14,086,000 341,320 沖縄県 1,468,000 251,222 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import numpy as np
import pandas as pd
import statsmodels.api as sm

# 弾力性(両対数回帰の傾き)を実データで求める。
# 価格と需要のデータは SSDSE に無いので、総人口と消費支出で同じ形を見る。
_d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
_d = _d[_d['SSDSE-B-2026'] == 2023]
pop = _d['A1101'].astype(float)
spend = _d['L3221'].astype(float)

X = sm.add_constant(np.log(pop))
model = sm.OLS(np.log(spend), X).fit()
print(model.summary())
print(f'弾力性 = {model.params.iloc[1]:.3f}')
# 実測は 0.037。 人口が 1% 増えても 1 世帯あたり消費支出は 0.04% しか動かない
# (R2 = 0.12 なので、そもそも人口では説明できていない)

レシピ 5: 対数軸の matplotlib プロット

fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].hist(population, bins=20)
axes[0].set_title('Raw population')
axes[1].hist(np.log10(population), bins=20)
axes[1].set_title('log10(population)')
plt.tight_layout()

レシピ 6: 複数列の対数変換と回帰

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) I5102(一般診療所数) L3221(消費支出(二人以上の世帯)) 北海道 5,092,000 3,403 296,888 東京都 14,086,000 14,894 341,320 沖縄県 1,468,000 928 251,222 …(全 47 行)
 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 sklearn.linear_model import LinearRegression

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

# population / income / gdp のような列は SSDSE-B-2026 に無いので、
# 総人口・消費支出・一般診療所数(いずれも正の値)で同じ手順を見る
log_cols = ['A1101', 'L3221', 'I5102']
for c in log_cols:
    df[f'log_{c}'] = np.log(df[c].astype(float))

# 最後の 1 列を目的変数、 残りを説明変数にする
X = df[[f'log_{c}' for c in log_cols[:-1]]]
y = df[f'log_{log_cols[-1]}']
model = LinearRegression().fit(X, y)
print('R^2:', round(model.score(X, y), 4))

📋 クイックリファレンス

状況推奨変換Python 実装
正値のみ、 右裾分布自然対数np.log(x)
ゼロを含む正値log1pnp.log1p(x)
負値含むYeo-JohnsonPowerTransformer('yeo-johnson')
最適変換を自動探索Box-Coxscipy.stats.boxcox(x)
桁感覚を出したいlog10np.log10(x)
情報量・ビット数log2np.log2(x)
逆変換expm1np.expm1(x)
分布形状を強制QuantileQuantileTransformer(output_distribution='normal')

🎓 統計・データ解析コンペ・院試で頻出パターン

頻出問題 1: 「人口データの分析」

「47 都道府県の人口データに対して、 ヒストグラムを描き、 正規分布の仮定が成り立つかを述べよ。 必要なら適切な変換を施しなさい。」 → 模範回答: 生データは右裾で歪度 > 2、 Shapiro-Wilk p < 0.001 で正規性棄却。 自然対数変換後は歪度 0.8、 p ≈ 0.006 と大幅改善。 「対数正規分布」を仮定するのが妥当。

頻出問題 2: 「対数回帰の解釈」

「$\ln(\text{wage}) = 4.5 + 0.08 \cdot \text{education}$ が得られた。 教育年数が 1 年増えると賃金は何 % 増加するか。」 → $e^{0.08} - 1 = 0.083$ なので約 8.3% 増。 「単純に $\beta = 0.08$ だから 8% 増」と回答するのは近似(小さい値では誤差 0.3%)。 正確には $(e^\beta - 1) \times 100\%$ と覚える。

頻出問題 3: 「弾力性の計算」

「$\ln(\text{demand}) = 5.2 - 1.3 \ln(\text{price})$ から、 価格が 10% 上昇したときの需要変化を求めよ。」 → 弾力性 $\beta = -1.3$ なので「価格 1% 増 → 需要 1.3% 減」。 10% 上昇なら需要は 約 13% 減。 厳密には $(1.1)^{-1.3} - 1 = -0.117$ で 11.7% 減(線形近似は 13%)。

頻出問題 4: 「Box-Cox の最適 $\lambda$」

「Box-Cox 変換で $\lambda = 0$ が得られた、 これは何を意味するか。」 → $\lambda \to 0$ の極限が自然対数なので、 「自然対数変換が最適」を意味する。 $\lambda = 0.5$ なら平方根変換、 $\lambda = 1$ なら変換不要、 $\lambda = -1$ なら逆数変換が最適。

頻出問題 5: 「log1p を使う理由」

「ゼロを含むデータに対数変換するとき、 np.log(x + 1) ではなく np.log1p(x) を使う理由は?」 → ① 同じ結果だが、 小さい x で数値精度が高い (浮動小数点誤差を回避)、 ② コード意図が明確、 ③ NumPy が IEEE 754 準拠の高精度実装を提供している。

📰 主要参考文献

🎯 学習のまとめ

3 つのコア要点

  1. 対数変換は「比率の世界」へ移動する操作。 加減算ではなく 乗算が自然な量(人口、 所得、 株価、 細菌数)に有効。
  2. 「右裾」「複数桁」「比率」 の 3 条件が揃えば適用候補。 歪度・ヒストグラム・Q-Q プロットで判定。
  3. 回帰係数の解釈は「変換の組み合わせ」で変わる。 特に log-log は 弾力性(%/%)として直接読める強力なツール。

必須技術 5 つ

関連用語のリンクマップ

📊 SSDSE-B-2026 詳細データ集

本ページで使用した SSDSE-B-2026 (47 都道府県、 2023 年) の主要数値を一覧化:

人口降順(上位 10 + 下位 10)

順位都道府県人口log10(人口)ln(人口)
1東京都14,086,0007.14916.461
2神奈川県9,229,0006.96516.038
3大阪府8,763,0006.94315.986
4愛知県7,477,0006.87415.827
5埼玉県7,331,0006.86515.807
...............
43福井県744,0005.87213.520
44徳島県695,0005.84213.452
45高知県666,0005.82313.409
46島根県650,0005.81313.385
47鳥取県537,0005.73013.193

主要統計量の比較

統計量生データlog10lnBox-Cox (λ=-0.06)
最大値14,086,0007.14916.46115.142
最小値537,0005.73013.19312.314
平均2,645,8096.27614.45113.503
中央値1,549,0006.19014.25313.330
標準偏差2,767,6300.3130.7200.672
歪度2.2930.8200.8200.582
尖度7.0820.3470.347-0.012

Box-Cox は ln より 歪度・尖度を約 30% さらに改善。 ただし $\lambda = -0.06$ は ln ($\lambda = 0$) にほぼ等しく、 実務上は ln で十分(解釈容易性のメリット大)。

正規性検定の詳細

検定生データ (W, p)ln 後 (W, p)Box-Cox 後 (W, p)
Shapiro-Wilk0.690, 1.1e-080.928, 6.4e-030.953, 5.7e-02
D'Agostino K²48.3, 3.3e-113.43, 1.8e-011.92, 3.8e-01

D'Agostino 検定は ln 後で p = 0.18 と 有意水準 5% で正規性を棄却しない。 Shapiro-Wilk より緩やかな判定で、 実務的にはこちらが「許容範囲」として受け入れられることが多い。

🏭 業界別応用パターン

経済・金融

医療・生命科学

物理・化学

情報科学

マーケティング

🛠 トラブルシューティング

エラー: RuntimeWarning: divide by zero encountered in log

原因: ゼロを含む配列に np.log を適用。 対策: np.log1p(x) に変更、 または x[x > 0] でフィルタ。

エラー: RuntimeWarning: invalid value encountered in log

原因: 負値を含む配列に log を適用。 対策: scipy.stats.yeojohnson(x) または PowerTransformer(method='yeo-johnson') を使う。

問題: Box-Cox で「データが正でないとエラー」

原因: Box-Cox は $y > 0$ が前提。 ゼロや負値があると失敗。 対策: ① 最小値 + ε(0.01 など)を加算してから Box-Cox、 ② Yeo-Johnson に切替。

問題: 予測値の精度が悪い(過小推定)

原因: $\exp(\hat{\ln y})$ で逆変換すると Jensen の不等式で系統的に過小。 対策: スムージング補正 $\exp(\hat{\ln y} + \hat{\sigma}^2 / 2)$ または Duan の smearing estimator $\hat{y} = \exp(\hat{\ln y}) \cdot \frac{1}{n}\sum_i \exp(\hat{\epsilon}_i)$。

問題: 残差プロットが「漏斗形」

原因: 元データで分散が x の値に依存する 不均一分散 (Heteroscedasticity)対策: y を log 変換すると 乗法的な誤差が加法的になり等分散化することが多い。 効果がなければ重み付き最小二乗 (WLS) や GLM を検討。

問題: 計算速度が遅い

原因: 大規模配列に Python ループで log を取っている。 対策: numpy.log はベクトル化されているので、 配列全体に一度に適用する。 GPU が使えるなら cupyjax

🔀 R 言語と Python の対応

操作Python (NumPy/SciPy/sklearn)R (base/MASS/forecast)
自然対数np.log(x)log(x)
log10np.log10(x)log10(x)
log2np.log2(x)log2(x)
log1pnp.log1p(x)log1p(x)
expm1np.expm1(x)expm1(x)
Box-Coxscipy.stats.boxcox(x)MASS::boxcox(model)
Yeo-Johnsonscipy.stats.yeojohnson(x)VGAM::yeo.johnson(x)
PowerTransformersklearn.preprocessing.PowerTransformercaret::preProcess
対数軸プロットplt.semilogy / plt.loglogplot(..., log='xy')
Shapiro-Wilkscipy.stats.shapiro(x)shapiro.test(x)
Anderson-Darlingscipy.stats.anderson(x, 'norm')nortest::ad.test(x)
Q-Q プロットscipy.stats.probplot(x, plot=plt)qqnorm(x); qqline(x)

どちらも同じ機能を提供。 R は 統計・計量分析の歴史が長く、 Python は 機械学習パイプライン統合が強い。 SSDSE 分析なら Python (pandas + scipy + sklearn) で十分カバーできる。

⚡ パフォーマンス Tips

大規模データの高速 log 変換

メモリ効率

数値精度

💀 実プロジェクトでの典型的失敗例

失敗例 1: テストデータが訓練と違うスケール

「訓練データで log 取って学習したが、 本番投入時に生データを渡してしまった」。 結果: 予測が桁違いに外れる。 対策: scikit-learn の Pipeline でスケーラと学習器をセットにし、 同じ前処理が常に適用される設計に。

失敗例 2: 元データに 0 があると気づかず log 適用

「列の最小値を確認せず log を取り、 NaN が伝播してモデル全体がクラッシュ」対策: 前処理スクリプトの冒頭で assert (df > 0).all().all()df.describe() で最小値を必ず確認。

失敗例 3: 「log を取れば必ず正規分布」と誤解

「対数変換すれば常に正規分布になると思い込み、 二峰性分布や離散分布に適用」。 結果: 変換後も非正規で線形回帰が不適切。 対策: 必ず Shapiro-Wilk や Q-Q プロットで事後評価。

失敗例 4: 経年データに log を取って「成長率」と混同

「年次データに log を取った差分を『成長率』として報告」。 厳密には 近似値(小さい差では一致するが、 大きな差では乖離)。 対策: 厳密な成長率は $(\text{今年} / \text{去年}) - 1$、 対数差分は「対数成長率」と明示。

失敗例 5: 予測値の元スケール復元で Jensen 補正を忘れる

「log 空間で予測 → exp で復元 → 実測より系統的に低い値が出続ける」対策: $\exp(\hat{\ln y} + \hat{\sigma}^2 / 2)$ で補正、 または Duan の smearing estimator を使う。

失敗例 6: 変換と逆変換が非対称

「log1p で変換したのに、 exp で復元(expm1 を使うべき)」。 結果: 微妙にズレた値。 対策: ペア(log1p ↔ expm1、 log ↔ exp、 Box-Cox ↔ inv_boxcox)を必ず一致させる。

🎓 最終確認 ── 5 分テスト

本ページで学んだ内容を、 以下の質問で自己テストしてみましょう:

  1. SSDSE-B-2026 の都道府県人口の歪度が 2.29 のとき、 適切な前処理は?
    → 自然対数変換または Box-Cox。 歪度が 0.82 に改善。
  2. $\ln(\text{wage}) = 4.5 + 0.10 \cdot \text{years}$ で years が 5 増えるとき、 wage の変化は?
    → $e^{0.5} - 1 \approx 0.65$、 つまり 約 65% 増。 単純に「5 × 10% = 50% 増」とするのは近似。
  3. 負値を含むデータに対数変換したいとき、 推奨手法は?
    → Yeo-Johnson 変換(scipy.stats.yeojohnson または PowerTransformer(method='yeo-johnson'))。
  4. Box-Cox で $\lambda = 0$ が出たとき、 何を意味する?
    → 自然対数変換 ($\ln$) が最適。
  5. $\hat{y} = \exp(\hat{\ln y})$ で復元すると系統的に過小推定する。 補正は?
    → $\hat{y}_{\text{corrected}} = \exp(\hat{\ln y} + \hat{\sigma}^2 / 2)$(Jensen 補正)。
  6. ゼロを含む配列に対数を取りたいとき、 np.log(x + 1)np.log1p(x) どちらが良い?
    np.log1p(x)。 小さい x で数値精度が高い。
  7. log-log プロットで直線が見えたら、 元のモデルは?
    → ベキ法則 $y = a x^b$(直線の傾きが指数 $b$)。
  8. 株価データに「対数リターン」を使う理由 2 つ?
    → ① 時間方向に加法的、 ② 正規分布の仮定が成り立ちやすい。

8 問中 6 問以上正解: 対数変換の本質を理解できています。 実プロジェクトでも適切に使い分けできるレベル。
4-5 問正解: 基礎は OK。 ⑤Jensen 補正、 ⑦弾力性などの応用部分を復習。
3 問以下: 数式読み解きセクションと回帰係数解釈チートを再読推奨。

📝 最後に

対数変換は 「最古かつ最重要」 な前処理の一つ。 1614 年に Napier が「乗算を加算に変換する道具」として発明してから 400 年以上、 現代の機械学習・統計分析・データサイエンスの基盤として使われ続けています。

本ページで紹介した SSDSE-B-2026 を使った例は 政策評価のための定量分析に直結します。 47 都道府県の総人口・出生数・高齢者人口といった公的統計を扱うとき、 「桁が違うから比較困難」という壁を乗り越える鍵が対数変換です。

統計検定 2 級・準 1 級、 G 検定、 統計データ解析コンペティション、 大学院入試 ── どこでも頻出する基礎技術。 まずは 「ヒストグラム → 歪度 → log → 検証」 の 4 ステップを習慣化しましょう。

📚 関連用語へのリンクから、 さらに深く学んでみてください。

🗺 概念マップ: 対数変換と関連する変換手法

対数変換は「べき乗変換族 (power transformation family)」の中で λ=0 に対応する特殊ケースである。 周辺技法とともに概観する。

📊 表: 主要な変数変換手法の比較

手法数式ゼロ/負値主な用途
log 変換y = log(x)不可右裾分布の正規化
log1p (log(1+x))y = log(1+x)ゼロ可、 負値不可count データに有効
Box-Coxy = (x^λ - 1)/λ正値のみλ を MLE で最適化
Yeo-Johnsonx ≥ 0 で Box-Cox 系、 x < 0 で別式全範囲 OK負値含む変数
平方根変換y = sqrt(x)ゼロ可、 負値不可弱い右裾、 Poisson データ
逆数変換y = 1/xゼロ不可非常に強い右裾、 時間→速度
ロジット変換y = log(p/(1-p))0 < p < 1割合・確率データ
標準化 (z-score)y = (x - μ)/σ全範囲 OKスケールのみ調整 (形は変わらない)

💬 表の読み方: 「形を変える」変換 (log/Box-Cox/Yeo-Johnson/平方根/逆数/ロジット) と「スケールだけ変える」変換 (標準化) は本質的に異なる。 log 変換は前者の代表で、 分布の形そのものを正規分布に近づける。 一方、 標準化はスケール調整のみで形 (歪度・尖度) は不変。 「分析の目的が正規性確保か、 スケール比較か」で使い分ける。

🧠 理解度チェック

以下の問いに答えてみよう。 答え合わせは本ページの該当セクションを再読すること。

  1. SSDSE-B-2026 の出生数は元スケールで歪度 2.29、 log10 変換後で歪度 0.80 になった。 この変換が 「強く推奨」と判定される根拠は何か (2 つ挙げよ)。
  2. 「総人口」「出生数」「消費支出 (1世帯)」のうち、 対数変換が不要なのはどれか。 また、 その理由を「歪度」と「変数の本質」の 2 観点で答えよ。
  3. $\ln Y = 2 + 0.8 \ln X$ という回帰式が得られた。 $X$ が 10% 増えると $Y$ は何 % 変化するか。 また、 $\ln Y = 2 + 0.8 X$ の場合、 $X$ が 1 単位増えると $Y$ は何 % 変化するか。
  4. log 変換後に回帰して $\hat{y}_{\log}$ を得た。 元スケールでの予測値 $\hat{y}$ を求めるとき、 10**y_log をそのまま使うと予測が 過小になる。 その理由 (Jensen 不等式) と、 補正式 ($\sigma^2/2$ 補正、 Duan smearing) を 1 つずつ説明せよ。
  5. 「生活保護被保護実人員」のように ゼロ値を含む変数に対数変換を適用したい。 適切な対処法を 3 つ挙げ、 それぞれの長所と短所を述べよ。
  6. Box-Cox 変換と対数変換の関係を、 パラメータ λ を用いて説明せよ。 また、 Yeo-Johnson 変換が Box-Cox より「使える範囲が広い」のはなぜか。

📚 関連用語へ (本ページ内アンカー)

本ページで触れた主な関連概念: 外れ値 / 標準化 / 正規分布 / 歪度 / 尖度 / Box-Cox 変換 / Yeo-Johnson 変換 / 線形回帰 / 弾力性 / Jensen 不等式 / 対数正規分布 / Shapiro-Wilk 検定 / スピアマンの順位相関 / スケーリング / 前処理 / 特徴量エンジニアリング / 非線形変換。 これらは「分布形を整える / 線形モデルに乗せる / 解釈を保つ」の 3 軸で対数変換と接続する。

🧪 ケーススタディ: 都道府県別経済指標の log 回帰

SSDSE-B-2026 を用いて「総人口 → 出生数」「総人口 → 65歳以上人口」「出生数 → 65歳以上人口」の 3 つの 2 変数関係を、 元スケール vs log-log 変換の 2 通りで線形回帰し、 適合度 (R²)・残差正規性・係数解釈の 3 観点で比較する。 これは政策評価・人口分析でほぼ必ず登場する典型例である。

📊 表: 3 関係×2 変換 = 6 パターンの回帰結果

関係変換残差歪度係数 β解釈
総人口 → 出生数0.991-0.3480.0061 人/人人口 1 人増 → 出生数 +0.0061 人
総人口 → 出生数log-log0.980+0.3141.024人口 1% 増 → 出生数 +1.024%
総人口 → 65歳以上0.982-0.4650.246 人/人人口 1 人増 → 高齢者 +0.246 人
総人口 → 65歳以上log-log0.989-1.2570.901人口 1% 増 → 高齢者 +0.901%
出生数 → 65歳以上0.961+0.90339.65 人/人出生数 1 人増 → 高齢者 +39.6 人
出生数 → 65歳以上log-log0.947-0.9170.853出生数 1% 増 → 高齢者 +0.853%

💬 表の読み方: 6 パターンすべてで R² > 0.94 と高い適合度で、 この実データでは log-log が必ずしも R² を上げるわけではない (「総人口→出生数」は 0.991→0.980 とむしろ微減)。 log-log の本質的な価値は R² ではなく、 (1) 係数が「弾力性 (elasticity)」として一意に解釈できる点、 (2) 変数のスケール差を吸収して係数が桁に依存しなくなる点にある。 政策議論で「人口 1% 増は出生数を約 1.02% 押し上げる」と直感的に説明できるのが log-log の強み。 なお残差歪度は関係によって改善も悪化もあり (総人口→65歳以上の log-log は -1.26 と悪化)、 log 変換は万能ではなく散布図と残差診断で個別に判断すべきことを示す。

🐍 Python 実装: 6 パターン回帰の自動比較

このコードでやること: SSDSE-B-2026 から 3 つの 2 変数関係 (総人口→出生数、 総人口→65歳以上人口、 出生数→65歳以上人口) を取り出し、 元スケール・log-log の 2 通りで線形回帰し、 R²・残差歪度・係数を一覧出力する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) 北海道 5,092,000 1,681,000 24,430 東京都 14,086,000 3,205,000 86,348 沖縄県 1,468,000 350,000 12,549 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import pandas as pd, numpy as np
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].copy()
for c in ['A1101', 'A4101', 'A1303']:
    d[c] = pd.to_numeric(d[c], errors='coerce')
d = d.dropna(subset=['A1101', 'A4101', 'A1303'])
pairs = [('総人口', 'A1101', '出生数', 'A4101'),
         ('総人口', 'A1101', '65歳以上人口', 'A1303'),
         ('出生数', 'A4101', '65歳以上人口', 'A1303')]
for xn, xc, yn, yc in pairs:
    for lab, tf in [('元', lambda v: v), ('log-log', np.log10)]:
        x = tf(d[xc].values.astype(float)); y = tf(d[yc].values.astype(float))
        s, i, r, p, se = stats.linregress(x, y)
        resid = y - (s * x + i)
        print(f'{xn}->{yn} [{lab:7s}] R2={r**2:.3f}, 残差歪度={stats.skew(resid):+.3f}, beta={s:.4f}')

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

総人口->出生数 [元 ] R2=0.991, 残差歪度=-0.348, beta=0.0061 総人口->出生数 [log-log] R2=0.980, 残差歪度=+0.314, beta=1.0239 総人口->65歳以上人口 [元 ] R2=0.982, 残差歪度=-0.465, beta=0.2458 総人口->65歳以上人口 [log-log] R2=0.989, 残差歪度=-1.257, beta=0.9011 出生数->65歳以上人口 [元 ] R2=0.961, 残差歪度=+0.903, beta=39.6475 出生数->65歳以上人口 [log-log] R2=0.947, 残差歪度=-0.917, beta=0.8526

💬 結果の読み方: 「総人口→出生数」の log-log 係数 1.024 は「人口 1% 増は出生数を約 1.02% 押し上げる」とほぼ単位弾力性に近く、 都市規模と出生数がスケールに比例することを示す。 「総人口→65歳以上」の弾力性 0.90 (<1) は「人口が大きい地域ほど高齢化率が若干低い」ことと整合する。 一方で R² は log-log で必ず上がるわけではなく (総人口→出生数は 0.991→0.980)、 log 変換の主目的は R² 向上ではなく「弾力性としての解釈可能性」と「スケール非依存の係数」にある点に注意。

📐 図 (テキスト): log-log プロットの典型形状

log10(出生数)
 5.0 ┤                                           ● 東京
 4.8 ┤                                     ● 神奈川
 4.7 ┤                              ● 大阪
                                ● 愛知
 4.5 ┤                       ● 埼玉  ● 兵庫
                          ● 千葉
 4.3 ┤                  ● 福岡  ● 北海道
                    ● 静岡  ● 茨城
 4.1 ┤             ● 京都  ● 広島
              ● 宮城  ● 新潟
 3.9 ┤        ● 岐阜  ● 群馬
            ● 三重  ● 栃木
 3.6 ┤    ● 沖縄  ● 鳥取  ● 島根
       ● 高知  ● 徳島  ● 福井
     └──────────────────────────────────
       5.7   6.0   6.3   6.6   7.0  log10(人口)
  

💬 図の読み方: log-log プロットでは 47 都道府県が傾き ≈1.02 の直線上に並ぶ。 東京・神奈川・大阪の上位 3 都府県も外れ値として極端に振れず、 沖縄・鳥取・島根などの小規模県とほぼ同じ直線上に乗る。 これが「log 変換が外れ値の影響を圧縮する」効果の視覚的な現れであり、 元スケールでは東京が右上に飛んで他県が左下に固まる「対数欠如プロット」と対照的な姿となる。

⚠️ 落とし穴: 「log を取れば常に R² が上がる」は誤解

log 変換は「分布形を正規化し残差を整える」のが主目的であって、 必ずしも R² を上げない。 例えば「平均寿命 (歳)」のように既にスケールが揃っている変数では、 log 変換しても R² はほぼ変わらず、 むしろ係数の解釈性が悪化する。 「右裾分布で歪度の絶対値が 1 を超える」「ヒストグラムが滝のように右に流れる」「値域が 1-3 桁以上にまたがる」のいずれかを満たすときに log 変換を検討する、 という運用上の判断基準を持つこと。

🧠 追加の理解度チェック

  1. 「総人口→出生数」の log-log 弾力性 1.024 が「ほぼ 1」であることの含意 (人口あたり出生数=粗出生率が地域によらず概ね一定) を 80-150 字で説明せよ。
  2. SSDSE-B-2026 で「消費支出 (1世帯・円)」と「総人口 (人)」の関係を回帰するとき、 どちらに log 変換を適用するのが適切か。 両方/片方/どちらも不要、 から選び理由を述べよ。
  3. 残差歪度が元スケールと log-log で符号も大きさも変わる (例: 総人口→65歳以上は +0 付近 →-1.26) のはなぜか。 中心極限定理・ホワイト検定・ブートストラップの観点から述べよ。

🧪 SSDSE-B-2026 を用いた分布変換の総合演習

SSDSE-B-2026 から「分布形が大きく異なる 6 変数」を選び、 各変数について「元スケール統計量 → log10 変換後統計量 → Box-Cox 最適 λ → Yeo-Johnson 最適 λ」の 4 段比較を行い、 どの変換が最良か体系的に判断する。 これは特徴量エンジニアリングの実務手順そのものである。

📊 表: 6 変数の分布特性と推奨変換

変数単位最小値最大値中央値元歪度log10 後歪度Box-Cox λ推奨
総人口537,00014,086,0001,549,0002.2190.793-0.492log 強推奨
出生数3,26386,3489,5242.2920.801-0.478log 強推奨
65歳以上人口179,0003,205,000524,0001.7820.714-0.500log 強推奨
15歳未満人口65,0001,513,000197,0002.0680.734-0.460log 強推奨
転入者数7,578406,74923,7833.4010.967-0.459log 強推奨
消費支出 (1世帯)円/月223,423344,092300,652-0.520-0.8152.965変換不要

💬 表の読み方: 「マルチスケール量」5 変数 (総人口・出生数・65歳以上人口・15歳未満人口・転入者数) は元歪度 1.8-3.4、 値域が 1-2 桁以上にまたがる典型的な右裾分布。 これらは log 変換で歪度 0.7-1.0、 Box-Cox の最適 λ も -0.46〜-0.50 とほぼ log〜平方根の逆数付近に収束する。 一方「1 世帯あたり消費支出」は元歪度 -0.52 (むしろ左裾)・値域が 1.5 倍程度で、 Box-Cox の最適 λ は +2.97 と log から大きく離れる。 「歪度 |1| 超ならまず log を検討、 Box-Cox で λ が負〜0 付近なら log 系で確定」というワークフローが効率的。

🐍 Python: Box-Cox の最適 λ を MLE で推定する

このコードでやること: SSDSE-B-2026 の 6 変数について、 scipy.stats.boxcox で最尤推定された λ と、 変換後の歪度を計算し一覧表示する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1301(15歳未満人口) A1303(65歳以上人口) A4101(出生数) A5101(転入者数(日本人移動者)) 北海道 5,092,000 514,000 1,681,000 24,430 47,388 東京都 14,086,000 1,513,000 3,205,000 86,348 406,749 沖縄県 1,468,000 236,000 350,000 12,549 26,410 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import pandas as pd, numpy as np
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]
variables = {'総人口': 'A1101', '出生数': 'A4101', '65歳以上人口': 'A1303',
             '15歳未満人口': 'A1301', '転入者数': 'A5101', '消費支出': 'L3221'}
for name, code in variables.items():
    x = pd.to_numeric(d[code], errors='coerce').dropna().values.astype(float)
    if (x <= 0).any():
        print(f'{name}: 非正値ありスキップ'); continue
    bc, lam = stats.boxcox(x)
    print(f'{name:12s}: 元歪度={stats.skew(x):+.3f}, Box-Cox lam={lam:+.3f}, 変換後歪度={stats.skew(bc):+.3f}')

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

総人口 : 元歪度=+2.219, Box-Cox lam=-0.492, 変換後歪度=+0.115 出生数 : 元歪度=+2.292, Box-Cox lam=-0.478, 変換後歪度=+0.120 65歳以上人口 : 元歪度=+1.782, Box-Cox lam=-0.500, 変換後歪度=+0.105 15歳未満人口 : 元歪度=+2.068, Box-Cox lam=-0.460, 変換後歪度=+0.114 転入者数 : 元歪度=+3.401, Box-Cox lam=-0.459, 変換後歪度=+0.115 消費支出 : 元歪度=-0.520, Box-Cox lam=+2.965, 変換後歪度=+0.000

💬 結果の読み方: 「マルチスケール量」5 変数の λ は -0.46〜-0.50 で、 log (λ=0) と平方根の逆数の中間に収束し、 変換後歪度はいずれも 0.11 前後まで縮む。 一方、 1 世帯あたり消費支出は左裾 (元歪度 -0.52) のため λ=+2.97 (べき乗変換) が選ばれる。 「Box-Cox を機械的に走らせて λ を見れば、 log/sqrt/無変換/べき乗のどれが適切か即判断できる」という運用ルールが、 特徴量エンジニアリングの第一歩として極めて有効。

📐 図 (テキスト): 変換前後の歪度の劇的な縮小

歪度の絶対値
3.5 ┤ █ 転入者数
3.0 ┤ █
2.5 ┤ █             █ 出生数     █ 総人口
2.0 ┤ █             █             █             █ 15歳未満
1.5 ┤ █             █             █             █             █ 65歳以上
1.0 ┤ █             █             █             █             █
0.5 ┤ █             █             █             █             █   █ 消費支出
0.0 ┤ █▁           █▁           █▁           █▁           █▁   █▔
    └──────────────────────────────────────────────────────────────
      元 → Box-Cox 変換後 (各変数のペア)
    凡例: 高い棒 = 元スケール歪度の絶対値、 低い棒 = Box-Cox 変換後
  

💬 図の読み方: 「マルチスケール量」5 変数では元の歪度 1.8-3.4 が変換後 0.11 前後まで圧縮され、 ほぼゼロに近づく。 これは「強い右裾分布が正規分布に近づいた」直接の指標。 1 世帯あたり消費支出はもともと歪度が小さく (むしろ左裾)、 変換しても大きく動かない。 図の高低差が「log 系変換の威力を一目で示す」典型ビジュアル。

⚠️ 落とし穴: λ の信頼区間を見落とすと変換選択を誤る

Box-Cox の最適 λ は最尤推定であり、 標本サイズが小さい (n < 50) と推定の不確実性が大きい。 SSDSE-B-2026 の単一年度データ (n=47) で総人口の λ=-0.49 と推定されても、 信頼区間は幅広く、 「λ=0 (log) を採用するのが解釈上シンプルで実用的」と判断する場面が多い。 scipy.stats.boxcox_normmax でプロファイル尤度に基づく λ の信頼区間を計算し、 区間が 0 を含むなら log 変換を採用するのが実務的な運用。

🧠 演習問題

  1. SSDSE-B-2026 で「医師数 (人口 10 万人あたり)」の分布を調査せよ。 歪度・尖度を計算し、 log 変換と Box-Cox の最適 λ を求め、 推奨される変換を判定せよ。
  2. 「平均年収」と「家賃」は典型的な右裾分布。 両変数とも log 変換した上で、 log(年収) → log(家賃) の回帰係数 (弾力性) を推定し、 1 % 年収増で家賃が何 % 上がるか議論せよ。
  3. SSDSE-B-2026 の「合計特殊出生率」(0.8 - 2.1 程度) に対して、 Box-Cox 最適 λ がどの値になると予想されるか。 また、 log 変換と Box-Cox 変換のどちらを採用すべきか、 理由とともに述べよ。

🧪 対数変換の歴史・哲学・分野別の活用

対数変換は単なる「データを平らにする道具」ではなく、 物理学・経済学・生物学など多くの分野で「対数スケールこそが本質を表す」とされてきた長い歴史を持つ。 本セクションでは、 対数尺度の起源と分野別の典型活用例、 そして「いつ対数を取るべきか」という哲学的問いに簡潔に答える。

📜 歴史的背景

対数 (logarithm) は 1614 年にジョン・ネイピアが計算簡略化のために導入。 19 世紀には Weber-Fechner の法則 (人間の感覚は刺激強度の対数に比例) で「対数尺度は知覚の自然尺度」と位置づけられた。 20 世紀には地震マグニチュード (Richter 尺度)、 音圧 (dB)、 pH、 星の等級など、 「対数を取らないと議論できない」現象が次々と発見された。 統計学では Galton (1879) が「対数正規分布」を発見し、 経済学では Cobb-Douglas 生産関数 (1928) が対数線形モデルの古典となる。

📊 表: 分野別の対数変換典型ユースケース

分野対象データなぜ log か代表モデル
経済学所得・GDP・株価パレート分布的、 弾力性で議論Cobb-Douglas 生産関数
物理学地震規模・音圧・光度人間の知覚が対数的、 桁数の差が大きいRichter スケール、 dB
化学水素イオン濃度濃度が極端な桁差を取るpH = -log[H+]
生物学細胞数・酵素活性指数増殖、 マルチスケールGompertz 成長モデル
疫学罹患率・オッズ比比の正規化、 対称性確保ロジスティック回帰
情報理論確率・尤度積を和に変換、 数値安定対数尤度・エントロピー
金融資産リターン複利、 連続時間モデルBlack-Scholes、 GARCH
都市・地理人口・都市規模Zipf 則 (ランクサイズ法則)log(rank) vs log(size)

💬 表の読み方: 対数変換は「統計手法上の便宜」だけでなく、 多くの分野で「本質的に対数尺度こそが意味のある尺度」として採用されている。 SSDSE-B-2026 の都道府県人口は経済学・都市地理学の文脈にぴったり乗り、 Zipf 則 (log(順位) vs log(人口) が直線になる) が成立するか確認するのが好例である。

🐍 Python: SSDSE-B-2026 で Zipf 則を検証する

このコードでやること: SSDSE-B-2026 の 47 都道府県人口を降順にソートし、 順位 (1 位=東京, 2 位=神奈川…) と人口の log-log プロットを作成、 Zipf 則の直線性 (傾き ≈ -1) を確認する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) 北海道 5,092,000 東京都 14,086,000 沖縄県 1,468,000 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import pandas as pd, numpy as np
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]
pop = pd.to_numeric(d['A1101'], errors='coerce').dropna().values.astype(float)
pop = np.sort(pop)[::-1]              # 人口降順
rank = np.arange(1, len(pop) + 1)    # 順位 1..47
x = np.log10(rank); y = np.log10(pop)
slope, intercept, r, p, se = stats.linregress(x, y)
print(f'Zipf 直線: log10(人口) = {intercept:.3f} + {slope:.3f} x log10(順位)')
print(f'R2 = {r**2:.4f}, p = {p:.1e}')
print(f'傾き {slope:.3f} (Zipf 則の予言値 -1 との差: {abs(slope + 1):.3f})')

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

Zipf 直線: log10(人口) = 7.385 + -0.888 x log10(順位) R2 = 0.9582, p = 1.1e-32 傾き -0.888 (Zipf 則の予言値 -1 との差: 0.112)

💬 結果の読み方: 47 都道府県の人口は log-log で見ると傾き -0.89、 R²=0.96 の極めて強い直線関係を示す。 完全な Zipf 則 (傾き -1) からはやや乖離 (-0.89) しているが、 これは日本の都道府県が「東京一極集中」傾向を反映し、 上位が緩やかに下がる「準 Zipf 分布」になっているため。 米国・欧州の都市規模分布では傾きがほぼ -1 に近く、 「対数変換を取ることで初めて見えてくる普遍的法則」の典型例となる。 元スケールでは東京だけが極端に飛んで他県が左下に固まる「ホッケースティック型」のグラフになり、 法則性は見えない。

💡 哲学: 「対数を取るべきか」を決める 3 つの問い

  1. 変数の値域は何桁にまたがるか: 最小値と最大値の比が 10 倍以下なら log は不要。 100 倍以上なら log を強く検討。
  2. 変数の本質的な意味は「比率」か「差」か: 「2 倍に増えた」を「1 単位増えた」と等価に扱いたいなら log。 例: 株価は 100→200 と 1000→2000 が同等の意味。
  3. 解釈の利便性: 政策議論で「1% 変化」「弾力性」を語りたいなら log-log。 「1 単位増えると何単位増える」を語りたいなら元スケール。

💬 この 3 つの問いは「データの性質」「概念の本質」「議論の目的」の 3 層から成る。 SSDSE-B-2026 で「人口と出生数の関係」を語るなら、 (1) 値域が 26 倍にまたがり、 (2) 「人口 1% 増 → 出生数 1% 増」という比例関係こそが意味を持ち、 (3) 政策の文脈で「地域の人口規模と出生の関係」を議論したい、 という 3 条件がすべて揃うため log 変換が最適解となる。

🧪 機械学習パイプラインにおける対数変換の位置づけ

機械学習の特徴量エンジニアリングにおいて、 対数変換は「分布形を整える前処理」として広く使われる。 ただしモデルの種類によって「効くモデル」と「効かないモデル」があり、 機械的に全変数に log を適用するのは誤り。 ここでは SSDSE-B-2026 を用いて、 線形回帰・木系モデル (Random Forest, XGBoost)・k-NN・ニューラルネットの 4 種類で log 変換の効果を比較する。

📊 表: モデル別の log 変換効果

モデル種類スケール感度log の効果具体例
線形回帰非常に高い大きく効く人口 → 出生数の相対誤差が安定
Ridge / Lasso高い (正則化のため)効く標準化と組合せて使う
Decision Treeなし (順位のみ)効かない分割は順位で決まる
Random Forestなし効かない予測値の解釈で出力 log のみ有効
XGBoost / LightGBMなし入力には不要、 出力には有効目的変数を log で正規化
k-NN非常に高い (距離計算)大きく効くlog + 標準化が必須
SVM (RBF)高い効くlog + 標準化が標準
ニューラルネット高い (勾配安定性)効く入出力とも log + Batch Norm

💬 表の読み方: 木系モデル (Decision Tree, Random Forest, XGBoost) は分割が変数の順位だけで決まるため、 入力に log を取っても予測結果は変わらない。 一方、 線形回帰・k-NN・SVM・NN などは「距離」や「線形結合」を計算するため、 値のスケール差が大きい変数 (人口 vs 失業率など) は log で揃えないと前処理として不適切。 「モデルを決めてから log を適用するか判断する」のがプロの流儀。

🐍 Python: 3 モデル (線形回帰・RF・k-NN) で log の効果を比較

このコードでやること: SSDSE-B-2026 で「総人口・65歳以上人口・15歳未満人口」から「出生数」を予測する 3 特徴量回帰を、 線形回帰・Random Forest・k-NN で実行。 入力・目的変数を log10 変換した場合と元スケールの場合で RMSE を比較する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1301(15歳未満人口) A1303(65歳以上人口) A4101(出生数) 北海道 5,092,000 514,000 1,681,000 24,430 東京都 14,086,000 1,513,000 3,205,000 86,348 沖縄県 1,468,000 236,000 350,000 12,549 …(全 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
import pandas as pd, numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
from sklearn.neighbors import KNeighborsRegressor
from sklearn.metrics import mean_squared_error
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].copy()
for c in ['A1101', 'A1303', 'A1301', 'A4101']:
    d[c] = pd.to_numeric(d[c], errors='coerce')
d = d.dropna(subset=['A1101', 'A1303', 'A1301', 'A4101'])
features = ['A1101', 'A1303', 'A1301']  # 総人口・65歳以上人口・15歳未満人口
y = d['A4101'].values                    # 出生数
for label, X in [('元スケール', d[features].values.astype(float)),
                 ('log10 変換', np.log10(d[features].values.astype(float)))]:
    yp = np.log10(y) if 'log10' in label else y
    Xs = StandardScaler().fit_transform(X)
    for mname, M in [('LinReg', LinearRegression()),
                     ('RF', RandomForestRegressor(n_estimators=100, random_state=42)),
                     ('k-NN', KNeighborsRegressor(n_neighbors=5))]:
        M.fit(Xs, yp)
        rmse = np.sqrt(mean_squared_error(yp, M.predict(Xs)))
        print(f'{label} {mname:7s}: RMSE = {rmse:,.4f}')

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

元スケール LinReg : RMSE = 775.9024 元スケール RF : RMSE = 1,776.4671 元スケール k-NN : RMSE = 4,798.2588 log10 変換 LinReg : RMSE = 0.0161 log10 変換 RF : RMSE = 0.0184 log10 変換 k-NN : RMSE = 0.0529

💬 結果の読み方: 元スケール (RMSE は出生数の人数単位) と log10 スケール (RMSE は対数単位) は直接比較できないが、 「目的変数を log 変換すると相対誤差が安定する」点が重要。 元スケールの線形回帰は東京・大阪などの上位県で大きな絶対誤差を出すが、 log スケールでは相対誤差が均等に評価される。 どのスケールでも LinReg が最も小さく、 k-NN は近傍 5 点の平均で丸まるため誤差が最大になる。 「k-NN や SVM では log + 標準化が必須」という実務原則がここから理解できる。

⚠️ 落とし穴: 目的変数の log 変換と評価指標の整合性

目的変数を log 変換してモデルを学習する場合、 RMSE や MAE などの評価指標も log スケールで計算される。 元スケールでの予測精度を報告するには、 必ず逆変換してから評価指標を再計算する必要がある。 また、 RMSLE (Root Mean Squared Log Error) のように「log スケールで誤差を測る」評価指標を使うのが、 Kaggle 等のコンペで標準的になりつつある。 SSDSE-B-2026 で出生数などの右裾変数の予測タスクを設計するなら、 RMSE ではなく RMSLE を主指標にするのが理に適っている。

🧠 まとめ問題

  1. SSDSE-B-2026 で「総人口」「15歳未満人口」「65歳以上人口」の 3 変数を用いて「出生数」を予測する。 線形回帰と Random Forest のどちらにも log 変換を入れるべきか。 入力 / 目的変数のどちらにか。 理由とともに答えよ。
  2. k-NN で「65歳以上人口」と「総人口」の 2 特徴量から「出生数」を予測する。 (1) log 変換が必要な変数はどれか、 (2) 標準化も併用すべきか、 (3) 距離尺度は Euclidean か Manhattan か、 議論せよ。
  3. Kaggle の住宅価格予測コンペでは目的変数を log 変換するのが定石。 SSDSE-B-2026 で「出生数の都道府県別分布」を予測する場合、 同様に log を取るべきか。 そのときの評価指標として RMSE・RMSLE・MAPE のどれが最適か。

🧪 SSDSE-B-2026 時系列での対数変換: 成長率の解釈

時系列データにおいて、 log 差分は「成長率」を直接表現する強力なツールである。 SSDSE-B-2026 の 2015-2023 年度データから「全国の総人口」「全国の65歳以上人口」の年次推移 (47 都道府県の合計) を取り、 元スケール差分と log 差分の意味を比較する。

📐 数式: log 差分と成長率の関係

時刻 $t$ の値を $X_t$、 1 期前を $X_{t-1}$ とすると、 成長率は

$$g_t = \frac{X_t - X_{t-1}}{X_{t-1}} \approx \ln X_t - \ln X_{t-1}$$

「対数差分は成長率の良い近似 (1 次のテイラー展開)」という関係が成立する。 例えば $X_t / X_{t-1} = 1.05$ (5% 増) なら $\ln(1.05) = 0.0488$ で、 実成長率 0.05 とほぼ一致。 ただし 20% 以上の高成長期では誤差が出始める ($\ln(1.20) = 0.1823$ vs 真値 0.2)。 経済時系列では log 差分は「連続複利的成長率」として解釈され、 累積成長を加算で扱える利点がある。

📊 表: SSDSE-B-2026 全国計の log 差分 (年次成長率)

年度総人口 (人)log 差分65歳以上人口 (人)log 差分
2015127,094,74533,465,441
2016127,044,000-0.000434,590,000+0.0331
2017126,920,000-0.001035,149,000+0.0160
2018126,748,000-0.001435,575,000+0.0120
2019126,555,000-0.001535,886,000+0.0087
2020126,146,099-0.003235,335,805-0.0155
2021125,500,000-0.005136,215,000+0.0246
2022124,946,000-0.004436,235,000+0.0006
2023124,353,000-0.004836,229,000-0.0002

💬 表の読み方: 総人口は 2015-2023 年で一貫して負成長 (年率 -0.04% から -0.51%) で、 log 差分の絶対値がおおむね年々増加 (減少率の加速) を示す。 一方 65歳以上人口は 2016-2019 年に年率 +0.9〜3.3% と急増し、 log 差分の符号が総人口とは逆。 2020 年の census 定義変更・年齢不詳の扱いで一時的に負の値が出る点は実データならではの注意点。 元スケール差分では桁が異なり比較しにくいが、 log 差分にすれば「総人口の縮小」と「高齢者の増加」を同じ成長率スケールで並べられる点が log の威力。

🐍 Python: 都道府県別年率成長率の log 差分計算

このコードでやること: SSDSE-B-2026 から「東京・大阪・愛知・沖縄」4 都府県の 2012→2023 年の総人口成長率を、 元の比率と log 差分で並べ、 年率成長率を計算する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) 北海道 5,092,000 東京都 14,086,000 沖縄県 1,468,000 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import pandas as pd, numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df['A1101'] = pd.to_numeric(df['A1101'], errors='coerce')  # 総人口
prefs = ['東京都', '大阪府', '愛知県', '沖縄県']
for pref in prefs:
    d = df[df['Prefecture'] == pref].sort_values('SSDSE-B-2026')
    pop = d['A1101'].values
    yrs = d['SSDSE-B-2026'].values
    ratio = pop[-1] / pop[0]
    log_diff = np.log(pop[-1]) - np.log(pop[0])
    n_years = yrs[-1] - yrs[0]
    annual_pct = (ratio ** (1 / n_years) - 1) * 100
    print(f'{pref}: {yrs[0]}={pop[0]:>10,.0f}人, {yrs[-1]}={pop[-1]:>10,.0f}人, '
          f'log差分={log_diff:+.4f}, 年率={annual_pct:+.2f}%')

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

東京都: 2012=13,234,000人, 2023=14,086,000人, log差分=+0.0624, 年率=+0.57% 大阪府: 2012= 8,861,000人, 2023= 8,763,000人, log差分=-0.0111, 年率=-0.10% 愛知県: 2012= 7,431,000人, 2023= 7,477,000人, log差分=+0.0062, 年率=+0.06% 沖縄県: 2012= 1,411,000人, 2023= 1,468,000人, log差分=+0.0396, 年率=+0.36%

💬 結果の読み方: 東京は年率 +0.57% で 11 年間ほぼ連続成長、 大阪は -0.10% と微減。 愛知 +0.06% で横ばい、 沖縄 +0.36% で人口増を維持。 規模の異なる 4 地域の成長率を「同じスケールで」比較できる。 元の差分 (東京 +852,000 人、 沖縄 +57,000 人) を見せても比較しにくいが、 log 差分 / 年率にすれば「沖縄は東京に次ぐ人口増スピード」と即座に判断できる。 これが「log 差分は成長率」の威力。

⚠️ 落とし穴: 高成長期では log 差分と実成長率に差が出る

log 差分は実成長率の 1 次近似で、 成長率が小さい (年率 10% 未満) ときは誤差 1% 以内で実用十分。 ただし 50% 超の高成長 (新興企業の売上、 ハイパーインフレ国の通貨など) では誤差が顕著になる。 例: 2 倍成長 = log 差分 0.693 だが、 実成長率 100% とは違う。 報告書で「log 差分」を使う際は、 必ず「成長率の対数近似」であることを明示し、 1 桁以上の変化では実比率を併記すること。

📋 まとめ: 対数変換の使い方チェックリスト

本ページの議論を 1 ページのチェックリストとして整理する。 実務で対数変換を検討するときは、 以下の項目を順番に確認すれば、 「取るべきか / どう取るべきか / どう解釈するか」まで一貫した判断が可能となる。

📊 表: 対数変換の判断・実装・解釈 7 ステップ

ステップ確認項目合格基準
1. 値域確認最大値/最小値の比は?10 倍超なら log 候補、 100 倍超なら強推奨
2. 歪度確認skewness の絶対値は?|skew| > 1 で log 検討
3. ゼロ/負値確認ゼロや負値はあるか?あれば log1p または Yeo-Johnson
4. 変換実施log10 vs ln vs Box-Cox?解釈性 → log10、 数学的 → ln、 最適化 → Box-Cox
5. 変換後検証歪度が改善したか?変換後 |skew| < 0.5 で OK
6. モデル適合残差正規性は確保?Shapiro-Wilk p > 0.05 で確保
7. 結果報告解釈・逆変換の方法を明記?弾力性・semi-elasticity・補正の明示

💬 表の読み方: 7 ステップは「データの確認 (1-3) → 変換実施 (4) → 変換後の検証 (5-6) → 報告 (7)」という流れ。 ステップを飛ばすと「ゼロ値で NaN が混入」「変換後も歪度が残る」「解釈が semi-elasticity と弾力性で混ざる」などの実務トラブルが頻発する。 SSDSE-B-2026 の各変数に対してこの 7 ステップを一巡させれば、 教育用ハンズオン教材として十分な厳密性が確保される。

💡 最後に: 「log を取らない選択肢」も忘れない

本ページは「対数変換が効くケース」を多数紹介してきたが、 実務では「あえて log を取らない選択肢」も重要。 たとえば「年収分布の不平等を議論したい」とき、 元スケールでの分散や標準偏差は「絶対的な格差」を表すが、 log スケールに変換すると「比率的格差」しか見えなくなる。 ジニ係数の計算でも元スケールが標準。 同様に、 「政策議論で 1 円単位の議論をしたい」「広告効果のリフト額を金額で示したい」といったケースでは元スケールが適切。 「log は万能ではなく、 議論したい量に応じて使い分ける」が結論。 SSDSE-B-2026 を使って「どの分析でどちらが適切か」を自分で判断する力を養うことが、 本ページの最終的な目的である。 対数変換は分析者の道具箱の中で最も古く、 最も誤解されやすく、 最も強力なものの一つであり続けている。

🖼 視覚的理解 (3 図)

対数変換の効果を SSDSE-B-2026 都道府県データで可視化する。 (1) 変換前後の散布図、 (2) 歪度の改善ヒストグラム、 (3) 地方区分での分布比較の 3 切り口。

人口と出生数の散布図 (log 変換前後)
図 1: 都道府県総人口 × 出生数の散布図。 元スケールでは東京・大阪が右上に偏り曲線関係に見えるが、 両軸を log 変換するとほぼ直線になり、 弾力性 (elasticity) として解釈できる。
出生数のヒストグラム (元スケール vs log)
図 2: 出生数の分布。 元スケールでは右に長い裾を持つ歪んだ分布だが、 log 変換後は対数正規 → 正規に近づき、 平均・標準偏差での要約や t 検定が有効になる。
地方別人口の箱ひげ図比較
図 3: 8 地方区分での人口の箱ひげ図。 元スケールでは関東のみ突出して箱が見えないが、 log 変換で全地方の中央値・四分位範囲が比較可能になる。

🔗 隣接手法への橋渡し

対数変換は歪んだ分布を正規分布に近づける前処理であり、 前段の分布確認 (QQ プロット) と後段の回帰・検定の妥当性向上を繋ぐ。

上流のヒストグラム・歪度確認で対数変換の必要性を判断し、 並列の Box-Cox/Yeo-Johnson 変換と比較して負値・ゼロ対応を選び、 下流の残差プロットで変換後の等分散性が改善されたかを検証する流れで「右に裾を引く変数」の解析が安定する。

🌳 手法選択フロー

対数変換 を実際の課題に当てはめるとき、 用語固有の判断軸に沿って次の 3 段階で適切な選択を行う。

  1. 変数の歪度が大きいか (歪度 > 1)? Yes → 対数変換を検討、 No → 変換不要
  2. ゼロや負値を含むか? Yes → log(x+1) / Yeo-Johnson 変換、 No → 通常の log(x) で OK
  3. 非線形性が複雑 (S 字等) か? Yes → Box-Cox (λ 自動選択) / GAM、 No → 対数変換で十分

このフローは変数変換選択の基本方針。 対数変換は「右に裾を引く分布の正規化」と「乗法関係の加法化」を同時に達成する万能ツールだが、 ゼロ・負値対応で log(x+1) や Yeo-Johnson に切替える判断が必要。

🔎 解説の深化

直感 — 「掛け算の世界」を「足し算の世界」に写す

対数変換の核心は、 比が等しい差を、等しい距離に置き換えることです。 通常のスケールでは「100→200」と「10,000→20,000」はまったく違う大きさの差ですが、 どちらも「2倍」なので対数上では ln2 ≈ 0.693 という同じ幅になります。 このページで扱う三つの現象――右歪みの是正、 冪関係の両対数直線化、 弾力性としての係数解釈――は、 すべて「乗法的な構造を加法的に開く」というこの一点から派生します。 だから「桁が問題になるデータ」(人口・売上・宿泊者数・所得のように何倍もの開きがある量)ほど対数が効きます。

同じ理由で、 対数変換後に取った算術平均を元へ戻すと「幾何平均」になりますexp(mean(ln x)) = 幾何平均)。 対数の世界での「まん中」は、 元の世界では「比のまん中」なのです。

落とし穴(重要)— log は歪度を「ゼロにする魔法」ではない/代表値がすり替わる

実測で確かめます。 SSDSE-B-2026.csv(2023年・47都道府県)の右歪み列に自然対数を適用した歪度の変化です(scipy.stats.skew(bias=False))。

列(2023, n=47) 原スケール歪度 log後の歪度 最大/最小 比
A1101 総人口2.2930.82026.2倍
G7101 延べ宿泊者数3.4270.72047.2倍
G7102 外国人延べ宿泊者数4.8270.898732.5倍

落とし穴①: 歪度は確かに大きく下がりますが、 どれも 0 には届いていません(0.7〜0.9 残存)。 「log を取れば正規分布になる」は言い過ぎで、 完全対称を仮定した推論(例えば log 後にそのまま t 検定)には依然として注意が要ります。 効果の大きさは元の歪みの強さ次第で、 歪度を事前・事後で必ず数値確認するのが安全です。

落とし穴②(代表値のすり替え): log 平均を exp で戻すと算術平均ではなく幾何平均になり、 幾何平均は AM–GM 不等式より常に算術平均以下です。 外国人延べ宿泊者数(2023)では、 算術平均 2,021,866 に対し 幾何平均 460,382(中央値 276,670)で、 約4.4倍もの開きがあります。 「対数空間で平均して戻した値」を無自覚に『平均宿泊者数』と呼ぶと、 少数の巨大県(東京 3,437万人泊)に引っ張られる算術平均とは別物の代表値を報告することになります。 どちらを示すかは目的次第ですが、 混同は禁物です。

ゼロ対策の再確認: 上記はすべて正の値なので np.log がそのまま使えますが、 0 を含む列(新規開業件数・特定分野の件数など、 一部県が 0 になりうる量)では np.log(0) = -inf で破綻します。 np.log1p(x)=ln(1+x) を使えば x=0 → 0 と自然に扱え、 値が大きい領域では ln x とほぼ一致するため実務の既定手段になります。

発展 — 対数は Box–Cox の特殊ケース/指数成長は log で直線になる

自然対数は、 パラメータ付き変換 Box–Cox (x^λ − 1)/λλ→0 の極限にあたります。 つまり「log にするか、 平方根にするか、 生のままか」を データに λ を推定させて選ぶのが Box–Cox(正値限定)/Yeo–Johnson(負値も可)です。 log は「λ を 0 に固定した決め打ち」だと理解すると、 変換の選択肢が体系的に見えます。

もう一つの発展は時系列です。 一定率で伸びる量(指数成長)は、 対数を取ると直線になり、 傾きがそのまま成長率になります。 実測(東京都・外国人延べ宿泊者数 2012→2019)では 7,918,960 → 27,958,830 と増え、 ln y を年で回帰した傾きは 0.172、 すなわち 年率約18.7%の一定成長として要約できます(exp(0.172)−1)。 生スケールの折れ線では「近年ほど急」に見える伸びが、 対数軸では一定勾配の直線に整い、 成長の本質(率)が読み取りやすくなります。

※上の歪度・平均・成長率はいずれも SSDSE-B-2026.csvpd.read_csv(encoding='cp932', skiprows=[1]) で読み、 該当年で抽出して算出した実測値です(合成データではありません)。

幾何平均歪度Box–Cox 変換の単独ページは本用語集には未収録のため、 ここでは本文内の説明にとどめています。