library(tidyverse)
library(readxl)
library(vcdExtra)
library(gt)
本レポートは、集計表(table form。属性は、table、detaframe、tibbleなど)を、個票(case form)に変換する手順を明らかにする。
その(1)で、参考にするのはFrendly先生たちの以下のもので、書籍としては、Frendly,Meyer2016(“Discrete Data Analysis with r: Visualization and Modeling Techniques f,” n.d.)。 https://cran.ism.ac.jp/web/packages/vcdExtra/vignettes/vcd-tutorial.pdfである。
この変換表によれば、表形式のものをvcdExtra::expand.dft を用いることで、case formに変換できるとあるが、手元にある表形式のオブジェクトが、table属性であるとは限らないため、もう少し汎用の手法を考える。 個票に展開する一つ前は、度数表(Freqency Form)をexpand.dftで展開する部分であるので、いかなる属性の表であれ、一度Freq Formに変換し、それをexpand.dft個票に展開する、というアプローチをとることにする。 このFreq Formは、table属性のファイルをas.data.frame によって度数集計するだけでなく、tible::gather、もしくは、tible::pivot_longerによっても得られるので、read_xlを用いて外部のExcelファイルを読み込み、それを連続的に処理することが可能であることを示す。
続編のその(2)では、リモート集計で多重クロス表として集計したデータから個票を復元する手順を確認する。
目標は、SSJDAのリモート集計などで手にいれた集計表(クロス表)から、個票(case Form)を生成すること。
ステップとしては、集計表をFreq表(度数表)に変換。それを、vcdExtra::expand.dft()で、各行を、Freqの数だけ複製する。
2変数のクロス表までであれば、上記解説にしたがって、クロス表(table属性)からas.data.frame()でFreq表に変換し、それをexpand.dft すればよい。
問題は、それ以上の多重クロス表として集計したものから、個票(case form)を生成できるか、ということ。
上に整理したように、Freq表に整理できれば、それをexpand.dft すればよいので、課題は、Freq表をどのようにつくればよいのか、ということになる。(その2)で説明する。
入力は、read_excelで取り込まれたSSJDAによって集計されたExcelファイル。
この形式では、行に指定した変数の回答カテゴリにNAが並ぶ。まず、このNA、tidyr::fill(変数名)でNAの前の値でfillしておく。Excelの段階で、手作業で処理してもよいが、Excel段階はいじらない原則。
これをどのようにFreq表にするか。これには、gather、または、pivot_longer を使えばよい。その意味では、2元クロスの表に対しても、これで対応できるので、as.data.frame()によるFreq表の作成ではなく、gather/pivot_longer を使う方法に一般化すればよい。
完成!
個票(case form)に変換するクロス集計表の例
用いるサンプルデータ:Excelでの集計表
このデータを用いて、以下の手順で個票に変換する。
クロス集計表をデータとして作成する
度数表を、tidyverse のpivot_longer で作成する
変換処理のサンプルに用いているデータは、SSM2005/SSJDAリモート集計の集計機能で作成した。設問は、「性別役割意識」であり、調査票では、問16ア「男性は外で働き、女性は家庭をまもるべきである」に対する態度を聞いている。回答選択肢は、そう思う、どちらかといえばそう思う、どちらかといえばそう思わない、そう思わない、であり、わからない、はDKNAとしてコーディングされている。
.text <- "年齢 そう思う どちらかといえばそう思う どちらかといえばそう思わない そう思わない DKNA"
stringr::str_split(.text,pattern = "\t") %>% unlist() ->cnames
.text2 <- "20代,30代,40代,50代,60代"
stringr::str_split(.text2,pattern = ",") %>% unlist() ->rnames
.dtext <- "20,63,77,151,14,28,131,126,204,27,27,147,158,212,18,50,166,165,279,24,121,220,151,211,37"
stringr::str_split(.dtext,pattern = ",") %>% unlist() ->.ddd
matrix(as.numeric(.ddd),5,5,byrow=TRUE) -> .d
colnames(.d) <- cnames[-1]
rownames(.d) <- rnames
#.d %>% gt()
.d %>% knitr::kable()
| そう思う | どちらかといえばそう思う | どちらかといえばそう思わない | そう思わない | DKNA | |
|---|---|---|---|---|---|
| 20代 | 20 | 63 | 77 | 151 | 14 |
| 30代 | 28 | 131 | 126 | 204 | 27 |
| 40代 | 27 | 147 | 158 | 212 | 18 |
| 50代 | 50 | 166 | 165 | 279 | 24 |
| 60代 | 121 | 220 | 151 | 211 | 37 |
この.dの属性は以下の通りである。
.d %>% class()
## [1] "matrix" "array"
.d %>% str()
## num [1:5, 1:5] 20 28 27 50 121 63 131 147 166 220 ...
## - attr(*, "dimnames")=List of 2
## ..$ : chr [1:5] "20代" "30代" "40代" "50代" ...
## ..$ : chr [1:5] "そう思う" "どちらかといえばそう思う" "どちらかといえばそう思わない" "そう思わない" ...
まず、上にあるTable 1に「Table formからcaseFormに直接変換できる」という記述があるので、確認してみる。 しかし、.d、table属性ではなく、matrix、array属性であるために、「expand.dft(.) でエラー: ‘freq’ not found in column names」となってエラーとなる。expand.dft()で個票に展開するには、表をmatrix属性ではなくtable属性にするか、次項のように、freqという列を持った度数表に変換する。
.d %>% as.table() %>% expand.dft() %>% head()
## Var1 Var2
## 1 20代 そう思う
## 2 20代 そう思う
## 3 20代 そう思う
## 4 20代 そう思う
## 5 20代 そう思う
## 6 20代 そう思う
つぎに、度数表を媒介にして展開してみよう。table formは、as.data.frame によって度数表になる1。
.d %>% as.table() %>% as.data.frame()
## Var1 Var2 Freq
## 1 20代 そう思う 20
## 2 30代 そう思う 28
## 3 40代 そう思う 27
## 4 50代 そう思う 50
## 5 60代 そう思う 121
## 6 20代 どちらかといえばそう思う 63
## 7 30代 どちらかといえばそう思う 131
## 8 40代 どちらかといえばそう思う 147
## 9 50代 どちらかといえばそう思う 166
## 10 60代 どちらかといえばそう思う 220
## 11 20代 どちらかといえばそう思わない 77
## 12 30代 どちらかといえばそう思わない 126
## 13 40代 どちらかといえばそう思わない 158
## 14 50代 どちらかといえばそう思わない 165
## 15 60代 どちらかといえばそう思わない 151
## 16 20代 そう思わない 151
## 17 30代 そう思わない 204
## 18 40代 そう思わない 212
## 19 50代 そう思わない 279
## 20 60代 そう思わない 211
## 21 20代 DKNA 14
## 22 30代 DKNA 27
## 23 40代 DKNA 18
## 24 50代 DKNA 24
## 25 60代 DKNA 37
今展開された度数表(Freq form)をexpand.dft()で個票に変換する。以下では、先頭の6行と最後の6行を表示させている。
.d %>% as.table() %>% as.data.frame() %>% expand.dft() -> case.form.data
case.form.data %>% head()
## Var1 Var2
## 1 20代 そう思う
## 2 20代 そう思う
## 3 20代 そう思う
## 4 20代 そう思う
## 5 20代 そう思う
## 6 20代 そう思う
case.form.data %>% tail()
## Var1 Var2
## 2822 60代 DKNA
## 2823 60代 DKNA
## 2824 60代 DKNA
## 2825 60代 DKNA
## 2826 60代 DKNA
## 2827 60代 DKNA
.d %>% as.data.frame() %>% rownames_to_column("Age10") -> .d.df
.d.df %>% pivot_longer(cols = -Age10, names_to = "Q16", values_to = "Freq") %>% expand.dft() -> .case
cnames[2:6]
## [1] "そう思う" "どちらかといえばそう思う"
## [3] "どちらかといえばそう思わない" "そう思わない"
## [5] "DKNA"
summary(.case)
## Age10 Q16
## Length:2827 Length:2827
## Class :character Class :character
## Mode :character Mode :character
with(.case, table(Age10,factor(Q16,levels=cnames[2:6])))
##
## Age10 そう思う どちらかといえばそう思う どちらかといえばそう思わない
## 20代 20 63 77
## 30代 28 131 126
## 40代 27 147 158
## 50代 50 166 165
## 60代 121 220 151
##
## Age10 そう思わない DKNA
## 20代 151 14
## 30代 204 27
## 40代 212 18
## 50代 279 24
## 60代 211 37
case.form.data %>% count(Var1,Var2.f=factor(Var2,levels=cnames[2:6])) %>%
pivot_wider(names_from = Var2.f,values_from = n)
## # A tibble: 5 × 6
## Var1 そう思う どちらかといえばそう思う どちらかといえばそ… そう思わない DKNA
## <chr> <int> <int> <int> <int> <int>
## 1 20代 20 63 77 151 14
## 2 30代 28 131 126 204 27
## 3 40代 27 147 158 212 18
## 4 50代 50 166 165 279 24
## 5 60代 121 220 151 211 37
こうして、.dとして用意された表形式のデータは、case.form.data として個票に展開された。ここで、この個票データをxtabsを用いて表形式に集計する。
#case.form.data %>% names
xtabs(~Var1+factor(Var2,levels=cnames[2:6]),case.form.data) %>% knitr::kable()
| そう思う | どちらかといえばそう思う | どちらかといえばそう思わない | そう思わない | DKNA | |
|---|---|---|---|---|---|
| 20代 | 20 | 63 | 77 | 151 | 14 |
| 30代 | 28 | 131 | 126 | 204 | 27 |
| 40代 | 27 | 147 | 158 | 212 | 18 |
| 50代 | 50 | 166 | 165 | 279 | 24 |
| 60代 | 121 | 220 | 151 | 211 | 37 |
上の表での列の並び順文字コード順になっており、当初の並びではない。これは、factor指定することで並び順を調整することは可能である。xx参照。
Freiendly.M, Meyer,David, 2016,“Discrete Data Analysis with R Visualization and Modeling Techniques for Categorical and Count Data,”CRC press, https://www.routledge.com/Discrete-Data-Analysis-with-R-Visualization-and-Modeling-Techniques-for/Friendly-Meyer/p/book/9781498725835
Wickham,Hadly,R for Data Science,,https://r4ds.had.co.nz/ (訳:黒川利明『Rではじめるデータサイエンス』オライリージャパン)(“Rではじめるデータサイエンス | Hadley Wickham, Garrett Grolemund, オライリー,ジャパン | Amazon,” n.d.)
度数表への変換は、tdyr::pivot_longer()、もしくはtdyr::gather() で行うことも可能である。また、count()でも可能。いずれの場合でも、集計値の列名は、Freqに変更する必要がある。↩︎