tidyr::gather( )とtidyr::spread( )でデータフレームを自在に変形する

以下は`{tidyr}`パッケージのgather()関数とspread()関数のヘルプページを改変・再構成したメモ書きです。

`{tidyr}`パッケージとTidy Dataについてより詳しく知りたい方は [R for Data Science - 12 Tidy data](http://r4ds.had.co.nz/tidy-data.html) をぜひ御覧ください。


----
# 基本的な使い方

説明のために、まず次のようにして`stocks`というデータフレームを作ります。

```
set.seed(71)
day_length <- 4
stocks <- data.frame(
  time = seq.Date(as.Date('2009-01-01'), by = "day", length.out = day_length),
  X = rnorm(n = day_length, mean = 0, sd = 1),
  Y = rnorm(n = day_length, mean = 0, sd = 2),
  Z = rnorm(n = day_length, mean = 0, sd = 4)
)
```

X, Y, Zの3つの銘柄について前日の株価との値段の増減比（％）を取ったもの、とでも思ってください。

|time       |          X|          Y|         Z|
|:----------|----------:|----------:|---------:|
|2009-01-01 | -0.4318422| -0.8358013| -4.433913|
|2009-01-02 | -0.4471872| -2.3743278|  5.053723|
|2009-01-03 | -0.4785726| -2.1445711|  1.171803|
|2009-01-04 |  0.4171454|  2.0065677|  2.193100|

分析時に読み込むデータは、このように**１つの列が１つの系列**になっていることが多々あります。

しかしこの形だと、銘柄(X, Y, Z...)が増えるたびに列を増やさなければいけなくなってしまいます。
これでは分析用のコードを書く上で不都合なので、**分析対象が増えても行だけが増えるように**データの構造を変更したいことがあります。




## gather
 
そんなときに`tidyr::gather()`を使うとデータフレームの構造を簡単に変更できて便利です。

tidyr::gather()関数は以下の様に引数を指定します。...(ドット３つ)は可変長の引数を取れることを意味します。

```r
data %>%
  tidyr::gather(key, value, ...)
```

さきほどのstocksデータについて、`X, Y, Z`をstock列の"ラベル"にして、付随する数値はprice列に放り込みたいときは、以下のようにします。
※パイプ演算子(%>%)を使うためにdplyrパッケージを読み込んでいます


```r
library(dplyr)
stocksm <- stocks %>%
  tidyr::gather(key = stock, value = price, X, Y, Z)
stocksm
```

|time       |stock |      price|
|:----------|:-----|----------:|
|2009-01-01 |X     | -0.4318422|
|2009-01-02 |X     | -0.4471872|
|2009-01-03 |X     | -0.4785726|
|2009-01-04 |X     |  0.4171454|
|2009-01-01 |Y     | -0.8358013|
|2009-01-02 |Y     | -2.3743278|
|2009-01-03 |Y     | -2.1445711|
|2009-01-04 |Y     |  2.0065677|
|2009-01-01 |Z     | -4.4339130|
|2009-01-02 |Z     |  5.0537229|
|2009-01-03 |Z     |  1.1718029|
|2009-01-04 |Z     |  2.1931001|

ここで、`tidyr::gather()`の可変長引数は`dplyr::select()`に準ずる書き方ができるため、普通は以下のように「time列を除いて全部」というように書くことが多いです。

```r
stocks %>%
  tidyr::gather(key = stock, value = price, -time)
```

あるいは、「X列からZ列」という意味で、以下のように書いても同じ結果が得られます。

```r
stocks %>%
  tidyr::gather(key = stock, value = price, X:Z)
```



## spread

さきほどの構造を元の形に戻したくなったら、`tidyr::spread()`を使います。
key 引数で指定した変数が列名になり、
value 引数に指定した変数が各列の中身になります。こんどは可変長引数はありません。

```r
stocksm %>%
  tidyr::spread(key = stock, value = price)
```

|time       |          X|          Y|         Z|
|:----------|----------:|----------:|---------:|
|2009-01-01 | -0.4318422| -0.8358013| -4.433913|
|2009-01-02 | -0.4471872| -2.3743278|  5.053723|
|2009-01-03 | -0.4785726| -2.1445711|  1.171803|
|2009-01-04 |  0.4171454|  2.0065677|  2.193100|

key引数に日付を指定することで、「行と列を入れ替える」こともできます。

```r
stocksm %>%
  tidyr::spread(key = time, value = price)
```

|stock | 2009-01-01| 2009-01-02| 2009-01-03| 2009-01-04|
|:-----|----------:|----------:|----------:|----------:|
|X     | -0.4318422| -0.4471872| -0.4785726|  0.4171454|
|Y     | -0.8358013| -2.3743278| -2.1445711|  2.0065677|
|Z     | -4.4339130|  5.0537229|  1.1718029|  2.1931001|

# 便利なオプション
ここからは便利なオプションの使い方（抜粋）です。

## spreadすると欠損値が生じる場合

次のようなデータを考えます。

```r
df <- data.frame(
  time = as.Date(c("2016-09-01", "2016-09-02")),
  label = c("a", "b"),
  val = c(3, 4)
)
```


|time       |label | val|
|:----------|:-----|---:|
|2016-09-01 |a     |   3|
|2016-09-02 |b     |   4|

上のようなデータフレームをlabelでspreadすると次のように欠損値が挿入されます。

```r
df %>%
 tidyr::spread(key = label, value = val)
```

|time       |  a|  b|
|:----------|--:|--:|
|2016-09-01 |  3| NA|
|2016-09-02 | NA|  4|

これをそのままgatherしてしまうと

|time       |label | val|
|:----------|:-----|---:|
|2016-09-01 |a     |   3|
|2016-09-02 |a     |  NA|
|2016-09-01 |b     |  NA|
|2016-09-02 |b     |   4|

こうなってしまうので、gatherした後にNAをわざわざ表示したくない場合はna.rm = TRUEオプションを指定します。

```r
df %>%
 tidyr::spread(key = label, value = val1) %>%
 tidyr::gather(key = label, value = val1, a:b, na.rm = TRUE)
```

|   |time       |label | val|
|:--|:----------|:-----|---:|
|1  |2016-09-01 |a     |   3|
|4  |2016-09-02 |b     |   4|

## 暗黙の型変換を起こしてしまう場合

```r
kvs_iris <- data.frame(
  id = rep(c(1, 51), each = 3),
  name = c("Species", "Species_num", "Sepal.Length"),
  value = c("setosa", 1, 5.1, "versicolor", 7.0, 2)
)
```

Key-Value Storeのようなキーと値がペアになったデータベースからデータをダンプすると以下の様な形式になることがあります。

|  id|name         |val        |
|---:|:------------|:----------|
|   1|Sepal.Length |5.1        |
|   1|Species      |setosa     |
|   1|Species_num  |1          |
|  51|Sepal.Length |7          |
|  51|Species      |versicolor |
|  51|Species_num  |2          |

このとき、データフレーム内部では列ごとに、一番表現力の大きい形式へと暗黙のうちに変換がなされています。name列とvalue列は因子になっています。

```
> str(kvs_iris)
'data.frame':	6 obs. of  3 variables:
 $ id   : num  1 1 1 51 51 51
 $ name : Factor w/ 3 levels "Sepal.Length",..: 2 3 1 2 3 1
 $ value: Factor w/ 6 levels "1","2","5.1",..: 5 1 3 6 4 2
```

上のデータフレームを、単純にspreadすると次のようになります。

```r
kvs_iris %>%
 tidyr::spread(key = name, value = val)
```

|  id|Sepal.Length |Species    |Species_num |
|---:|:------------|:----------|:-----------|
|   1|5.1          |setosa     |1           |
|  51|7            |versicolor |2           |

一見問題なさそうですが、str()関数で各列を見てみると、もとのデータフレームの因子が保持されてしまっています。

```r
kvs_iris %>% 
  spread(key = name, value = val) %>%
  str()
```

```
'data.frame':	2 obs. of  4 variables:
 $ id          : num  1 51
 $ Sepal.Length: Factor w/ 6 levels "1","2","5.1",..: 3 4
 $ Species     : Factor w/ 6 levels "1","2","5.1",..: 5 6
 $ Species_num : Factor w/ 6 levels "1","2","5.1",..: 1 2
```

これでは困るので、convert = TRUE　オプションを指定すると、spread後に適切な型に変換されています。

```r
df %>%
 tidyr::spread(var, value, convert = TRUE) %>%
 str()
```

```
'data.frame':	2 obs. of  4 variables:
 $ id          : num  1 51
 $ Sepal.Length: num  5.1 7
 $ Species     : chr  "setosa" "versicolor"
 $ Species_num : int  1 2
```

データフレームを作るときにstringAsFactors = FALSEを指定していても、暗黙の型変換によってid列以外が文字列になってしまいますが、その場合も同様に変換してくれます。ちょうべんり。


# 実行環境

```
> devtools::session_info()
Session info --------------------------------------------------------------------------
 setting  value                       
 version  R version 3.3.1 (2016-06-21)
 system   x86_64, darwin13.4.0        
 ui       RStudio (0.99.491)          
 language (EN)                        
 collate  ja_JP.UTF-8                 
 tz       Asia/Tokyo                  
 date     2016-09-03                  

Packages ------------------------------------------------------------------------------
 package    * version date       source        
 assertthat   0.1     2013-12-06 cran (@0.1)   
 devtools     1.12.0  2016-06-24 CRAN (R 3.3.0)
 digest       0.6.10  2016-08-02 CRAN (R 3.3.0)
 magrittr     1.5     2014-11-22 cran (@1.5)   
 memoise      1.0.0   2016-01-29 CRAN (R 3.3.0)
 Rcpp         0.12.6  2016-07-19 CRAN (R 3.3.0)
 tibble       1.1     2016-07-04 cran (@1.1)   
 tidyr      * 0.6.0   2016-08-12 CRAN (R 3.3.0)
 withr        1.0.2   2016-06-20 CRAN (R 3.3.0)
```


**Enjoy!**