library(tidyverse)
library(readxl)
library(vcdExtra)
library(gt)

1 概要

1.1 構成

本レポートは、集計表(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)では、リモート集計で多重クロス表として集計したデータから個票を復元する手順を確認する。

1.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 を使う方法に一般化すればよい。

  • 完成!

2 Frendly,Meyer2016の方法での確認

個票(case form)に変換するクロス集計表の例

用いるサンプルデータ:Excelでの集計表

このデータを用いて、以下の手順で個票に変換する。

  • クロス集計表をデータとして作成する

    • expand.dftで一気に個票に変換する
    • クロス表を度数表(Freq form)に変換した後、expand.dftで個票に変換する
  • 度数表を、tidyverse のpivot_longer で作成する

3 データを作成する

変換処理のサンプルに用いているデータは、SSM2005/SSJDAリモート集計の集計機能で作成した。設問は、「性別役割意識」であり、調査票では、問16ア「男性は外で働き、女性は家庭をまもるべきである」に対する態度を聞いている。回答選択肢は、そう思う、どちらかといえばそう思う、どちらかといえばそう思わない、そう思わない、であり、わからない、はDKNAとしてコーディングされている。

3.1 行名、列名ラベルを用意しておく

  • c()の中で”“をつかって作成してもよいが、以下のようにすれば手間が省ける。
.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] "そう思う" "どちらかといえばそう思う" "どちらかといえばそう思わない" "そう思わない" ...

4 .dにexpand.dft()を適用する

まず、上にある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代 そう思う

5 度数表(Freq Table)に変換したのにち個票(case form)に変換する

5.1 度数表に変換する

つぎに、度数表を媒介にして展開してみよう。table formは、as.data.frame によって度数表になる1

  • expand.dftの入力としての度数表をつくるには、入力の表にはtable属性が必要。matrix属性では、as.data.frameをつかっても、Freq formにならない。
  • expand.dft()では、度数部分の列名は、Freqがdefault。別の名前を使うのであれば、expand.dftで、freq=“xxx”と指定する。
.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

5.2 度数表(Freq form)を個票(case form)に変換

今展開された度数表(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

6 度数表(Freq Table)への変換を、pivot_longer で行う

  • 変換の元となる表が、table属性でなくても対応が可能。
  • とくに、Excelのファイルで取得したものをread_excelで読み込んでtibbleになっている場合など、こちらの方がシンプルな処理となる。

6.1 matrix をdata.rrameに変換。それをpivot_longerで度数表に。それをexpand.dftで個票に変換

.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

6.2 集計して確認

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

7 確認:個票を表形式に変換する

7.1 count とpivot_widerによる集計=個票(case form)のクロス表への変換

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

7.2 xtabs で表に変換して数値を確認

こうして、.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参照。

8 参考文献

“Discrete Data Analysis with r: Visualization and Modeling Techniques f.” n.d. https://www.routledge.com/Discrete-Data-Analysis-with-R-Visualization-and-Modeling-Techniques-for/Friendly-Meyer/p/book/9781498725835.
“Rではじめるデータサイエンス | Hadley Wickham, Garrett Grolemund, オライリー,ジャパン | Amazon.” n.d. https://www.amazon.co.jp/R%E3%81%A7%E3%81%AF%E3%81%98%E3%82%81%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B5%E3%82%A4%E3%82%A8%E3%83%B3%E3%82%B9-Hadley-Wickham/dp/487311814X.

  1. 度数表への変換は、tdyr::pivot_longer()、もしくはtdyr::gather() で行うことも可能である。また、count()でも可能。いずれの場合でも、集計値の列名は、Freqに変更する必要がある。↩︎