tidyr v1.0.0がそろそろリリースされそうな雰囲気を感じるのでそろそろ重い腰を上げて使ってみます。
ちなみにこのv1.0.0というバージョンは、stableになったという意味ではなくて、新機能てんこ盛りなのでメジャーバージョン上げとくか、というノリなんだと思います。 既存の機能には基本的には変更はないはずです(ただし内部実装は変わってたりするので念のため動作確認しましょう)。
pivot_longer()
/pivot_wider()
gather()
とspread()
の上位互換です。縦長に変形したいときはpivot_longer()
、横長に変形したいときはpivot_wider()
を使います。
かなーりいろんな機能が詰め込まれているので、詳しくはvignetteを見てください。
ここで紹介する例もvignetteから取ってきたものです。
pivot_longer()
基本的な使い方は、
pivot_longer(<縦長に変形したい列>)
という感じです。<縦長に変形したい列>
は、dplyr::select()
と同じ記法が使えます。
例えば、こういう横長なデータを縦長にしたいとします。
relig_income #> # A tibble: 18 x 11 #> religion `<$10k` `$10-20k` `$20-30k` `$30-40k` `$40-50k` `$50-75k` #> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 Agnostic 27 34 60 81 76 137 #> 2 Atheist 12 27 37 52 35 70 #> 3 Buddhist 27 21 30 34 33 58 #> 4 Catholic 418 617 732 670 638 1116 #> 5 Don’t k~ 15 14 15 11 10 35 #> 6 Evangel~ 575 869 1064 982 881 1486 #> 7 Hindu 1 9 7 9 11 34 #> 8 Histori~ 228 244 236 238 197 223 #> 9 Jehovah~ 20 27 24 24 21 30 #> 10 Jewish 19 19 25 25 30 95 #> 11 Mainlin~ 289 495 619 655 651 1107 #> 12 Mormon 29 40 48 51 56 112 #> 13 Muslim 6 7 9 10 9 23 #> 14 Orthodox 13 17 23 32 32 47 #> 15 Other C~ 9 7 11 13 13 14 #> 16 Other F~ 20 33 40 46 49 63 #> 17 Other W~ 5 2 3 4 2 7 #> 18 Unaffil~ 217 299 374 365 341 528 #> # ... with 4 more variables: `$75-100k` <dbl>, `$100-150k` <dbl>, #> # `>150k` <dbl>, `Don't know/refused` <dbl>
ここで、religion
以外の列を縦長に変形するには以下のようにします。
relig_income %>% pivot_longer(-religion) #> # A tibble: 180 x 3 #> religion name value #> <chr> <chr> <dbl> #> 1 Agnostic <$10k 27 #> 2 Agnostic $10-20k 34 #> 3 Agnostic $20-30k 60 #> 4 Agnostic $30-40k 81 #> 5 Agnostic $40-50k 76 #> 6 Agnostic $50-75k 137 #> 7 Agnostic $75-100k 122 #> 8 Agnostic $100-150k 109 #> 9 Agnostic >150k 84 #> 10 Agnostic Don't know/refused 96 #> # ... with 170 more rows
上のように、特に何も指定しなければ、列名がname
列、値がvalue
列に格納されます。
別の名前を使いたければ、それぞれnames_to
、values_to
という引数に列名を指定します。
例えば、列名をincome
列、値をcount
列に格納したい場合は次のようになります。
relig_income %>% pivot_longer(-religion, names_to = "income", values_to = "count")
pivot_longer()
の機能をすべて解説しているとGWが終わってしまうので省略しますが、gather()
より便利な例を一つだけ。
pivot_longer()
を使うと、gather()
+ separate()
が一気にできます。names_sep
/names_pattern
で列名の分割の仕方を指定し、values_to
に複数の列名を指定します。
以下の例だと、.
の前後で列名を分割し、前半をS/P
列、後半をL/W
列に格納しています。
iris %>% pivot_longer( -Species, names_to = c("S/P", "L/W"), names_sep = "\\." ) #> # A tibble: 600 x 4 #> Species `S/P` `L/W` value #> <fct> <chr> <chr> <dbl> #> 1 setosa Sepal Length 5.1 #> 2 setosa Sepal Width 3.5 #> 3 setosa Petal Length 1.4 #> 4 setosa Petal Width 0.2 #> 5 setosa Sepal Length 4.9 #> 6 setosa Sepal Width 3 #> 7 setosa Petal Length 1.4 #> 8 setosa Petal Width 0.2 #> 9 setosa Sepal Length 4.7 #> 10 setosa Sepal Width 3.2 #> # ... with 590 more rows
注意点
ちなみに、gather()
と違って列の指定はひとつの引数にまとめないといけない点に注意してください。
例えば、以下のようにするとエラーになってしまいます。
iris %>% pivot_longer(Sepal.Length, Sepal.Width, Petal.Length, Petal.Width) #> Error in pivot_longer_spec(data, !!cols, names_to = names_to, values_to = values_to, : object 'Petal.Length' not found
ひとつにまとめるというのは、具体的にはc()
で囲めばOKです。
iris %>% pivot_longer(c(Sepal.Length, Sepal.Width, Petal.Length, Petal.Width))
pivot_wider()
基本的な使い方は、
pivot_longer(names_from = <列名が格納された列>, values_from = <値が格納された列>)
という感じです。
例えば、元データのstation
列を列名に、seen
列を値に使って横長に変形するには以下のようにします。
fish_encounters #> # A tibble: 114 x 3 #> fish station seen #> <fct> <fct> <int> #> 1 4842 Release 1 #> 2 4842 I80_1 1 #> 3 4842 Lisbon 1 #> 4 4842 Rstr 1 #> 5 4842 Base_TD 1 #> 6 4842 BCE 1 #> 7 4842 BCW 1 #> 8 4842 BCE2 1 #> 9 4842 BCW2 1 #> 10 4842 MAE 1 #> # ... with 104 more rows fish_encounters %>% pivot_wider(names_from = station, values_from = seen) #> # A tibble: 19 x 12 #> fish Release I80_1 Lisbon Rstr Base_TD BCE BCW BCE2 BCW2 MAE #> <fct> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> #> 1 4842 1 1 1 1 1 1 1 1 1 1 #> 2 4843 1 1 1 1 1 1 1 1 1 1 #> 3 4844 1 1 1 1 1 1 1 1 1 1 #> 4 4845 1 1 1 1 1 NA NA NA NA NA #> 5 4847 1 1 1 NA NA NA NA NA NA NA #> 6 4848 1 1 1 1 NA NA NA NA NA NA #> 7 4849 1 1 NA NA NA NA NA NA NA NA #> 8 4850 1 1 NA 1 1 1 1 NA NA NA #> 9 4851 1 1 NA NA NA NA NA NA NA NA #> 10 4854 1 1 NA NA NA NA NA NA NA NA #> 11 4855 1 1 1 1 1 NA NA NA NA NA #> 12 4857 1 1 1 1 1 1 1 1 1 NA #> 13 4858 1 1 1 1 1 1 1 1 1 1 #> 14 4859 1 1 1 1 1 NA NA NA NA NA #> 15 4861 1 1 1 1 1 1 1 1 1 1 #> 16 4862 1 1 1 1 1 1 1 1 1 NA #> 17 4863 1 1 NA NA NA NA NA NA NA NA #> 18 4864 1 1 NA NA NA NA NA NA NA NA #> 19 4865 1 1 1 NA NA NA NA NA NA NA #> # ... with 1 more variable: MAW <int>
欠損値を埋めるにはvalues_fill
引数を指定します。これは列名 = <デフォルト値>
という形式のリストを取ります。pivot_wider(..., values_fill = 0)
とかやるとエラーになるので注意しましょう。
fish_encounters %>% pivot_wider( names_from = station, values_from = seen, values_fill = list(seen = 0) ) #> # A tibble: 19 x 12 #> fish Release I80_1 Lisbon Rstr Base_TD BCE BCW BCE2 BCW2 MAE #> <fct> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> #> 1 4842 1 1 1 1 1 1 1 1 1 1 #> 2 4843 1 1 1 1 1 1 1 1 1 1 #> 3 4844 1 1 1 1 1 1 1 1 1 1 #> 4 4845 1 1 1 1 1 0 0 0 0 0 #> 5 4847 1 1 1 0 0 0 0 0 0 0 #> 6 4848 1 1 1 1 0 0 0 0 0 0 #> 7 4849 1 1 0 0 0 0 0 0 0 0 #> 8 4850 1 1 0 1 1 1 1 0 0 0 #> 9 4851 1 1 0 0 0 0 0 0 0 0 #> 10 4854 1 1 0 0 0 0 0 0 0 0 #> 11 4855 1 1 1 1 1 0 0 0 0 0 #> 12 4857 1 1 1 1 1 1 1 1 1 0 #> 13 4858 1 1 1 1 1 1 1 1 1 1 #> 14 4859 1 1 1 1 1 0 0 0 0 0 #> 15 4861 1 1 1 1 1 1 1 1 1 1 #> 16 4862 1 1 1 1 1 1 1 1 1 0 #> 17 4863 1 1 0 0 0 0 0 0 0 0 #> 18 4864 1 1 0 0 0 0 0 0 0 0 #> 19 4865 1 1 1 0 0 0 0 0 0 0 #> # ... with 1 more variable: MAW <int>
pivot_wider()
も、pivot_longer()
と同じく、複数の列を処理することができます。
例えば、英語ブログの方に書いたgather()
/spread()
ではうまく処理できない例は以下のように扱えます(Date型がちゃんと保持されているのに注目)。
# a bit different version of https://github.com/tidyverse/tidyr/issues/149#issue-124411755 d <- tribble( ~place, ~censor, ~date, ~status, "g1", "c1", as.Date("2019-02-01"), "ok", "g1", "c2", as.Date("2019-02-01"), "bad", "g1", "c3", as.Date("2019-02-01"), "ok", "g2", "c1", as.Date("2019-02-01"), "bad", "g2", "c2", as.Date("2019-02-02"), "ok" ) d %>% pivot_wider(names_from = place, values_from = c(date, status)) #> # A tibble: 3 x 5 #> censor date_g1 date_g2 status_g1 status_g2 #> <chr> <date> <date> <chr> <chr> #> 1 c1 2019-02-01 2019-02-01 ok bad #> 2 c2 2019-02-01 2019-02-02 bad ok #> 3 c3 2019-02-01 NA ok <NA>
あと、pivot_wider()
は、キーがユニークでない場合にどう処理するかを指定するvalues_fn
という引数がけっこう奥深いですが、ここでは省略します。興味ある人はvignetteのWider - Aggregationという項目を読んでください。
hoist()
これはlist型の列から一部の要素を抜き出してくるときに便利な関数です。NEWS.mdに載ってる以下の例がわかりやすいです。
df %>% hoist(metadata, name = "name") # shortcut for df %>% mutate(name = map_chr(metadata, "name"))
unnest_longer()
/unnest_wider()
unnest()
の、列方向にしか展開しないバージョン、行方向にしか展開しないバージョン、です。
これは?unnest
に載ってる例を出すと、こういうデータを、
df <- tibble( x = 1:3, y = list( NULL, tibble(a = 1, b = 2), tibble(a = 1:3, b = 3:1) ) ) df #> # A tibble: 3 x 2 #> x y #> <int> <list> #> 1 1 <NULL> #> 2 2 <tibble [1 x 2]> #> 3 3 <tibble [3 x 2]>
unnest()
だと列方向・行方向ともに展開しますが、
df %>% unnest(y) #> # A tibble: 4 x 3 #> x a b #> <int> <dbl> <dbl> #> 1 2 1 2 #> 2 3 1 3 #> 3 3 2 2 #> 4 3 3 1
unnest_wider()
だと行方向、unnest_longer()
だと列方向にしか展開しません。
df %>% unnest_wider(y) #> # A tibble: 3 x 3 #> x a b #> <int> <list> <list> #> 1 1 <NULL> <NULL> #> 2 2 <dbl [1]> <dbl [1]> #> 3 3 <int [3]> <int [3]> df %>% unnest_longer(y) #> # A tibble: 4 x 2 #> x y$a $b #> <int> <dbl> <dbl> #> 1 2 1 2 #> 2 3 1 3 #> 3 3 2 2 #> 4 3 3 1
また、これに関連して?unnest()
の内部実装もけっこう変わってるっぽいので、もしunnest()
を使っているコードがあれば、念のため動作を確認しておきましょう。
I have done my best to ensure that common uses of
unnest()
will continue to work, generating an informative warning tell you how to update your code. Please file an issue if I've missed an important use case.
pack()
/unpack()
/chop()
/unchop()
このあたりの関数を直接使う機会はあまりないかもしれませんが、以下でツイートされていた概念です(dice()
はchop()
になったらしい)。
unnest_()
/nest_()
の廃止
これらの関数は完全に動かなくなります。まだ使ってるという人ははやいところ脱却しましょう。
tidyr::nest_() #> Error: 'nest_' is defunct. #> Use 'nest' instead. #> See help("Defunct")
その他
とりあえずこんなものかな...。何かあれば随時追記します。