論理の流刑地

地獄の底を、爆笑しながら闊歩する

【R】wide方式からlong方式への変換の仕方 in 自力&tidyr::pivot_longer

これも同じことを100回調べるので備忘用シリーズ
なんかStataとかよりwide↔longの相互変換のやりかたがわかりにくいのよな。

Introduction

基本的にはstats::reshape()を使う。
tidyrパッケージを使う方法もあるけど、とりあえずベーシックなやり方をまとめとく。

参考URL

とりあえずトレーニング用データとしてmlogitパッケージのTrainデータを読み込んどく

library(mlogit)
data(Train)
head(Train)

 id choiceid choice price_A time_A change_A comfort_A price_B time_B change_B comfort_B
 1  1        1      A    2400    150        0         1    4000    150        0         1
 2  1        2      A    2400    150        0         1    3200    130        0         1

Wide to Long

関数作った

いろいろ調べた...結果reshapeの引数指定がめんどくさいので、ラッパー関数を作った

##---Wide to Longの変換用関数---##
##[引数定義]##
#[1]dta , 変換対象のデータフレーム
#[2]vary_vname ,characterのvector。longにしたい対象の変数のindexを除いた部分(Ex: wage1, wage2,,,って感じだったら"wage")
#[3]vary_idx , longにしたい対象の変数のindexをあらわすvector(Ex: wage1, wage2,,,wage10だったら1:10)
#[4]sep , vary_vnameとvary_idxの間に区切り文字があればそれを指定
#[5]lev_vname ,  vary_idxを水準としてもつ新変数の名前(charcter)

wideToLong <- function(dta ,vary_vname, vary_idx ,sep=NULL,lev_vname="LEVEL"){
  if(is.null(sep)){ sep <- ""}
  vary_prm <- lapply(vary_vname , function(vname){
    return( paste(vname ,vary_idx, sep=sep))
  })#lapply
  
  id_vnames <- setdiff( colnames(dta), unlist( vary_prm))
  long_df <- reshape(data = dta ,direction="long",idvar = id_vnames, varying =vary_prm, v.names = vary_vname ,sep=sep,times=vary_idx,timevar=lev_vname)
  rownames(long_df) <- c()
  return(long_df )
} #function

実行

dta_w <- wideToLong( Train, c("price","time","change","comfort"),c("A","B"),sep="_",lev_vname="AorB")

head(dta_w)
id choiceid choice AorB price time change comfort
1  1        1      A    A  2400  150      0       1
2  1        2      A    A  2400  150      0       1
3  1        3      A    A  2400  115      0       1

悪くないね。

tidyrの神機能"pivot"

(こっから追記@19/10/21)

自分の検索スキルを恨むべきなのだが、上のような行儀のよろしくない関数をかく必要はなくて
tidyrのver1.0.0で追加されたpivotingを使うことで、より高速かつ汎用的なwide to longの整形をすることができる。

参考URL

  1. 雑訳vignette: Pivoting (tidyr 1.0.0) | Atusy's blog
  2. tidyr 1.0.0の新機能 pivot_*() / tidyr-pivot - Speaker Deck

先ほどと同じ処理を行う

tra_L <- tidyr::pivot_longer( 
Train , 
price_A:comfort_B ,
names_to =  c(".value","Type"),
names_pattern="(.*)_(.*)"
)

head(as.data.frame(tra_L))
  id choiceid choice Type price time change comfort
1  1        1      A    A  2400  150      0       1
2  1        1      A    B  4000  150      0       1
3  1        2      A    A  2400  150      0       1
4  1        2      A    B  3200  130      0       1
5  1        3      A    A  2400  115      0       1
6  1        3      A    B  4000  115      0       0

あらシンプル!

第三引数のnames_toにおける".value"が肝で、これにより、新しく作る変数の名前として元の変数名(の部分文字列)を使うことができる。
たとえば身長と体重のT期間にわたるデータがheight1, weight1,height2, weight2,,,, height_T, weight_Tという変数で入っている場合はc(".value","time")といった形で指定すると、long型になった整形先データにはheight, weight , timeという変数ができる。

第四引数のname_patternは正規表現を用いて、新しい変数に相当する箇所を指定する。

パネルデータなどの変換などの用途に対してもとても強力である。

くわしくは参考URL1の「一行に複数の観測値がある場合」の箇所を参考にしてほしい。

これ本当に汎用性が高い

おまけ:Rにおける正規表現に関する参考URL集(完全に自分用備忘)

別言語だと正規表現ガリガリ書いていたりしたが、Rについては完全に無知だったので参照用に残しとく。

  1. R における正規表現 - RjpWiki
  2. R: Regular Expressions as used in R

おおむねこの二つで足りると思う。[[:alnum:]]とか結構独特だったりする。
パネルデータの整形上は[[:digit:]]が数値を表すことを忘れないようにするのが大事かな。

Enjoy!