たまに次のような時系列を意図したデータに出くわす時がある。
# サンプルデータを作る set.seed(5) d <- data.table::data.table(ID = letters[1:5], "BMI(0)" = rnorm(5, 25), "BMI(6)" = rnorm(5, 25), "BMI(12)" = rnorm(5, 25), "Steps(0)" = rnorm(5, 10000), "Steps(6)" = rnorm(5, 10000), "Steps(12)" = rnorm(5, 10000))
# `{knitr}`を使って表示してみる knitr::kable(d, format = "markdown")
ID | BMI(0) | BMI(6) | BMI(12) | Steps(0) | Steps(6) | Steps(12) |
---|---|---|---|---|---|---|
a | 24.15914 | 24.39709 | 26.22763 | 9999.861 | 10000.90 | 9999.707 |
b | 26.38436 | 24.52783 | 24.19822 | 9999.403 | 10000.94 | 10001.419 |
c | 23.74451 | 24.36463 | 23.91961 | 9997.816 | 10001.47 | 10001.499 |
d | 25.07014 | 24.71423 | 24.84247 | 10000.241 | 10000.71 | 9999.343 |
e | 26.71144 | 25.13811 | 23.92824 | 9999.741 | 10000.82 | 9999.147 |
どういうデータかというと、項目(タイミング)
という列名で時系列を意図したデータ。0~12ヶ月で半年ごとに観測されたデータを今回は意図していている。
これを{ggplot}
などで扱いやすいように{tidyr}
のpivot_longer()
を使って縦長のデータに書き換える時、列名をそのままname
列として持ってきても良いが、時系列として、項目名とタイミングでわけて取り扱いたい。
そのとき区切り文字が()
みたいなのだとちょっと注意を要するのでメモしておく。というか一時混乱したのでメモしておく。
といっても、{tidyr}
のpivot_longer()
のnames_pattern
引数の話をするだけ。項目_タイミング
が列名だと簡単なんだけど区切り文字が()
(カッコ)だとちょっと注意を要する。つまり区切り文字が正規表現のメタ文字にあたるときに注意を要する。メタ文字は色々あるが、列名で使われる事がありそうなのは.
とか、()
とかかなと自分の観測範囲では思っている。
なんで注意を要するかというと、列名をnames_pattern
引数で切り出すときに正規表現を使って取り出すから。
結論をさっさと書くが、
a <- tidyr::pivot_longer(d, cols = 2:7, names_to = c("item", "time"), names_pattern = "(.*)\\((.*)\\)") # データが長いので`head()`で表示範囲を短くする knitr::kable(head(a), format = "markdown")
ID | item | time | value |
---|---|---|---|
a | BMI | 0 | 24.15914 |
a | BMI | 6 | 24.39709 |
a | BMI | 12 | 26.22763 |
a | Steps | 0 | 9999.86101 |
a | Steps | 6 | 10000.90051 |
a | Steps | 12 | 9999.70652 |
とするとできる。
注意が必要なのはメタ文字じゃない意味で区切り文字としての()
カッコとか.
ドットとかを分割するところのパターンに使うときにはエスケープしてやるとできる。エスケープするにはバックスラッシュを2個前につける。
ちなみにうっかりバックスラッシュ1個だけでエスケープしようとするとエラーが出る。
なんでこんな(正規表現で取り出していることを理解していればわかりそうなこと)をあえてメモするかというと、縦長のデータを横長にする時に使う{tidyr}
のpivot_wider()
だと値を2つくっつけて列名するとき()
使ったデータを作るときにnames_glue
引数とはちょっと違うから。
今回作った縦長のデータの場合に、もとのデータのように項目(タイミング)
のような列名にしたい場合、次のようになる。
b <- tidyr::pivot_wider(a, id_cols = ID, names_from = c(item, time), names_glue = "{item}({time})") knitr::kable(b, format = "markdown")
ID | BMI(0) | BMI(6) | BMI(12) | Steps(0) | Steps(6) | Steps(12) |
---|---|---|---|---|---|---|
a | 24.15914 | 24.39709 | 26.22763 | 9999.861 | 10000.90 | 9999.707 |
b | 26.38436 | 24.52783 | 24.19822 | 9999.403 | 10000.94 | 10001.419 |
c | 23.74451 | 24.36463 | 23.91961 | 9997.816 | 10001.47 | 10001.499 |
d | 25.07014 | 24.71423 | 24.84247 | 10000.241 | 10000.71 | 9999.343 |
e | 26.71144 | 25.13811 | 23.92824 | 9999.741 | 10000.82 | 9999.147 |
普通に作りたい列名をnames_glue
で指定する時に特に何も考えずに()
を入れたいところに入れるとできる。この感覚でpivot_longer()
でnames_pattern
を指定すると混乱する。
近親的な関数で列名を処理する系の引数なのにちょっと互いの微妙な入力すべき値の形の微妙なただし多いなる違いにより時折混乱する。