まずはサンプルデータ
#事前・事後・遅延事後でCAFのデータを取ってるというデザイン
pre.fluency <-rnorm(50,mean=10,sd=2)
pre.accuracy<-rnorm(50,mean=7,sd=2)
pre.complexity<-rnorm(50,mean=4,sd=2)
post.fluency<-rnorm(50,mean=14,sd=4)
post.accuracy<-rnorm(50,mean=10,sd=3)
post.complexity<-rnorm(50,mean=7,sd=2)
delayed.fluency<-rnorm(50,mean=12,sd=2)
delayed.accuracy<-rnorm(50,mean=8,sd=1)
delayed.complexity<-rnorm(50,mean=6,sd=1)
#それぞれの列を横にくっつける
dat<-cbind(pre.fluency,pre.accuracy,pre.complexity,post.fluency,post.accuracy,post.complexity,delayed.fluency,delayed.accuracy,delayed.complexity)
dat<-as.data.frame(dat) #データフレーム型に変換
dat$subject<-rep(1:50) #実験参与者のID列をつける
縦横変換
昔の縦横変換は,gatherでやっていました。現在は,pivot_関数を使います。その名の通り,縦長(long型)にするのがpivot_longer関数で,横長にするのがpivot_wider関数です。
library(dplyr)
dat %>%
tidyr::pivot_longer(cols = 1:9)
## # A tibble: 450 × 3
## subject name value
## <int> <chr> <dbl>
## 1 1 pre.fluency 9.34
## 2 1 pre.accuracy 3.92
## 3 1 pre.complexity 4.58
## 4 1 post.fluency 13.9
## 5 1 post.accuracy 2.24
## 6 1 post.complexity 9.38
## 7 1 delayed.fluency 10.6
## 8 1 delayed.accuracy 6.93
## 9 1 delayed.complexity 7.92
## 10 2 pre.fluency 9.13
## # … with 440 more rows
colsのところでどの列をまとめるかという指定をします。ここの指定は,上のやり方だと数字で列指定(1列目から9列目)としています。この部分はc()関数を使って文字列で指定してもいいですし,列指定のときに使えるstarts_with()やcontains() なんかもできます。例えば,まあこれはあくまで偶然そうなだけですけど,ここではまとめたい1列目から9列目はすべて”y”で終わっているので,以下のようにすることもできます。
dat %>%
tidyr::pivot_longer(cols = ends_with("y"))
## # A tibble: 450 × 3
## subject name value
## <int> <chr> <dbl>
## 1 1 pre.fluency 9.34
## 2 1 pre.accuracy 3.92
## 3 1 pre.complexity 4.58
## 4 1 post.fluency 13.9
## 5 1 post.accuracy 2.24
## 6 1 post.complexity 9.38
## 7 1 delayed.fluency 10.6
## 8 1 delayed.accuracy 6.93
## 9 1 delayed.complexity 7.92
## 10 2 pre.fluency 9.13
## # … with 440 more rows
また,まとめたくない列を”!“で指定することもできるので,今回のようにまとめないでほしい列が少ないという場合については,次のようにも出来ます。
dat %>%
tidyr::pivot_longer(!subject)
## # A tibble: 450 × 3
## subject name value
## <int> <chr> <dbl>
## 1 1 pre.fluency 9.34
## 2 1 pre.accuracy 3.92
## 3 1 pre.complexity 4.58
## 4 1 post.fluency 13.9
## 5 1 post.accuracy 2.24
## 6 1 post.complexity 9.38
## 7 1 delayed.fluency 10.6
## 8 1 delayed.accuracy 6.93
## 9 1 delayed.complexity 7.92
## 10 2 pre.fluency 9.13
## # … with 440 more rows
「まとめる」という感覚がいまいちよくわからないなぁとか,どの列を「まとめ」て,どの列は「まとめ」なくていいのかというのがピンと来ない場合には,次のように考えてください。
分析の際に従属変数(応答変数)となる列をまとめる
さて,今の段階ではまとめた際の列名が”name”になっていて,数値の部分が”value”という列名になっていますよね。ここも次のように指定できます。
dat %>%
tidyr::pivot_longer(!subject, names_to = "variable", values_to = "score")
## # A tibble: 450 × 3
## subject variable score
## <int> <chr> <dbl>
## 1 1 pre.fluency 9.34
## 2 1 pre.accuracy 3.92
## 3 1 pre.complexity 4.58
## 4 1 post.fluency 13.9
## 5 1 post.accuracy 2.24
## 6 1 post.complexity 9.38
## 7 1 delayed.fluency 10.6
## 8 1 delayed.accuracy 6.93
## 9 1 delayed.complexity 7.92
## 10 2 pre.fluency 9.13
## # … with 440 more rows
ただ,今回のケースではまとめた列に”pre”, “post”, “delayed”というテスト実施時期(test)という要因と,“complexity”, “accuracy”, “fluency”という測定値の要因が混在していますね。よって,あまり”name”の列名を変えることは意味がありません。むしろ,この列を分割してそれぞれの列に名前をつける必要があります。この列分割をseparate関数で行うという点は私が以前ブログ記事を書いた際と同じです。というわけで,次のようにします。
dat %>%
tidyr::pivot_longer(!subject) %>%
tidyr::separate(name, c("test","measure"), sep = "\\.")
## # A tibble: 450 × 4
## subject test measure value
## <int> <chr> <chr> <dbl>
## 1 1 pre fluency 9.34
## 2 1 pre accuracy 3.92
## 3 1 pre complexity 4.58
## 4 1 post fluency 13.9
## 5 1 post accuracy 2.24
## 6 1 post complexity 9.38
## 7 1 delayed fluency 10.6
## 8 1 delayed accuracy 6.93
## 9 1 delayed complexity 7.92
## 10 2 pre fluency 9.13
## # … with 440 more rows
これで,完璧ですね。と思いきや…!実はpivot_関数には”name_sep”という便利な引数があります。これはどういう時に使うかと言うと,まとめた際に一つの列に複数の要因が混在してしまうときに,それを指定した区切り文字によって分割するために使います。まさに上で起こった問題ですよね。テスト実施時期と測定値が一緒の列になっていたのをseparate関数で分割したわけですが,なんとseparateを使わなくても縦型に変換する段階で分割までできてしまいます。
dat %>%
tidyr::pivot_longer(!subject, names_to = c("test", "measure"), names_sep = "\\.", values_to = "score")
## # A tibble: 450 × 4
## subject test measure score
## <int> <chr> <chr> <dbl>
## 1 1 pre fluency 9.34
## 2 1 pre accuracy 3.92
## 3 1 pre complexity 4.58
## 4 1 post fluency 13.9
## 5 1 post accuracy 2.24
## 6 1 post complexity 9.38
## 7 1 delayed fluency 10.6
## 8 1 delayed accuracy 6.93
## 9 1 delayed complexity 7.92
## 10 2 pre fluency 9.13
## # … with 440 more rows
このときのポイントは2点あります。1つは”names_to”で列名を2つ指定すること。縦型変換の際に列の分割もするので,ここでのnames_toの指定は分割後の列名とします。もう一つは,区切り文字は正規表現を受け付けるということ。区切り文字がドット(.)なので,ここでnames_sep= “.”としてしまうと,正規表現におけるドットだと認識されてしまいます。これでは任意の1文字ですので,列名がうまく分割されずに以下のようになってしまいます。
dat %>%
tidyr::pivot_longer(!subject, names_to = c("test", "measure"), names_sep = ".", values_to = "score")
## Warning: Expected 2 pieces. Additional pieces discarded in 9 rows [1, 2, 3, 4,
## 5, 6, 7, 8, 9].
## # A tibble: 450 × 4
## subject test measure score
## <int> <chr> <chr> <dbl>
## 1 1 "" "" 9.34
## 2 1 "" "" 3.92
## 3 1 "" "" 4.58
## 4 1 "" "" 13.9
## 5 1 "" "" 2.24
## 6 1 "" "" 9.38
## 7 1 "" "" 10.6
## 8 1 "" "" 6.93
## 9 1 "" "" 7.92
## 10 2 "" "" 9.13
## # … with 440 more rows
したがって,正規表現ではありませんよということを追記する必要があります。このことをエスケープするなんて言いますが,Rにおけるエスケープは “\\”です。一般的にはエスケープは”\“ですが,R上では2つ重ねないといけないことに注意が必要です。