【R】「rbind()」と「dplyr::bind_rows()」を正しく使い分ける

# 1. 趣旨
`rbind()`と`dplyr::bind_rows()`は、どちらもデータフレームに行を結合する関数ですが、微妙に使い方が違うようなので、Tipsとして書き記しておきます。

今回は、ベースとなるdata.frameに、追加サンプルとしてベクトルや新たなdata.frameを結合するときを例に使い分けてみましょう。

# 2. 先に結論を言うと

基本的には

- <b>`rbind()`はdata.frameとベクトルの結合に有効</b>
- <b>`rbind()`はdata.frame同士の結合も可能だが、条件が超厳しい</b>
- <b>`dplyr::bind_rows()`はdata.frame同士の結合に有効</b>
- <b>`dplyr::bind_rows()`は、条件付きだがdata.frameとベクトルの結合にも使える。</b>


という感じです。

# 3. サンプルデータ

## ベースとなるデータフレーム

```r
library(magrittr)
name <- c("田中", "佐藤", "斉藤", "本田")
sex <- c("F", "F", "M", "M")
height <- c(160, 152, 170, 177)
weight <- c(48, 52, 65, 72)
df <- data.frame(name, sex, height, weight)
df$name %<>% as.character()
```

```R
> df
  name sex height weight
1 田中   F    160     48
2 佐藤   F    152     52
3 斉藤   M    170     65
4 本田   M    177     72
```

## 追加サンプル1（ベクトルによるサンプルの追加）

ここに金子さん（女性、身長148、体重42）と井上さん（男性、身長179）のデータを追加したい。
ただし、井上さんは極度の恥ずかしがり屋だったので体重が分からなかった。
2人の追加データは、サンプル単位（個人別）にベクトルで作成している。

```r
kaneko <- c("金子", "F", 148, 42)
inoue <-c("井上", "M", 179)
```

## 追加サンプル2（データフレームによるサンプルの追加）

さらに、石井さん、太田さん、藤田さんのデータも追加したい。
ただし、彼らの体重を聞くつもりが、誤ってウエストのデータを入手してしまったようだ。
3人の追加データは、一括してdata.frameとして作成している。

```r
name <- c("石井", "太田", "藤田")
sex <- c("M", "F", "M")
height <- c(165, 158, 185)
waist <- c(80, 67, 82)
df2 <- data.frame(name, sex, height, waist)
df2$name %<>% as.character()
```

```R
> df2
  name sex height waist
1 石井   M    165    80
2 太田   F    158    67
3 藤田   M    185    82
```

# 4. rbind 「とりあえずくっつけておいてあげる」

`rbind()`は、name属性のないベクトルの各要素を、data.frameに左詰めで順番通りに結合したい場合に有効です。
data.frame同士の結合にも使えますが、data.frame同士であれば後述の`dplyr::bind_rows()`のほうが有効です。

## 追加サンプル1（ベクトル）を結合

```R
> rbind(df, kaneko, inoue)
  name sex height weight
1 田中   F    160     48
2 佐藤   F    152     52
3 斉藤   M    170     65
4 本田   M    177     72
5 金子   F    148     42
6 井上   M    179   井上
```

結合するベクトルの各要素にはカラムが付されていないですが、`rbind`を使うと左詰めで結合できます。
ただし、`inoue`のように、結合先のdata.frameの列数とベクトル要素が一致していない場合、自動でNAを返してくれません。代わりに、data.frameの列数に合わせて、左詰めで繰り返し同じベクトルが結合されます。
こうなるのが嫌であれば、結合前のベクトル`inoue`を修正しなければなりません。

```R
> inoue2 <-c("井上", "M", 179, NA)
> rbind(df, kaneko, inoue2)
  name sex height weight
1 田中   F    160     48
2 佐藤   F    152     52
3 斉藤   M    170     65
4 本田   M    177     72
5 金子   F    148     42
6 井上   M    179   <NA>
```

## 追加サンプル2（data.frame）を結合

```R
> rbind(df, df2)
 match.names(clabs, names(xi)) でエラー:  名前が以前の名前と一致しません
```

`rbind()`を使ったdata.frame同士の結合の場合、列の順番は問題になりませんが、**カラム名、列数が完全に一致していないと結合してくれません**。
これが`rbind()`の欠点であり、ベクトルとの結合と違い、かなり使い勝手が悪いです。
例えば以下に用意した、カラム名、列数が完全に一致するような`df3`であれば列順を整理して結合されます。

```R
> name <- c("富岡", "武井")
> sex <- c("M", "F")
> height <- c(165, 154)
> weight <- c(59, 53)
> df3 <- data.frame(weight, height, sex, name)
> df3
  weight height sex name
1     59    165   M 富岡
2     53    154   F 武井


> rbind(df, df3)
  name sex height weight
1 田中   F    160     48
2 佐藤   F    152     52
3 斉藤   M    170     65
4 本田   M    177     72
5 富岡   M    165     59
6 武井   F    154     53
```

他にも、こちらで紹介されている方法もありますが、**「data.frame同士の結合」に関しては、基本的に後述の`dplyr::bind_rows()`の下位互換になっていると言わざるを得ない**と思います。
[列名の違いに関係なく列順で縦結合するユーザー関数(rbind拡張1)~三つ以上のデータフレーム対応 - 一所懸命に手抜きする](https://a-habakiri.hateblo.jp/entry/2016/11/06/234502)


# 5. dplyr::bind_rows() 「ちゃんと参照してくっつけておくわ」

`dplyr::bind_rows()`はデータのカラム名（names属性）が一致するように結合させたい場合に有効です。

## 追加サンプル1（ベクトル）を結合

```R
> dplyr::bind_rows(df, kaneko, inoue)
 エラー: Argument 2 must have names
```

`dplyr:bind_rows()`は、**names属性を「必ず」参照して結合させる**ので、~~要素ごとにカラムが与えられていないベクトルとの結合は不可能です。~~

**2020/01/22 修正**
基本的にそのまま結合させるのは不可能ですが、ベクトルの各要素にnames属性を与えることで結合可能になります。
names属性を付与するには、`names()`関数を用います。

```R
> kaneko2 <- kaneko
> names(kaneko2) <- c("name", "sex", "height", "weight")

> kaneko2
  name    sex height weight 
"金子"    "F"  "148"   "42" 

> rbind(df, kaneko2)
  name sex height weight
1 田中   F    160     48
2 佐藤   F    152     52
3 斉藤   M    170     65
4 本田   M    177     72
5 金子   F    148     42
```

ベクトルと結合したい場合は、上記のように**ベクトルの各要素にnames属性を「必ず」与える必要があります**。これにより、ベクトルが一行のdata.frameと擬制されるので、data.frameのカラム名と整合的な結合ができます。

もしくは、一旦data.frameに変換してから結合する方法もありますが、手数が多くなるので、それをやるなら**上記の方法か、もしくは初めからdata.frameで追加データを記録するほうがよっぽど楽です**。



## 追加サンプル2（data.frame）を結合

```R
> dplyr::bind_rows(df, df2)
  name sex height weight waist
1 田中   F    160     48    NA
2 佐藤   F    152     52    NA
3 斉藤   M    170     65    NA
4 本田   M    177     72    NA
5 石井   M    165     NA    80
6 太田   F    158     NA    67
7 藤田   M    185     NA    82
```

data.frame同士の場合、カラム名を参照してデータを結合し、欠損値`NA`も自動で与えられます。
`rbind()`では列名や列数が完全に一致していないと結合できませんでしたが、`dplyr::bind_rows()`の場合は一致している必要はないので、使い勝手が非常に良いです。

# 6. まとめ

行の結合について改めて結論をまとめると・・・
<B>
「names属性のないベクトルとdata.frameとの結合は`rbind()`」
「names属性のあるベクトルとdata.frameとの結合は`dplyr::bind_rows()`」
「data.frame同士の結合は`dplyr::bind_rows()`」
</B>

はいこれテストに出ますよ。

おしまい。


## 参考文献

- [R Source 26. names 属性と要素のラベル](http://cse.naro.affrc.go.jp/takezawa/r-tips/r/26.html)
- [列名の違いに関係なく列順で縦結合するユーザー関数(rbind拡張1)~三つ以上のデータフレーム対応 - 一所懸命に手抜きする](https://a-habakiri.hateblo.jp/entry/2016/11/06/234502)

## 主な更新履歴

- **2020/01/22**
    - 5. に記載した、`dplyr::bind_rows()`を用いたベクトル・data.frame間の結合について大幅修正。<br>`names()`関数を使えば結合可能であることが判明した。