クロス集計・χ2検定・順位相関係数

公開

2026年6月23日

更新日

2026年7月2日

カテゴリー変数の二変量統計について学びます。

パッケージの読み込み

library(tidyverse)

クロス集計

データの準備

学生 200 人を対象とした調査(性別・スマートフォン OS・満足度)を想定する。

set.seed(42)
n <- 200

df <- tibble(
  gender = sample(c("男", "女"), n, replace = TRUE),
  os = sample(
    c("iPhone", "Android"), n,
    replace = TRUE, prob = c(0.55, 0.45)
  ),
  satisfaction = ordered(
    sample(1:5, n, replace = TRUE),
    labels = c("非常に不満", "不満", "普通", "満足", "非常に満足")
  )
)

head(df)
# A tibble: 6 × 3
  gender os      satisfaction
  <chr>  <chr>   <ord>       
1 男     Android 満足        
2 男     iPhone  非常に満足  
3 男     Android 満足        
4 男     iPhone  非常に満足  
5 女     iPhone  満足        
6 女     iPhone  普通        

satisfactionordered() で「順序型」データにしておくと,後の順位相関でそのまま使える。

xtabs() によるクロス集計

tbl <- xtabs(~ gender + os, data = df)
tbl
      os
gender Android iPhone
    女      37     74
    男      37     52

xtabs() は「式(あるいはモデル)」(~ 行変数 + 列変数)を引数にとるので、データフレームと相性が良い。

addmargins() で周辺度数を追加

addmargins(tbl)
      os
gender Android iPhone Sum
   女       37     74 111
   男       37     52  89
   Sum      74    126 200

proportions() で割合表に変換

margin 引数で「どの方向を 100% にするか」を指定する。

# 行方向:性別ごとの OS 比率
proportions(tbl, margin = 1) |> round(3)
      os
gender Android iPhone
    女   0.333  0.667
    男   0.416  0.584
# 列方向:OS ごとの性別比率
proportions(tbl, margin = 2) |> round(3)
      os
gender Android iPhone
    女   0.500  0.587
    男   0.500  0.413
# 総計基準:全体構成比
proportions(tbl) |> round(3)
      os
gender Android iPhone
    女   0.185  0.370
    男   0.185  0.260
margin 基準 問い
1 行合計 = 1 性別ごとに,OS の選択比率はどう違うか?
2 列合計 = 1 OS ごとに,性別の構成はどう違うか?
省略 総計 = 1 全体に占める各セルの割合は?

周辺度数付きで確認するには,割合変換後に再度 addmargins() をかける。

proportions(tbl) |> round(3) |> addmargins()
      os
gender Android iPhone   Sum
   女    0.185  0.370 0.555
   男    0.185  0.260 0.445
   Sum   0.370  0.630 1.000

χ2検定(独立性の検定)

2 変数が互いに独立か否かを検定する。

  • H0(帰無仮説):性別と OS の選択は独立である
  • H1(対立仮説):性別と OS の選択に関連がある(α = 0.05)

検定の実施

result_chi <- chisq.test(tbl)
result_chi

    Pearson's Chi-squared test with Yates' continuity correction

data:  tbl
X-squared = 1.1069, df = 1, p-value = 0.2928

結果は、χ2値が1.1069、p値が0.2928となった。 0.2928 > 0.05なので、H0を棄却できず、有意な関連があるとはいえない。

補足:Fisher の正確検定

期待度数が小さなのセルがあるときには「カイ自乗近似は不正確かもしれません」というメッセージが出ることがある。 そのような場合には、fisher.test() を使うとよい。

small_tbl <- matrix(
  c(8, 2, 1, 9), nrow = 2,
  dimnames = list(c("男", "女"), c("iPhone", "Android"))
)
fisher.test(small_tbl)

    Fisher's Exact Test for Count Data

data:  small_tbl
p-value = 0.005477
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
    2.057999 1740.081669
sample estimates:
odds ratio 
  27.32632 

順位相関係数

ピアソンの積率相関係数とスピアマンの順位相関係数

アンケートの満足度評価(1〜5 点)のような順序尺度データ, あるいは外れ値が大きく正規性が期待できない場合は, ピアソンの積率相関係数(いわゆる相関係数)ではなくスピアマンの順位相関係数 \(\rho\) を使う。

\[ \rho = 1 - \frac{6 \sum d_i^2}{n(n^2 - 1)} \]

\(d_i\):各観測におけるランクの差

データの性質 推奨される相関係数
連続変数・正規性あり・外れ値なし ピアソン(method = "pearson"
順序尺度,または非正規・外れ値あり スピアマンmethod = "spearman"
同順位が非常に多い順序データ ケンドール(method = "kendall"

データの確認

先ほど作った dfsatisfaction(順序付き因子)と os(ダミー化)の関係を見る。 ここでは数値に変換して使う。

df2 <- df |>
  mutate(
    sat_num = as.integer(satisfaction),   # 1〜5 の整数
    os_num  = as.integer(os == "iPhone")  # iPhone = 1, Android = 0
  )

head(df2[, c("gender", "os", "satisfaction", "sat_num", "os_num")])
# A tibble: 6 × 5
  gender os      satisfaction sat_num os_num
  <chr>  <chr>   <ord>          <int>  <int>
1 男     Android 満足               4      0
2 男     iPhone  非常に満足         5      1
3 男     Android 満足               4      0
4 男     iPhone  非常に満足         5      1
5 女     iPhone  満足               4      1
6 女     iPhone  普通               3      1

cor() によるスピアマン相関

cor(df2$sat_num, df2$os_num, method = "spearman")
[1] -0.00558782

cor.test() による有意性検定

result_cor <- cor.test(
  df2$sat_num, df2$os_num,
  method = "spearman",
  exact  = FALSE    # n が大きいとき(厳密計算は重い)
)
result_cor

    Spearman's rank correlation rho

data:  df2$sat_num and df2$os_num
S = 1340750, p-value = 0.9374
alternative hypothesis: true rho is not equal to 0
sample estimates:
        rho 
-0.00558782 

0.9374073 > 0.05なので、有意な相関があるとはいえない。

複数変数の相関行列

df2 |>
  select(sat_num, os_num) |>
  cor(method = "spearman") |>
  round(3)
        sat_num os_num
sat_num   1.000 -0.006
os_num   -0.006  1.000

まとめ

表 1: 使用した主な関数
関数 役割
xtabs(~ 行 + 列, data) クロス集計表の作成
addmargins(tbl) 周辺度数(行計・列計・総計)の追加
proportions(tbl, margin = 1) 行合計 = 1 の割合表
proportions(tbl, margin = 2) 列合計 = 1 の割合表
chisq.test(tbl) 独立性の χ² 検定
fisher.test(tbl) 独立性の Fisher 正確検定(小標本)
cor(…, method = ‘spearman’) スピアマン順位相関係数の計算
cor.test(…, method = ‘spearman’) スピアマン相関の有意性検定