超初心者向けのRガイド

e. 多項選択回答の表の作成

Author

SUGINO Isamu, Build with R4.4.1

Published

January 12, 2025

1 全体の構成

2 多項選択回答(Multiple Answer)の処理

「以下の中から,あてはまるものを全てお選び下さい。」と云った質問―回答の方法を,多項選択方式と言う(複数回答と呼ぶ事も多い)。
例えば,内閣府が毎年行っている「国民生活に関する世論調査」(平成27年度)の以下の質問がそうである。

Q9 あなたは,今後の生活において,特にどのような面に力を入れたいと思いますか。
この中からいくつでもあげてください。(M.A.)

(ア) 食生活
(イ) 衣生活
(ウ) 自動車,電気製品,家具などの耐久消費財
(エ) 住生活
(オ) レジャー・余暇生活
(カ) 自己啓発・能力向上
(キ) 所得・収入
(ク) 資産・貯蓄
その他
ない
わからない

 この質問は1つの質問の様に見えているが,正確には「食生活に力を入れたいと思いますか」「衣生活に力を入れたいと思いますか」などの様に各選択肢項目が一つの二択質問になっていると考えるべきである。
データ・ファイル中の変数にする場合には各質問項目がそれぞれ0(選択されなかった)か1(選択された)の値を取る二値変数として記録される。
(近年では,多項選択形式で質問をすると”satisficing”――全ての質問や選択肢項目にきちんと回答せずに,適当なところで「これくらい答えておけばいいだろう」と回答を打ち切る回答行動――が生じて正確な情報が得られないとの批判もある。)
 以下では,この様な多項選択形式の質問の回答データの処理・作表についてのスクリプトを紹介する。

2.1 模擬データの作成

 まずは調査データの無い人でも試してみられる様に,多項選択回答データの模擬データを生成しよう(ここは,練習用のデータを持たない人の為に架空の模擬データを生成する部分であるので,実際に集計用データを持っている人はここはskipして下さい)。
 以下では,0と1をそれぞれ異なる確率で生成する4つの二値変数を発生させている。
多項選択の場合は,無回答(NA)の場合には全ての選択項目に○がついていない場合に含まれるので,ここでは個別にNAを発生させる事はしない。(多項選択でいずれかの項目だけNAになる事の方がおかしい。)

二値変数の生成とデータ・フレイム化
n <- 150
q0101 <- sample(c(0, 1), size = n, replace = T, prob = c(.30, .70))
q0102 <- sample(c(0, 1), size = n, replace = T, prob = c(.60, .40))
q0103 <- sample(c(0, 1), size = n, replace = T, prob = c(.40, .60))
q0104 <- sample(c(0, 1), size = n, replace = T, prob = c(.80, .20))

d01 <- data.frame(q0101, q0102, q0103, q0104)

 各変数の度数分布表を個別に作成するのは簡単である。繰り返し適用するなら,新関数として定義する事も出来る。NAを考えなくて良いので非常に簡単になる。

freq.tableD <- function (x) {
  freq    <- table(x)
  percent <- prop.table(freq)
  output  <- cbind("度数"     = addmargins(freq),
                   "相対度数" = addmargins(percent))
  return(output)
}
freq.tableD(d01$q0101)
    度数  相対度数
0     46 0.3066667
1    104 0.6933333
Sum  150 1.0000000
freq.tableD(d01$q0102)
    度数  相対度数
0     86 0.5733333
1     64 0.4266667
Sum  150 1.0000000
freq.tableD(d01$q0103)
    度数  相対度数
0     49 0.3266667
1    101 0.6733333
Sum  150 1.0000000
freq.tableD(d01$q0104)
    度数 相対度数
0    117     0.78
1     33     0.22
Sum  150     1.00

 しかし,多項選択形式の回答の整理には,全ての項目の選択数や選択率を一覧にして示す事が多い。次にその為の簡単なスクリプトを紹介しよう。

2.2 多項選択一括集計表の作成

 データ・フレイム d01 は集計したい二値変数だけからなり,集計したい二値変数は全て d01 に含まれているとする。
上記と同じ様に,各変数には0か1の値が必ず入力されており,NAを含め他の値は存在しないものとする(NAを考えなくて良い二値変数の処理は非常に簡単になる)。
 以下では,二値変数の個数を4つとは前提せず,ケース数も150とは前提としない一般的なスクリプトを作成している。
 apply( )tapply( ) と云う関数は使える様になると非常に便利であるので,ウェブ上などで”R apply”などと検索して調べてみると良い。
 apply( ) 関数において,MARGIN は1が行方向,2が列方向を意味し,FUN はそれに適用する関数(function)を指定する。
sum は合計の関数であり,0か1の二値変数を合計すると1の個数に等しくなる事を利用している。
 apply(data, MARGIN = 2, FUN = sum) を colSums(data),apply(data, MARGIN = 1, FUN = sum) を rowSums(data) と書いても同じになる。

nYES <- apply(d01, MARGIN = 2, FUN = sum) # colSums(d01)としても同じ。
MA01 <- cbind("○の数" = nYES, 
              "×の数" = nrow(d01) - nYES,
              "総度数" = nrow(d01),
              "○の%" = nYES/nrow(d01)*100, 
              "×の%" = (nrow(d01) - nYES)/nrow(d01)*100)

rownames(MA01) <- colnames(d01)
MA01
      ○の数 ×の数 総度数   ○の%   ×の%
q0101    104     46    150 69.33333 30.66667
q0102     64     86    150 42.66667 57.33333
q0103    101     49    150 67.33333 32.66667
q0104     33    117    150 22.00000 78.00000

表示がずれるので一応整形して表示しておく。

knitr::kable(MA01)
○の数 ×の数 総度数 ○の% ×の%
q0101 104 46 150 69.33333 30.66667
q0102 64 86 150 42.66667 57.33333
q0103 101 49 150 67.33333 32.66667
q0104 33 117 150 22.00000 78.00000

結果表をcsvに保存する。現行versionでは,文字コードを指定しないとUTF-8(BOM無し)となってWindowsのMS Excelでは文字化けをするので,Excel userは”sjis”に指定すること。

write.csv(MA01, file = "multiple_answer.csv", quote = F,
          fileEncoding = "sjis")

 最後に保存したcsvファイルをMS-Excelで開いた状態を以下に示す。   csvファイル

2.3 それぞれ独立にNAが含まれる二値変数の一括集計表

 何らかの理由で,それぞれ別個にNAを含む様な二値変数を一括集計表にする方法も考えて見よう。
NAが含まれる場合は上記よりも格段に難易度が上がる。
 まずは模擬データとして二値変数を生成するところから始める1

# NAを含む二値変数の生成
n <- 127
q0201 <- sample(c(0, 1, NA), size = n, replace = T, prob = c(.70, .25, .05))
q0202 <- sample(c(0, 1, NA), size = n, replace = T, prob = c(.60, .37, .03))
q0203 <- sample(c(0, 1, NA), size = n, replace = T, prob = c(.44, .54, .02))
q0204 <- sample(c(0, 1, NA), size = n, replace = T, prob = c(.90, .08, .02))
q0205 <- sample(c(0, 1, NA), size = n, replace = T, prob = c(.30, .66, .04))
d01 <- data.frame(q0201, q0202, q0203, q0204, q0205) # 二値変数だけでデータ・フレイムにする

 一応確認の為に,それぞれの度数分布表を個別に作成してみよう。
度数分布表を作成する新関数 freq.tableDN( ) は,必要だと思われる情報に限定している。

freq.tableDN <- function (x) {
  freqT    <- table(x, useNA = "always")
  percentT <- prop.table(freqT)
  percent  <- c(prop.table(table(x)), NA)
  return(cbind("度数"         = freqT, 
               "相対度数"     = percentT,
               "有効相対度数" = percent))
}
freq.tableDN(d01$q0201)
     度数   相対度数 有効相対度数
0      85 0.66929134    0.7142857
1      34 0.26771654    0.2857143
<NA>    8 0.06299213           NA
freq.tableDN(d01$q0202)
     度数    相対度数 有効相対度数
0      80 0.629921260    0.6349206
1      46 0.362204724    0.3650794
<NA>    1 0.007874016           NA
freq.tableDN(d01$q0203)
     度数   相対度数 有効相対度数
0      52 0.40944882        0.416
1      73 0.57480315        0.584
<NA>    2 0.01574803           NA
freq.tableDN(d01$q0204)
     度数   相対度数 有効相対度数
0     112 0.88188976   0.90322581
1      12 0.09448819   0.09677419
<NA>    3 0.02362205           NA
freq.tableDN(d01$q0205)
     度数   相対度数 有効相対度数
0      38 0.29921260    0.3220339
1      80 0.62992126    0.6779661
<NA>    9 0.07086614           NA

さて,いよいよ一括集計表を作成しよう。
下のスクリプト中, colSums(d01, na.rm=T) は apply(d01, MARGIN=2, FUN=sum, na.rm=T) とするのと同じである。
また,apply(d01, 2, function(x){sum(is.na(x))}) は,FUNの部分で新関数を定義しており,その関数は,「xがNAに等しい」の真偽値(TRUE=1, FALSE=0)の合計 となっている。

NAを含む二値変数の一括集計表
denom <- nrow(d01) # ケース数(データ・フレイムの行数に等しい)
nYES  <- colSums(d01, na.rm = T) # naをremoveして1を数える
nNA   <- apply(d01, 2, function(x){sum(is.na(x))}) # 列方向に,NAであるものの個数を数える
nNO   <- denom - (nYES + nNA) # ケース数からYESの数とNAの数を引く

MA.NA01 <- cbind("○の数" = nYES, "×の数" = nNO, 
                 "NAの数" = nNA, "総度数" = denom,
                 "○の%" = nYES/denom*100, 
                 "×の%" = nNO/denom*100, 
                 "NAの%" = nNA/denom*100,
                 "有効度数" = nYES+nNO, 
                 "○の有効%" = nYES/(nYES+nNO)*100)

rownames(MA.NA01) <- colnames(d01) # 表側に変数名を代入
MA.NA01
      ○の数 ×の数 NAの数 総度数    ○の%   ×の%    NAの% 有効度数
q0201     34     85      8    127 26.771654 66.92913 6.2992126      119
q0202     46     80      1    127 36.220472 62.99213 0.7874016      126
q0203     73     52      2    127 57.480315 40.94488 1.5748031      125
q0204     12    112      3    127  9.448819 88.18898 2.3622047      124
q0205     80     38      9    127 62.992126 29.92126 7.0866142      118
      ○の有効%
q0201  28.571429
q0202  36.507937
q0203  58.400000
q0204   9.677419
q0205  67.796610

以下は,knitrと云うパッケイジをRMarkdownもしくはQuartoで用いて,成形し小数点以下は一桁とした表。

knitr::kable(MA.NA01, digits = 1)
○の数 ×の数 NAの数 総度数 ○の% ×の% NAの% 有効度数 ○の有効%
q0201 34 85 8 127 26.8 66.9 6.3 119 28.6
q0202 46 80 1 127 36.2 63.0 0.8 126 36.5
q0203 73 52 2 127 57.5 40.9 1.6 125 58.4
q0204 12 112 3 127 9.4 88.2 2.4 124 9.7
q0205 80 38 9 127 63.0 29.9 7.1 118 67.8

上の各変数の度数分布表と見比べて対応している事を確認すると良い。

最後にcsvファイル保存

ここでもWindowsのMS Excelで使う場合は文字コードに注意。

write.csv(MA.NA01, file = "multiple_answer2.csv", quote = F,
          fileEncoding = "sjis")

保存したcsvファイルをMS-Excelで開くと以下の通りである。

(csvファイル)

3 多項選択のパタンと反応数NEW

3.1 多項選択のパタン変数の作成

多項選択質問の場合,各回答者がどの様なパタンで回答したのかを分類したい場合がしばしばある。
ここではまず単純且つ分かり易くパタン変数を作成する方法を示す。
全てを同じ桁数の値とする為の工夫として,一番上の位に9を置いている2

d01$pattern_q02 <- d01$q0201*1*10^4 + 
                   d01$q0202*2*10^3 + 
                   d01$q0203*3*10^2 + 
                   d01$q0204*4*10^1 +
                   d01$q0205*5 +
                   9*10^5             # 先頭の桁の9

結果の確認

d01[, c("q0201", "q0202", "q0203", "q0204", "q0205", "pattern_q02")]
with(d01, addmargins(table(pattern_q02, useNA = "always")))
pattern_q02
 9e+05 900005 900045 900300 900305 900340 900345 902000 902005 902040 902300 
     4     14      1     11     17      1      2      5      6      1      4 
902305 902345 910000 910005 910045 910300 910305 910340 912000 912005 912045 
     8      1      3      5      1      1      5      1      3      1      1 
912300 912305 912340 912345   <NA>    Sum 
     2      5      1      1     22    127 

上の注で「別の方法」と述べたのは以下の方法である。
一番上の桁に9を置かずに,文字列変数とする事で桁数を揃える。
数値型で桁数が多いと指数表記になってしまうが,文字型変数ではそれも防げる。
数値型変数と文字型変数ではその後の使い勝手が異なるので,いずれか便利な方を使うのが良い。

d01$pattern_q02.b <- d01$q0201*1*10^4 + 
                     d01$q0202*2*10^3 + 
                     d01$q0203*3*10^2 + 
                     d01$q0204*4*10^1 +
                     d01$q0205*5

# 書式設定を指定した文字列変数に変換
d01$Pattern_q02.b <- sprintf("%05d", d01$pattern_q02.b)

d01[, c("q0201", "q0202", "q0203", "q0204", "q0205", "pattern_q02", "Pattern_q02.b")]

尚,ぱっと見た場合に読み取り易い様に各桁に1, 2, 3, 4, 5, ..と付与したが,多項選択の項目数が10以上ある場合にはこの方法はとれない。
その場合はパタン変数を複数に分けて作成するか,もしくは各桁の1, 2, 3, 4, …を付けずに,全ての桁を0, 1で表現するしかないだろう。

# 各桁に項目番号の1, 2, 3, 4, ...を付与しない場合の書き方
d01$pattern_q02.c <- d01$q0201*10^4 + 
                     d01$q0202*10^3 + 
                     d01$q0203*10^2 + 
                     d01$q0204*10^1 +
                     d01$q0205      +
                     9*10^5             # 先頭の桁の9

# それぞれの方法の結果の比較
d01[, c("q0201", "q0202", "q0203", "q0204", "q0205", "pattern_q02", "pattern_q02.c", "Pattern_q02.b")]

3.2 多項選択の選択数の変数化

d01$n_q02 <- rowSums(d01[, c("q0201", "q0202", "q0203", "q0204", "q0205")])

結果の確認

d01[, c("q0201", "q0202", "q0203", "q0204", "q0205", "n_q02", "pattern_q02")]
with(d01, addmargins(table(pattern_q02, n_q02, useNA = "always")))
           n_q02
pattern_q02   0   1   2   3   4   5 <NA> Sum
     9e+05    4   0   0   0   0   0    0   4
     900005   0  14   0   0   0   0    0  14
     900045   0   0   1   0   0   0    0   1
     900300   0  11   0   0   0   0    0  11
     900305   0   0  17   0   0   0    0  17
     900340   0   0   1   0   0   0    0   1
     900345   0   0   0   2   0   0    0   2
     902000   0   5   0   0   0   0    0   5
     902005   0   0   6   0   0   0    0   6
     902040   0   0   1   0   0   0    0   1
     902300   0   0   4   0   0   0    0   4
     902305   0   0   0   8   0   0    0   8
     902345   0   0   0   0   1   0    0   1
     910000   0   3   0   0   0   0    0   3
     910005   0   0   5   0   0   0    0   5
     910045   0   0   0   1   0   0    0   1
     910300   0   0   1   0   0   0    0   1
     910305   0   0   0   5   0   0    0   5
     910340   0   0   0   1   0   0    0   1
     912000   0   0   3   0   0   0    0   3
     912005   0   0   0   1   0   0    0   1
     912045   0   0   0   0   1   0    0   1
     912300   0   0   0   2   0   0    0   2
     912305   0   0   0   0   5   0    0   5
     912340   0   0   0   0   1   0    0   1
     912345   0   0   0   0   0   1    0   1
     <NA>     0   0   0   0   0   0   22  22
     Sum      4  33  39  20   8   1   22 127

3.3 多項選択パタンのグループ化

後は必要に応じて,多項選択パタンをより少ないカテゴリに纏め上げるなどすればよい。
纏め上げ方は具体的な例によって千差万別だと思われるので,ここではやり方の一例だけ示す。
この例では5項目なので,論理的には2^5=32通りだけパタンがある。
以下はスクリプト例の為だけで,グループ化には何も意味はない。

d01$category_q02 <- 0   # 初期値
d01$category_q02[is.na(d01$pattern_q02)] <- NA   # 元変数がNAならNAに。
d01$category_q02[is.element(d01$pattern_q02, 
                            c(900000, 910000, 902000, 900300, 900040, 900005))] <- 1
d01$category_q02[is.element(d01$pattern_q02, 
                            c(912000, 910300, 910040, 910005, 900040, 900005))] <- 2
d01$category_q02[is.element(d01$pattern_q02, 
                            c(912300, 912040, 912005, 912340, 912305, 912345))] <- 3
d01$category_q02[is.element(d01$pattern_q02, 
                            c(902300, 902040, 902005, 902340, 902305, 902345))] <- 4
d01$category_q02[is.element(d01$pattern_q02, 
                            c(900340, 900305, 900345, 900045))] <- 5
with(d01, addmargins(table(pattern_q02, category_q02, useNA = "always")))
           category_q02
pattern_q02   0   1   2   3   4   5 <NA> Sum
     9e+05    0   4   0   0   0   0    0   4
     900005   0   0  14   0   0   0    0  14
     900045   0   0   0   0   0   1    0   1
     900300   0  11   0   0   0   0    0  11
     900305   0   0   0   0   0  17    0  17
     900340   0   0   0   0   0   1    0   1
     900345   0   0   0   0   0   2    0   2
     902000   0   5   0   0   0   0    0   5
     902005   0   0   0   0   6   0    0   6
     902040   0   0   0   0   1   0    0   1
     902300   0   0   0   0   4   0    0   4
     902305   0   0   0   0   8   0    0   8
     902345   0   0   0   0   1   0    0   1
     910000   0   3   0   0   0   0    0   3
     910005   0   0   5   0   0   0    0   5
     910045   1   0   0   0   0   0    0   1
     910300   0   0   1   0   0   0    0   1
     910305   5   0   0   0   0   0    0   5
     910340   1   0   0   0   0   0    0   1
     912000   0   0   3   0   0   0    0   3
     912005   0   0   0   1   0   0    0   1
     912045   1   0   0   0   0   0    0   1
     912300   0   0   0   2   0   0    0   2
     912305   0   0   0   5   0   0    0   5
     912340   0   0   0   1   0   0    0   1
     912345   0   0   0   1   0   0    0   1
     <NA>     0   0   0   0   0   0   22  22
     Sum      8  23  23  10  20  21   22 127

Footnotes

  1. ここも上と同様,集計の為のデータを持っている人は飛ばして進む。↩︎

  2. これとは別の方法として,多項選択の項目数と同じ桁で文字型変数として作成する方法も以下で紹介する。↩︎