はじめに
R でペアワイズ McNemar 検定を実装していたところ、特定のデータでだけ mcnemar.test() がエラーになる現象に遭遇しました。
原因は table() の仕様にありました。知っている人には当たり前かもしれませんが、知らないとデバッグに時間がかかるタイプの落とし穴なので共有します。
検証環境: R version 4.5.2 (2025-10-31)
問題の再現
10人の被験者に対する二値 (0/1) の対応データを考えます。
x <- c(0,0,0,0,0,0,0,0,0,0) # 全員 0
y <- c(0,0,0,1,1,1,1,1,1,1) # 0 が 3人、1 が 7人
x は全員が同じ値 (0) です。この2変数で McNemar 検定を行います。
tab <- table(x, y)
print(tab)
y
x 0 1
0 3 7
dim(tab)
# [1] 1 2
table() は観測された値だけでクロス表を作ります。x には 0 しか含まれないため、行が 1 つしかない 1x2 の表 が返ります。
mcnemar.test(tab)
# Error in mcnemar.test(tab) :
# 'x' must be square with at least two rows and columns
mcnemar.test() は正方行列を要求するので、1x2 の表を渡すとエラーになります。
原因
table() は観測されたカテゴリだけで次元を構成します。カウントがゼロの水準は、行または列ごと省略されます。
つまり「全員が同じ値」のような偏ったデータでは、期待する 2x2 の分割表にならない場合があります。
解決策: factor() で水準を明示する
factor() で水準 (levels) を明示的に指定してから table() に渡せば、ゼロカウントの行・列も保持されます。
tab2 <- table(factor(x, levels = 0:1), factor(y, levels = 0:1))
print(tab2)
0 1
0 3 7
1 0 0
dim(tab2)
# [1] 2 2
2x2 の正方行列になりました。mcnemar.test() も正常に動作します。
mcnemar.test(tab2)
McNemar's Chi-squared test with continuity correction
data: tab2
McNemar's chi-squared = 5.1429, df = 1, p-value = 0.02334
実例: Cochran Q 検定の事後ペアワイズ比較で遭遇
実際にこの問題に気づいたのは、Cochran Q 検定の事後検定として McNemar 検定のペアワイズ比較を行ったときでした。
28人の被験者が3つの文字 (中国語簡体字・タイ文字・タミル文字) を読めるかどうか (A/B の二値) を調査したデータです。
data <- matrix(c(
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,0,1,0,1,0,0,1,0,0,0,0,1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,1,
0,0,0,1,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0
), ncol = 3)
colnames(data) <- c("col_A", "col_B", "col_C")
各列の分布を確認すると、col_A は全員 0 です。
# col_A : 0=28, 1=0
# col_B : 0=12, 1=16
# col_C : 0=12, 1=16
Cochran Q 検定は問題なく実行できます。
library(DescTools) # version 0.99.50
result <- DescTools::CochranQTest(data)
print(result)
Cochran's Q test
data: y
Q = 23.273, df = 2, p-value = 8.839e-06
p < 0.05 なので、事後検定としてペアワイズ McNemar 検定を行います。
factor なし (エラーが発生)
pairs <- combn(ncol(data), 2)
for (i in 1:ncol(pairs)) {
pair <- pairs[, i]
name <- paste(colnames(data)[pair], collapse = " vs. ")
tab <- table(data[, pair[1]], data[, pair[2]])
cat(name, ": dim=", dim(tab), "→ ")
tryCatch({
p <- mcnemar.test(tab)$p.value
cat("p =", p, "\n")
}, error = function(e) {
cat("Error:", conditionMessage(e), "\n")
})
}
col_A vs. col_B : dim= 1 2 → Error: 'x' must be square with at least two rows and columns
col_A vs. col_C : dim= 1 2 → Error: 'x' must be square with at least two rows and columns
col_B vs. col_C : dim= 2 2 → p = 1
col_A を含むペアはすべてエラーになります。
factor あり (正常に動作)
for (i in 1:ncol(pairs)) {
pair <- pairs[, i]
name <- paste(colnames(data)[pair], collapse = " vs. ")
tab <- table(factor(data[, pair[1]], levels = 0:1),
factor(data[, pair[2]], levels = 0:1))
p <- mcnemar.test(tab)$p.value
cat(name, ": p =", p, "\n")
}
col_A vs. col_B : p = 0.0001768346
col_A vs. col_C : p = 0.0001768346
col_B vs. col_C : p = 1
すべてのペアで正常に計算されました。
まとめ
table() のみ |
factor() + table()
|
|
|---|---|---|
| ゼロカウントの水準 | 省略される | 保持される |
偏ったデータでの mcnemar.test()
|
エラー | 正常動作 |
table() で分割表を作るとき、特にループ内でペアワイズ比較を行う場合は、factor() で水準を明示するのを忘れないようにしましょう。mcnemar.test() に限らず、chisq.test() や fisher.test() など分割表を入力とする検定関数すべてに当てはまります。
宣伝: Reactive stat
この記事で紹介した Cochran Q 検定とペアワイズ McNemar 検定は、無料統計ウェブアプリ Reactive stat に実装されています。
- インストール不要、ブラウザだけで完結
- 詳しい解説とサンプルデータ付き
- R による信頼性の高い検定結果をそのまま論文に
- AI が結果を自動解説、すぐに理解して使いこなせる