LoginSignup
64
38

【tidyr】gather?, spread? もう古い。時代はpivot

Last updated at Posted at 2019-09-24

tidyr 1.0.0公開

皆さん、Tidyverse使ってますか?網羅的な統計処理を遂行するためにR, 特にTidyverseは非常に有効なツールです。先日Tidyverseの作成者である**Hadley(神)**がtidyrのアップデートを行いました。その中でも一番大きい変化は今まで用いられてきたgather(), spread()関数がpivot_longer(), pivot_wider()に変更されたことではないでしょうか?
**今回の記事ではこれらの関数が今までのものとどのように変わったかを簡単に解説します。**また、次回以降の記事ではpivot_*()についてのユニークな機能についても書いていこうと思っています。

TL;DR

基本的な使い方に関して、大きな変更は無いよ! でも引数がよりわかりやすくなったからコードの可読性がアップしたよ!やったね!

gather(), spread() → pivot_longer(), pivot_wider()

gather()関数はみなさんもよくご存じの通り、messy data (雑然データ)を**tidy data (整然データ)**とするために必須と言える関数です(reshape2::melt()みたいなものもありますが)。例えば、

{messy.R}
library(tidyverse)

df <- tibble("country" = c("a", "b", "c"),
             "1999" = c(0.7, 0.3, 1.0),
             "2000" = c(1.0, 2.0, 4.8),
             "2001" = c(2.0, 5.0, 7.0))
df

# A tibble: 3 x 4
  country `1999` `2000` `2001`
  <chr>    <dbl>  <dbl>  <dbl>
1 a          0.7    1        2
2 b          0.3    2        5
3 c          1      4.8      7

というようなmessy dataを考えてみましょう。これをtidy dataとするには、

{gather.R}
library(tidyverse)

df %>%
  gather(-country, key = "year", value = "amount")

# A tibble: 9 x 3
  country year  amount
  <chr>   <chr>  <dbl>
1 a       1999     0.7
2 b       1999     0.3
3 c       1999     1  
4 a       2000     1  
5 b       2000     2  
6 c       2000     4.8
7 a       2001     2  
8 b       2001     5  
9 c       2001     7  

とすることで見事にデータ処理のしやすいtidy dataが出力されます。このgather()関数は引数として、data, key, valueを取ります。今回の場合ですと、*「country列以外の列についてyear列を新たに設定し、表中の値をamount列に格納する」*という処理を施しています。...が、これらの処理を引数から予測しづらいことが問題となっていました。実際に、公式のドキュメントにおいても、

For some time, it’s been obvious that there is something fundamentally wrong with the design of spread() and gather(). Many people don’t find the names intuitive and find it hard to remember which direction corresponds to spreading and which to gathering. It also seems surprisingly hard to remember the arguments to these functions, meaning that many people (including me!) have to consult the documentation every time.

テキトー訳

どう見ても明らかなようにspread(), gather()関数は根本的な設計ミスってたわ。関数名がわかりにくく、どのように表が変化していくかを予測しにくいし、引数なんて覚えらんないから自分含め多くの人がドキュメント見てるわ。

とまぁ、惨憺な言われようです。そこで神が作り出したのが**pivot_*()**関数です。従来のgather()に相当するものはpivot_longer() です。使い方を見てみましょう。

{pivot_longer.R}
library(tidyverse)

df_longer <- df %>%
  pivot_longer(col = -country, names_to = "year", values_to = "amount")

df_longer

# A tibble: 9 x 3
  country year  amount
  <chr>   <chr>  <dbl>
1 a       1999     0.7
2 a       2000     1  
3 a       2001     2  
4 b       1999     0.3
5 b       2000     2  
6 b       2001     5  
7 c       1999     1  
8 c       2000     4.8
9 c       2001     7 

基本的に、 pivot_longer()gather()と同様の書き方をすれば良いことがわかります。しかし引数の名前がdata, key, valueからcol, names_to, values_toという比較的容易に引数から内容を把握できるものに改善されています。もし、このtidy dataをもとに戻したい場合はpivot_wider()を使います。従来ではspread()がその役割を担っていました。しかし、その引数がまたもやdata, key, valueだったので、pivot_wider()ではcol, names_from, values_fromに変更となりました。

{pivot_wider.R}
library(tidyverse)

df_wider <- df_longer %>%
  pivot_wider(names_from = "year", values_from = "amount")

df_wider

# A tibble: 3 x 4
  country `1999` `2000` `2001`
  <chr>    <dbl>  <dbl>  <dbl>
1 a          0.7    1        2
2 b          0.3    2        5
3 c          1      4.8      7

このように、引数名がより明確になったことでコードの可読性が向上したことがわかります。基本的な記法は変化していないことから、従来からtidyrを使用している人たちも簡単に移行できると思います。次回pivot_longer()のユニークな機能について書いていこうと思います。

64
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
64
38