マクネマー検定は、対応のある二値データの比較 (例: 例えば、同一患者の治療前後の症状改善を比較) に用いられる統計手法であり、医療、心理学、社会科学などの分野で広く活用されています。 R では mcnemar.test() が標準ですが、それがベストとは限りません。Mid-p test の有用性が証明されていますが、広く知られるには至っておらず、mcnemar.test() では実行できません。 本レポートでは、以下の4つのマクネマー検定手法を包括的に比較し、その理論的背景、長所・短所、適用条件を解説します。さらに、シミュレーションを通じて各手法の性能(Type I error制御と検出力)を評価し、実践的な推奨事項を提示します。
Asymptotic test (R標準: mcnemar.test(correct=FALSE)
)
Asymptotic test with continuity correction (R標準: mcnemar.test(correct=TRUE)
)
Exact test (二項分布ベース)
Mid-p test (現在最も推奨される手法)
2×2分割表の構造:
時点2陽性 | 時点2陰性 | 合計 | |
---|---|---|---|
時点1陽性 | a | b | a+b |
時点1陰性 | c | d | c+d |
合計 | a+c | b+d | n |
重要ポイント: マクネマー検定では不一致ペア (b, c) のみが統計量の計算に使用されます。一致ペア (a, d) は検定統計量に寄与しません。
基本原理: カイ二乗分布による大標本近似
統計量:
χ² = (b - c)² / (b + c)
適用条件: b + c ≥ 10 (目安)
長所と短所:
基本原理: Yatesの連続性補正付きカイ二乗近似
統計量:
χ² = max(0, (|b - c| - 1)²) / (b + c)
歴史的背景: 離散分布を連続分布で近似する際の補正として提案
長所と短所:
基本原理: 二項分布による厳密な確率計算
p値計算:
p = 2 × P(Binomial(n = b+c, p = 0.5) ≤ min(b,c))
理論的背景: 帰無仮説下でbは二項分布B(b+c, 0.5)に従う
長所と短所:
基本原理: Exact testの保守性を緩和する調整
p値計算:
Mid-p = 2 × [P(X ≤ min(b,c)) - 0.5 × P(X = min(b,c))] where X ~ Binomial(n = b+c, p = 0.5)
統計学的根拠: 観測値の確率の半分を減算することで離散性による保守的バイアスを補正
長所:
マクネマー検定においては、Mid-p testが現在の統計学的コンセンサスであり、第一種過誤率の制御と検出力のバランスが最も優れています。従来広く使用されてきたExact testやContinuity correctionは、現代的観点では推奨されません。研究の質を向上させるため、Mid-p testの採用を強く推奨します。
関連論文:
各手法のType I error制御と検出力を包括的に比較
# マクネマー検定: 各方法の性能比較シミュレーション set.seed(123) # 再現性のため # 1. Mid-p test の実装 midp_mcnemar <- function(b, c) { if (b + c == 0) return(1) n <- b + c min_bc <- min(b, c) p_midp <- 2 * (pbinom(min_bc, n, 0.5) - 0.5 * dbinom(min_bc, n, 0.5)) return(min(p_midp, 1)) } # 2. Exact test の実装 exact_mcnemar <- function(b, c) { if (b + c == 0) return(1) min_bc <- min(b, c) p_exact <- 2 * pbinom(min_bc, b + c, 0.5) return(min(p_exact, 1)) } # 3. データ生成関数 generate_mcnemar_data <- function(n, p10, p01, p11) { p00 <- 1 - p10 - p01 - p11 if (p00 < 0) stop("確率の合計が1を超えています") outcomes <- rmultinom(1, n, c(p11, p10, p01, p00)) return(list(a = outcomes[1], b = outcomes[2], c = outcomes[3], d = outcomes[4])) } # 4. 単一シミュレーション関数 run_single_simulation <- function(n_pairs, p10, p01, p11, n_sim = 5000) { results <- data.frame( r_standard = numeric(n_sim), r_corrected = numeric(n_sim), exact = numeric(n_sim), midp = numeric(n_sim) ) for (i in 1:n_sim) { data <- generate_mcnemar_data(n_pairs, p10, p01, p11) table_2x2 <- matrix(c(data$a, data$c, data$b, data$d), nrow = 2) # R標準のmcnemar.testを使用 (エラーハンドリング付き) tryCatch({ mcnemar_false <- mcnemar.test(table_2x2, correct = FALSE) mcnemar_true <- mcnemar.test(table_2x2, correct = TRUE) results$r_standard[i] <- mcnemar_false$p.value results$r_corrected[i] <- mcnemar_true$p.value }, error = function(e) { results$r_standard[i] <<- NA results$r_corrected[i] <<- NA }) results$exact[i] <- exact_mcnemar(data$b, data$c) results$midp[i] <- midp_mcnemar(data$b, data$c) } return(results) } # 5. Type I Error シミュレーション type1_error_simulation <- function(n_pairs, p10_p01, p11, n_sim = 5000, alpha = 0.05) { cat(sprintf("Type I Error シミュレーション (n=%d, p10=p01=%.2f, p11=%.2f)\n", n_pairs, p10_p01, p11)) results <- run_single_simulation(n_pairs, p10_p01, p10_p01, p11, n_sim) # NAを除いて計算 type1_rates <- sapply(results, function(x) { valid_values <- x[!is.na(x)] if(length(valid_values) == 0) return(NA) mean(valid_values < alpha) }) # NAの割合も計算 na_rates <- sapply(results, function(x) mean(is.na(x))) result_df <- data.frame( Method = c("R標準(correct=FALSE)", "R標準(correct=TRUE)", "Exact", "Mid-p"), Type1_Error_Percent = round(type1_rates * 100, 2), NA_Rate = round(na_rates * 100, 1), Expected = rep(5.0, 4) ) print(result_df) return(list(type1_rates = type1_rates, na_rates = na_rates)) } # 6. 検出力シミュレーション power_simulation <- function(n_pairs, p10, p01, p11, n_sim = 5000, alpha = 0.05) { effect_size <- p10 - p01 cat(sprintf("検出力シミュレーション (n=%d, p10=%.2f, p01=%.2f, 効果量=%.2f)\n", n_pairs, p10, p01, effect_size)) results <- run_single_simulation(n_pairs, p10, p01, p11, n_sim) # NAを除いて計算 power_rates <- sapply(results, function(x) { valid_values <- x[!is.na(x)] if(length(valid_values) == 0) return(NA) mean(valid_values < alpha) }) result_df <- data.frame( Method = c("R標準(correct=FALSE)", "R標準(correct=TRUE)", "Exact", "Mid-p"), Power_Percent = round(power_rates * 100, 1), Rank = rank(-power_rates, na.last = "keep") ) print(result_df) return(power_rates) } # 7. メインシミュレーション実行 cat("マクネマー検定 各方法の性能比較シミュレーション\n") cat(paste(rep("=", 60), collapse = ""), "\n\n") # パート1: Type I Error シミュレーション cat("【パート1: Type I Error シミュレーション】\n") cat(paste(rep("-", 40), collapse = ""), "\n") # 中確率パターン cat("\n◆ 中確率パターン (p10=p01=0.15, p11=0.30)\n") cat("小サンプル (n=30):\n") type1_mid_small <- type1_error_simulation(30, 0.15, 0.30, 5000) cat("\n大サンプル (n=200):\n") type1_mid_large <- type1_error_simulation(200, 0.15, 0.30, 5000) # パート2: 検出力シミュレーション cat("\n\n", paste(rep("=", 60), collapse = ""), "\n") cat("【パート2: 検出力シミュレーション】\n") cat(paste(rep("-", 40), collapse = ""), "\n") # 中効果 cat("\n◆ 中効果 (p10=0.20, p01=0.10, 効果量=0.10)\n") power_medium <- power_simulation(100, 0.20, 0.10, 0.30, 5000) # 大効果 cat("\n◆ 大効果 (p10=0.25, p01=0.10, 効果量=0.15)\n") power_large <- power_simulation(100, 0.25, 0.10, 0.30, 5000) cat("\nシミュレーション完了!\n")
マクネマー検定 各方法の性能比較シミュレーション ============================================================ 【パート1: Type I Error シミュレーション】 ---------------------------------------- ◆ 中確率パターン (p10=p01=0.15, p11=0.30) 小サンプル (n=30): Type I Error シミュレーション (n=30, p10=p01=0.15, p11=0.30) Method Type1_Error_Percent NA_Rate Expected r_standard R標準(correct=FALSE) 4.18 0 5 r_corrected R標準(correct=TRUE) 1.96 0 5 exact Exact 1.96 0 5 midp Mid-p 3.88 0 5 大サンプル (n=200): Type I Error シミュレーション (n=200, p10=p01=0.15, p11=0.30) Method Type1_Error_Percent NA_Rate Expected r_standard R標準(correct=FALSE) 5.40 0 5 r_corrected R標準(correct=TRUE) 4.18 0 5 exact Exact 4.22 0 5 midp Mid-p 5.36 0 5 ============================================================ 【パート2: 検出力シミュレーション】 ---------------------------------------- ◆ 中効果 (p10=0.20, p01=0.10, 効果量=0.10) 検出力シミュレーション (n=100, p10=0.20, p01=0.10, 効果量=0.10) Method Power_Percent Rank r_standard R標準(correct=FALSE) 45.5 1 r_corrected R標準(correct=TRUE) 37.5 4 exact Exact 37.6 3 midp Mid-p 43.0 2 ◆ 大効果 (p10=0.25, p01=0.10, 効果量=0.15) 検出力シミュレーション (n=100, p10=0.25, p01=0.10, 効果量=0.15) Method Power_Percent Rank r_standard R標準(correct=FALSE) 73.6 1 r_corrected R標準(correct=TRUE) 66.6 4 exact Exact 66.8 3 midp Mid-p 72.3 2 シミュレーション完了! -------------------------------------------------------------- Analysis is conducted using R version 4.5.1 (2025-06-13) The script uses the following packages and versions: compiler 4.5.1
条件 | R標準(FALSE) | R標準(TRUE) | Exact | Mid-p |
---|---|---|---|---|
小サンプル(n=30) | 4.18% | 1.96% | 1.96% | 3.88% |
大サンプル(n=200) | 5.40% | 4.18% | 4.22% | 5.36% |
Mid-p testが名目水準(5%)に最も近いことが確認できます。
効果量 | R標準(FALSE) | R標準(TRUE) | Exact | Mid-p |
---|---|---|---|---|
中(0.10) | 45.5% | 37.5% | 37.6% | 43.0% |
大(0.15) | 73.6% | 66.6% | 66.8% | 72.3% |
一貫したパターン: R標準(correct=FALSE) > Mid-p > Exact ≈ R標準(correct=TRUE)
手法 | Type I Error制御 | 検出力 | 総合評価 |
---|---|---|---|
Mid-p test | ◎最適 | ◎高 | 🥇推奨 |
R標準(FALSE) | ○良好 | ◎最高 | 🥈実用的 |
R標準(TRUE) | △保守的 | △低 | ❌推奨しない |
Exact test | △保守的 | △低 | ❌推奨しない |
Fagerland, M. W., Lydersen, S., & Laake, P. (2013). The McNemar test for binary matched-pairs data: mid-p and asymptotic are better than exact conditional. BMC Medical Research Methodology, 13, 91.
McNemar, Q. (1947). Note on the sampling error of the difference between correlated proportions or percentages. Psychometrika, 12(2), 153-157.
Fagerland, M. W., Lydersen, S., & Laake, P. (2014). Recommended tests and confidence intervals for paired binomial proportions. Statistics in Medicine, 33(16), 2850-2875.
Wikipedia - McNemar's test
R Documentation - mcnemar.test
exact2x2 Package
注意: 本レポートのコードは全て再現可能です。Rの基本パッケージのみを使用しているため、追加のパッケージインストールは不要です。
最終更新: 2025年8月
推奨引用: このレポートを引用する場合は、主要参考文献のFagerland et al. (2013)を併せて引用してください。