備忘ログ

チラシの裏的備忘録&メモ

`{tidyr}`の`pivot_longer()`の`names_pattern`と`pivot_wider()`の`names_glue`で混乱したメモ

たまに次のような時系列を意図したデータに出くわす時がある。

# サンプルデータを作る
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を指定すると混乱する。

近親的な関数で列名を処理する系の引数なのにちょっと互いの微妙な入力すべき値の形の微妙なただし多いなる違いにより時折混乱する。