ノンパラメトリック法を学ぼう!
「データが正規分布に従わない」。データ分析でこんな壁に当たったことはありませんか?実際のビジネスデータは教科書通りの美しい分布になることは稀で、普通、従来の統計手法の前提条件を満たしません。今回は、そんな「現実のデータ」に立ち向かう強力な武器、ノンパラメトリック法について学んでいきましょう。
ノンパラメトリック法とは何か
ノンパラメトリック法(非母数統計)とは、データの分布について特定の仮定(正規分布など)を置かずに統計的推論を行う手法の総称です。「母数(パラメータ)」を仮定しない、という意味で「ノンパラメトリック」と呼ばれています。
パラメトリック法との違い
パラメトリック法(母数統計)
- 例:t検定、分散分析、回帰分析
- 前提条件:データが特定の分布(通常は正規分布)に従う
- メリット:条件を満たせば検出力が高い
- デメリット:前提条件が厳しい
ノンパラメトリック法(非母数統計)
- 例:ウィルコクソン検定、クラスカル・ウォリス検定
- 前提条件:分布の仮定が不要または緩い
- メリット:幅広いデータに適用可能、外れ値に頑健
- デメリット:一般的に検出力が若干低い
いつ使うべきか?
以下のような状況でノンパラメトリック法が威力を発揮します:
- 小サンプル:n < 30程度で正規性の確認が困難
- 非正規分布:歪んだ分布、複数の山がある分布
- 外れ値の存在:極端な値がデータに含まれている
- 順序尺度データ:「満足・普通・不満足」などの順序データ
- 分布が未知:どの分布に従うか不明
主要なノンパラメトリック検定法
データ分析でよく使われる代表的なノンパラメトリック法を紹介します。
1. 符号検定(Sign Test)
目的:1つの標本の中央値が特定の値と等しいかを検定、または対応のある2標本の中央値の差を検定
仕組み:データを基準値と比較して「+」「-」の符号に変換し、プラスの数とマイナスの数を比較する最もシンプルな検定
数式:
帰無仮説 H0 の下で、プラスの符号の個数 S は二項分布 B(n, 0.5) に従う
ここで、n は総サンプル数、k は観測されたプラスの符号の個数
使用場面:
- 顧客満足度(5段階評価)が中央値の3より高いか低いかを調べる
- 新しい治療法の前後で症状が改善したかを調べる
2. 符号付き順位検定(Signed Rank Test / ウィルコクソンの符号付き順位検定)
目的:対応のある2標本の分布の位置が同じかを検定(対応のあるt検定のノンパラメトリック版)
仕組み:差の絶対値で順位付けし、その順位に元の差の符号を付けて検定統計量を計算
数式:
検定統計量 V+ は、正の差の順位和:
ここで、di は対応データの差、R(|di|) は |di| の順位
大サンプルでは正規近似:
使用場面:
- 研修前後でスキルテストのスコアに差があるかを調べる
- 新旧システムでの作業時間に差があるかを調べる
3. ウィルコクソンの順位和検定(Wilcoxon Rank-Sum Test / マン・ホイットニーのU検定)
目的:独立した2標本の分布の位置が同じかを検定(対応のないt検定のノンパラメトリック版)
仕組み:2つの標本を合併して順位付けし、各群の順位和を比較
数式:
検定統計量 W1(第1群の順位和):
ここで、R(X1i) は合併データでの X1i の順位
大サンプルでの正規近似:
使用場面:
- A商品とB商品の顧客評価に差があるかを調べる
- 男女間で購買金額に差があるかを調べる
4. 並べ替え検定(Permutation Test)
目的:観測された差が偶然によるものかを、データの並び替えによって直接的に評価
仕組み:実際のデータをランダムに並び替えて得られる統計量の分布と、観測値を比較
数式:
p 値の計算:
ここで、T*(π) は並び替え π での統計量、T_obsは観測された統計量、Π は全並び替えの集合
使用場面:
- 小サンプルでの群間比較
- 複雑なデータ構造での仮説検定
5. クラスカル・ウォリス検定(Kruskal-Wallis Test)
目的:3つ以上の独立標本の分布の位置が同じかを検定(一元配置分散分析のノンパラメトリック版)
仕組み:全データを合併して順位付けし、各群の平均順位を比較
数式:
検定統計量 H:
ここで、k は群数、N は総サンプル数、ni は第 i 群のサンプル数、Ri は第 i 群の順位和
H は自由度 k-1 の χ2 分布に近似的に従う
使用場面:
- 複数の営業支店間で売上実績に差があるかを調べる
- 複数の年齢層間で商品評価に差があるかを調べる
6. 順位相関係数(Rank Correlation)
目的:2つの変数間の順位に基づく関連性を測定(主にスピアマンの順位相関係数)
仕組み:各変数を順位に変換してから相関係数を計算
数式:
スピアマンの順位相関係数 rs:
ここで、di は第 i 番目の観測値の2変数間の順位差、n はサンプル数
同順位がない場合の簡易式で、一般的には:
使用場面:
- 顧客満足度とリピート購入意向の関係を調べる
- 従業員の評価順位と昇進順位の関係を調べる
実際にやってみよう:Rによる実践例
それでは、ウィルコクソンの順位和検定と符号付き順位検定の実例をRで実行してみましょう。
例1:ウィルコクソンの順位和検定(独立した2標本の比較)
シナリオ:A店舗とB店舗での顧客満足度(10点満点)に差があるかを調べる。
# 必要なパッケージの読み込み
library(ggplot2)
library(dplyr)
# サンプルデータの生成
set.seed(123)
# A店舗:やや高い満足度だが分布が歪んでいる
store_A <- c(6, 7, 8, 7, 9, 6, 8, 7, 9, 10, 5, 8, 7, 9, 8)
# B店舗:やや低い満足度で外れ値あり
store_B <- c(4, 5, 6, 5, 7, 4, 6, 5, 3, 8, 5, 6, 4, 2, 7)
# データの確認
cat("A店舗の満足度:", store_A, "\n")
cat("B店舗の満足度:", store_B, "\n")
# 基本統計量
cat("\n=== 基本統計量 ===\n")
cat("A店舗 - 中央値:", median(store_A), ", 平均:", round(mean(store_A), 2), "\n")
cat("B店舗 - 中央値:", median(store_B), ", 平均:", round(mean(store_B), 2), "\n")
# ウィルコクソンの順位和検定(マン・ホイットニーのU検定)
wilcox_result <- wilcox.test(store_A, store_B, alternative = "two.sided", exact = FALSE)
cat("\n=== ウィルコクソンの順位和検定結果 ===\n")
cat("検定統計量 W:", wilcox_result$statistic, "\n")
cat("p値:", round(wilcox_result$p.value, 4), "\n")
cat("結論:", ifelse(wilcox_result$p.value < 0.05,
"有意水準5%で2つの店舗に有意差あり",
"有意水準5%で2つの店舗に有意差なし"), "\n")
# データの可視化
data_plot <- data.frame(
satisfaction = c(store_A, store_B),
store = c(rep("A", length(store_A)), rep("B", length(store_B)))
)
# ボックスプロット(タイトルとラベルを大きく表示)
p1 <- ggplot(data_plot, aes(x = store, y = satisfaction, fill = store)) +
geom_boxplot(alpha = 0.7) +
geom_jitter(width = 0.2, alpha = 0.6) +
labs(title = "Store Satisfaction Distribution",
x = "Store",
y = "Satisfaction Score") +
theme_minimal() +
theme(legend.position = "none",
plot.title = element_text(size = 20, face = "bold", hjust = 0.5),
axis.title.x = element_text(size = 16, face = "bold"),
axis.title.y = element_text(size = 16, face = "bold"),
axis.text = element_text(size = 14))
print(p1)実行結果
A店舗の満足度: 6 7 8 7 9 6 8 7 9 10 5 8 7 9 8
B店舗の満足度: 4 5 6 5 7 4 6 5 3 8 5 6 4 2 7
=== 基本統計量 ===
A店舗 - 中央値: 8 , 平均: 7.6
B店舗 - 中央値: 5 , 平均: 5.13
=== ウィルコクソンの順位和検定結果 ===
検定統計量 W: 198
p値: 4e-04
結論: 有意水準5%で2つの店舗に有意差あり例2:符号付き順位検定(対応のある2標本の比較)
シナリオ:研修前後でのスキルテストスコア(100点満点)に改善効果があるかを調べる。
# 対応のあるデータ(研修前後のスコア)
set.seed(456)
employee_id <- 1:20
# 研修前スコア(60-80点台が中心)
before_training <- c(65, 72, 68, 59, 74, 61, 69, 75, 63, 71,
66, 73, 67, 70, 64, 76, 68, 69, 62, 74)
# 研修後スコア(研修効果でやや上昇、但し個人差あり、一部は下がる)
after_training <- c(70, 78, 71, 63, 79, 58, 75, 80, 69, 76,
72, 69, 73, 75, 62, 81, 74, 73, 59, 78)
# データフレーム作成
training_data <- data.frame(
employee_id = employee_id,
before = before_training,
after = after_training,
difference = after_training - before_training
)
# データの確認
cat("研修前後のスコアデータ(最初の10名):\n")
print(head(training_data, 10))
# 基本統計量
cat("\n=== 基本統計量 ===\n")
cat("研修前 - 中央値:", median(before_training), ", 平均:", round(mean(before_training), 2), "\n")
cat("研修後 - 中央値:", median(after_training), ", 平均:", round(mean(after_training), 2), "\n")
cat("差の中央値:", median(training_data$difference), ", 平均:", round(mean(training_data$difference), 2), "\n")
# 符号付き順位検定(ウィルコクソンの符号付き順位検定)
signed_rank_result <- wilcox.test(after_training, before_training,
paired = TRUE, alternative = "greater", exact = FALSE)
cat("\n=== 符号付き順位検定結果 ===\n")
cat("検定統計量 V:", signed_rank_result$statistic, "\n")
cat("p値:", round(signed_rank_result$p.value, 4), "\n")
cat("結論:", ifelse(signed_rank_result$p.value < 0.05,
"有意水準5%で研修効果あり(スコア向上)",
"有意水準5%で研修効果なし"), "\n")
# 改善の方向性分析
positive_changes <- sum(training_data$difference > 0)
negative_changes <- sum(training_data$difference < 0)
no_change <- sum(training_data$difference == 0)
cat("\n=== 改善方向の分析 ===\n")
cat("スコア向上した人数:", positive_changes, "名\n")
cat("スコア低下した人数:", negative_changes, "名\n")
cat("変化なし:", no_change, "名\n")
# データの可視化
# 研修前後の比較
p2 <- ggplot(training_data, aes(x = before, y = after)) +
geom_point(alpha = 0.7, size = 3, color = "steelblue") +
geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "red") +
geom_smooth(method = "lm", se = FALSE, color = "green", alpha = 0.6, formula = y ~ x) +
labs(title = "Training Score Comparison: Before vs After",
x = "Before Training Score",
y = "After Training Score") +
annotate("text", x = 60, y = 80,
label = "Red line: No change\nGreen line: Actual trend",
hjust = 0, vjust = 1, size = 4) +
theme_minimal() +
theme(plot.title = element_text(size = 18, face = "bold", hjust = 0.5),
axis.title.x = element_text(size = 15, face = "bold"),
axis.title.y = element_text(size = 15, face = "bold"),
axis.text = element_text(size = 12))
print(p2)
# 差分の分布
p3 <- ggplot(training_data, aes(x = difference)) +
geom_histogram(bins = 8, fill = "lightblue", color = "black", alpha = 0.7) +
geom_vline(xintercept = 0, linetype = "dashed", color = "red", linewidth = 1) +
geom_vline(xintercept = median(training_data$difference),
linetype = "solid", color = "blue", linewidth = 1) +
labs(title = "Distribution of Score Changes",
x = "Score Change (After - Before)",
y = "Frequency") +
annotate("text", x = 2, y = 4,
label = paste("Median:", median(training_data$difference)),
color = "blue", size = 4) +
theme_minimal() +
theme(plot.title = element_text(size = 18, face = "bold", hjust = 0.5),
axis.title.x = element_text(size = 15, face = "bold"),
axis.title.y = element_text(size = 15, face = "bold"),
axis.text = element_text(size = 12))
print(p3)実行結果
研修前後のスコアデータ(最初の10名):
employee_id before after difference
1 1 65 70 5
2 2 72 78 6
3 3 68 71 3
4 4 59 63 4
5 5 74 79 5
6 6 61 58 -3
7 7 69 75 6
8 8 75 80 5
9 9 63 69 6
10 10 71 76 5
=== 基本統計量 ===
研修前 - 中央値: 68.5 , 平均: 68.3
研修後 - 中央値: 73 , 平均: 71.75
差の中央値: 5 , 平均: 3.45
=== 符号付き順位検定結果 ===
検定統計量 V: 196.5
p値: 3e-04
結論: 有意水準5%で研修効果あり(スコア向上)
=== 改善方向の分析 ===
スコア向上した人数: 16 名
スコア低下した人数: 4 名
変化なし: 0 名
実行結果の解釈
ウィルコクソンの順位和検定の解釈:
- p値が0.05未満なら、2つの店舗の満足度に統計的有意差あり
- 検定統計量Wが大きいほど、最初のグループ(A店舗)の値が大きい傾向
符号付き順位検定の解釈:
- p値が0.05未満なら、研修効果が統計的に有意
- 検定統計量Vは順位の和を表し、値が大きいほど改善効果が大きい
まとめ
ノンパラメトリック法は、完璧ではないデータと向き合う現実のデータサイエンティストにとって不可欠なツールキットです。正規分布の美しさに惑わされることなく、目の前にあるデータの特性を受け入れ、適切な手法を選択する。それこそが、ビジネスの現場で価値ある洞察を生み出す第一歩なのです。
次回データ分析を行う際は、いきなりt検定などに飛び込む前に、「このデータにはノンパラメトリック法の方が適しているのではないか?」と一度立ち止まって考えてみてください。その小さな意識変化が、より信頼性の高い分析結果につながるはずです。


