餡子付゛録゛

ソフトウェア開発ツールの便利な使い方を紹介。

アンケート調査結果によくある、まったくない、たまにある、よくある…と言った、ざっくり頻度の比率をあらわす棒グラフからの順序ロジット回帰

題名そのままなんですが、アンケート調査結果によくあるざっくり頻度(e.g. まったくない/たまにある/よくある/常にある)の比率をあらわす棒グラフからの順序ロジット回帰を行いたいと思います。

多様な選択を可能にする学びに関する調査報告書」の図表21を見てみましょう。何かの傾向はありそうですが、すぐに分かるものではないです。

性別・年代別にみた子どもの頃に「進学・進路」について性別を理由にした発言をされたかどうか

クロス表にして独立性検定をかけるのもひとつの方法ですが、独立性検定はどのような傾向があるのかは教えてくれません。異常値と言えるセルは分かるといえば分かるわけですが、回答項目に順序があるという情報を使ってくれません。

こういうときは順序ロジット回帰をかけてしまうと、話が分かりやすくなります。実際にやってみましょう。

頻度表の作成

図表21は比率が出ていると同時に、性別・年代ごとの合計数750も出ているので、比率を頻度表に戻すことができます。

# 比率を入力
P <- matrix(c(
6.0, 13.7, 26.8, 53.5,
7.9, 16.0, 27.5, 48.7,
9.9, 16.5, 27.6, 46.0,
10.8, 18.7, 30.5, 40.0,
9.2, 18.0, 29.3, 43.5,
10.0, 19.9, 24.4, 45.7,
9.2, 16.7, 29.2, 44.9,
11.7, 15.6, 28.3, 44.4
), 8, 4, byrow = TRUE)/100
# 使いまわしを考えて各行(i.e. 性別×世代)ごとにサンプルサイズを与える
nobs <- c(750, 750, 750, 750, 750, 750, 750, 750)
(N <- round(rep(nobs, ncol(P))*P))
     [,1] [,2] [,3] [,4]
[1,]   45  103  201  401
[2,]   59  120  206  365
[3,]   74  124  207  345
[4,]   81  140  229  300
[5,]   69  135  220  326
[6,]   75  149  183  343
[7,]   69  125  219  337
[8,]   88  117  212  333

頻度表からデータフレームの作成

尤度関数を自分で書く場合は頻度表をそのまま計算した方が速いのですが、表示のことを考えて集計前のデータフレームを再構成します。

ans <- integer(sum(N))
sex <- integer(length(ans))
gen <- integer(length(ans))
p <- 1
for(i in 1:nrow(N)){
   for(j in 1:ncol(N)){
      k <- j + (i - 1)*ncol(N)
      n <- N[i, j]
      q <- p + n - 1
      ans[p:q] <- j
      # 4行目までが女性、5行目からは男性の頻度
      sex[p:q] <- ifelse(i<=4, 1, 2)
      gen[p:q] <- i - 4*(4<i)
      p <- q + 1
   }
}
# ansは順序型,sexとgenは因子型にする
# ansは順序を1>2>3>4と値と逆順にしたあとラベリング
df01 <- data.frame(
   ans = ordered(ans, levels = 4:1, labels = c("never", "rarely", "sometimes", "frequently")), 
   sex = factor(sex, labels = c("female", "male")), 
   gen = factor(gen, labels = c("20s", "30s", "40s", "50s")))

順序型と因子型を使ったので、summaryがそれっぽくなっています。

summary(df01)
         ans           sex        gen      
 never     :2750   female:3000   20s:1500  
 rarely    :1677   male  :3000   30s:1500  
 sometimes :1013                 40s:1500  
 frequently: 560                 50s:1500  

計算間違いがないか、データフレームを集計して頻度表とつき合わせておきましょう。

xtabs(~ gen + ans, df01, subset = sex == "female")
     ans
gen   never rarely sometimes frequently
  20s   401    201       103         45
  30s   365    206       120         59
  40s   345    207       124         74
  50s   300    229       140         81
xtabs(~ gen + ans, df01, subset = sex == "male")
     ans
gen   never rarely sometimes frequently
  20s   326    220       135         69
  30s   343    183       149         75
  40s   337    219       125         69
  50s   333    212       117         88

行列Nと比較して差異はないようです。

順序ロジット回帰

今回はMASS::polrで推定します。

library(MASS)
summary(r_polr <- polr(ans ~ sex*gen, df01, Hess = TRUE))
Call:
polr(formula = ans ~ sex * gen, data = df01, Hess = TRUE)

Coefficients:
                 Value Std. Error t value
sexmale         0.4013    0.09636   4.165
gen30s          0.2070    0.09719   2.130
gen40s          0.3320    0.09697   3.424
gen50s          0.5341    0.09601   5.563
sexmale:gen30s -0.2136    0.13621  -1.568
sexmale:gen40s -0.3887    0.13564  -2.866
sexmale:gen50s -0.5312    0.13517  -3.930

Intercepts:
                     Value   Std. Error t value
never|rarely          0.1591  0.0697     2.2826
rarely|sometimes      1.3668  0.0719    18.9988
sometimes|frequently  2.6093  0.0798    32.7184

Residual Deviance: 14787.40 
AIC: 14807.40 

できました。

  • 男性ダミー(sexmale)の係数が正で大きいので、男性の方が性別を理由に進路について何か言われがちな
  • 年代ダミー(gen*0s)が係数が正で、高い年齢ほど性別を理由に進路について何か言われたことが分かり、また男性ダミーと年代ダミーの交差項(sexmale:gen*0s)がそれを相殺するような大きさなので、高齢女性は男性と同じぐらい性別を理由に進路について何か言われがちだったが、時代とともに言われなくなり、今の若い女性は男性と比較してそういうことを言われない

ことが分かります。

ワルド検定

検定をしたら、男性も世代によって女性ほどではないが変化があるかも知れないと心配な人は、Wald検定でもかけましょう。

# 推定した係数(polrオブジェクトの場合、切片項はcoefでとれない)
b <- c(coef(r_polr), r_polr$zeta)
# 帰無仮説: gen50sの係数 + sexamle:gen50sの係数 = 0 を検定する
c_r <- grep("gen50s", names(b)) # gen50sが含まれる変数の列の位置
C <- rep(0, length(b)) # gen50sが含まれない変数の位置は0
C[c_r] <- 1 # gen50sが含まれる変数の位置は1
# 標準は縦ベクトルなので、転置して横ベクトルにする
C <- t(C)
R <- 0
# ワルド検定量
(ws <- t(C %*% b - R) %*% solve(C %*% vcov(r_polr) %*% t(C)) %*% (C %*% b - R))
             [,1]
[1,] 0.0009253855

検定統計量が小さいです。

# χ二乗検定
pchisq(ws, length(c_r) - 1, lower.tail = FALSE)
         [,1]
[1,] 0.975732

p値も0.1を遥かに超えます。30代、40代でやっても同様の結果です。なお、有意になっても効果量が女性の場合よりも圧倒的に小さいので、話は変わらないと思います。

平行性の検定

上で推定した順序ロジットは、複数の閾値を推定するモデルの係数が、すべて同一である平行性が仮定されています。しかし、これは必ずしもそうとは限りません。平行性の仮定が疑わしいときはbrant検定で確認します。

library(brant)
brant(r_polr)
-------------------------------------------- 
Test for	X2	df	probability 
-------------------------------------------- 
Omnibus		16.2	14	0.3
sexmale		0.09	2	0.95
gen30s		0.3	2	0.86
gen40s		1.54	2	0.46
gen50s		0.44	2	0.8
sexmale:gen30s	1.37	2	0.5
sexmale:gen40s	0.53	2	0.77
sexmale:gen50s	0.69	2	0.71
--------------------------------------------

今回は平行性の仮定が棄却されることはありませんでしたが、棄却された場合は一般化順序ロジットモデルの適用を検討すべしとなります。

まとめと注意

この図表の掲載してあったレポートでは、(図表をよくみたら掴めるとは思いますが)女性の方の年代による変化が指摘していなかったので、それをこの分析の成果としましょう。

なお、全体としてどういう分布になっているかを掴むのには図表の方がよく、今回の推定は空目防止ぐらいの効果しかないので、過信は禁物です。