1. 趣旨
rbind()
とdplyr::bind_rows()
は、どちらもデータフレームに行を結合する関数ですが、微妙に使い方が違うようなので、Tipsとして書き記しておきます。
今回は、ベースとなるdata.frameに、追加サンプルとしてベクトルや新たなdata.frameを結合するときを例に使い分けてみましょう。
2. 先に結論を言うと
基本的には
rbind()
はdata.frameとベクトルの結合に有効rbind()
はdata.frame同士の結合も可能だが、条件が超厳しいdplyr::bind_rows()
はdata.frame同士の結合に有効dplyr::bind_rows()
は、条件付きだがdata.frameとベクトルの結合にも使える。
という感じです。
3. サンプルデータ
ベースとなるデータフレーム
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()
> 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人の追加データは、サンプル単位(個人別)にベクトルで作成している。
kaneko <- c("金子", "F", 148, 42)
inoue <-c("井上", "M", 179)
追加サンプル2(データフレームによるサンプルの追加)
さらに、石井さん、太田さん、藤田さんのデータも追加したい。
ただし、彼らの体重を聞くつもりが、誤ってウエストのデータを入手してしまったようだ。
3人の追加データは、一括してdata.frameとして作成している。
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()
> 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(ベクトル)を結合
> 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
を修正しなければなりません。
> 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)を結合
> rbind(df, df2)
match.names(clabs, names(xi)) でエラー: 名前が以前の名前と一致しません
rbind()
を使ったdata.frame同士の結合の場合、列の順番は問題になりませんが、カラム名、列数が完全に一致していないと結合してくれません。
これがrbind()
の欠点であり、ベクトルとの結合と違い、かなり使い勝手が悪いです。
例えば以下に用意した、カラム名、列数が完全に一致するようなdf3
であれば列順を整理して結合されます。
> 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)~三つ以上のデータフレーム対応 - 一所懸命に手抜きする
5. dplyr::bind_rows() 「ちゃんと参照してくっつけておくわ」
dplyr::bind_rows()
はデータのカラム名(names属性)が一致するように結合させたい場合に有効です。
追加サンプル1(ベクトル)を結合
> dplyr::bind_rows(df, kaneko, inoue)
エラー: Argument 2 must have names
dplyr:bind_rows()
は、names属性を「必ず」参照して結合させるので、要素ごとにカラムが与えられていないベクトルとの結合は不可能です。
2020/01/22 修正
基本的にそのまま結合させるのは不可能ですが、ベクトルの各要素にnames属性を与えることで結合可能になります。
names属性を付与するには、names()
関数を用います。
> 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)を結合
> 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. まとめ
行の結合について改めて結論をまとめると・・・
「names属性のないベクトルとdata.frameとの結合はrbind()
」
「names属性のあるベクトルとdata.frameとの結合はdplyr::bind_rows()
」
「data.frame同士の結合はdplyr::bind_rows()
」
はいこれテストに出ますよ。
おしまい。
参考文献
主な更新履歴
-
2020/01/22
- 5. に記載した、
dplyr::bind_rows()
を用いたベクトル・data.frame間の結合について大幅修正。names()
関数を使えば結合可能であることが判明した。
- 5. に記載した、