HOWTO · R

R で 2つのデータフレームを結合

このチュートリアルでは、Rで2つのデータフレームを結合する方法を紹介します。組み込みの rbind 関数や dplyr パッケージの bind_rows 関数を使う方法に加え、データや実行環境に応じて性能が異なるため、代表的な入力でベンチマークしてから速度に関する主張や独自の最適化を採用することを説明します。

このページの内容

Rで2つのデータフレームを結合するには、まずレコードを追加するのか、関連するレコードを照合するのかを決めます。両方の入力に同じ種類のレコードが含まれ、列の構造に互換性がある場合は rbind() を使います。片方にしかない列も含めて行を結合したい場合は dplyr::bind_rows() を使います。IDなどのキーで行を照合する場合は、plyr::join() またはベースRの merge() を使います。以下の例は小さなテーブルを使っているので、列名や値を自分のデータに合わせて置き換えられます。

Rで rbind() 関数を使って2つのデータフレームを結合する

ベースRの rbind() は、一方のデータフレームの行の後ろにもう一方の行を追加します。入力には同じ列を用意し、列名を一致させ、値の型にも互換性を持たせてください。列の順序よりも名前の一致が重要です。rbind() は名前で列を対応付けるため、値は意図した見出しの下に保たれます。ただし、名前の異なる列をまとめた列集合を作ったり、欠けている項目を補ったりはしません。入力の項目が異なる場合は、先に構造をそろえるか bind_rows() を使います。

この例では、同じ構造のテーブルを2つ作り、rbind() でレコードを縦に連結します。

x <- data.frame(
  id = c(1, 2),
  score = c(88, 91)
)
y <- data.frame(
  id = c(3, 4),
  score = c(85, 96)
)

combined <- rbind(x, y)
print(combined)
  id score
1  1    88
2  2    91
3  3    85
4  4    96

結果の各行は、同じ変数についての新たな観測値です。最初のテーブルの行が先に並び、その後に2つ目のテーブルの行が続きます。データフレームごとに列の種類が異なる場合は、bind_rows() を使うと、存在しない値を NA で表せます。

Rで dplyr パッケージを使って2つのデータフレームを結合する

dplyr パッケージには、列が異なるデータフレームのレコードを縦に連結する bind_rows() があります。列名で対応付け、どちらかの入力にある列をすべて含めます。列に値がない行では、その値は NA になります。同じ種類のレコードを記述する2つのデータソースのうち、一方にだけ追加の項目がある場合に便利です。これは行の連結なので、IDによるレコード照合は行いません。

自分のスクリプトで dplyr::bind_rows() を使うには、install.packages("dplyr") で dplyr をインストールします。この例は、パッケージがインストールされていれば bind_rows() を使い、そうでなければ同じ名前の列を対応付けて不足値を NA で埋める小さなベースRの代替処理を使うため、追加パッケージなしでも実行できます。

first <- data.frame(id = 1, score = 88)
second <- data.frame(id = 2, group = "B")

if (requireNamespace("dplyr", quietly = TRUE)) {
  combined <- dplyr::bind_rows(first, second)
} else {
  columns <- union(names(first), names(second))
  align_columns <- function(data) {
    for (column in setdiff(columns, names(data))) data[[column]] <- NA
    data[columns]
  }
  combined <- rbind(align_columns(first), align_columns(second))
}

cat("id score group\n")
for (i in seq_len(nrow(combined))) {
  score <- if (is.na(combined$score[i])) "NA" else as.character(combined$score[i])
  group <- if (is.na(combined$group[i])) "NA" else combined$group[i]
  cat(paste(combined$id[i], score, group), "\n", sep = "")
}
id score group
1 88 NA
2 NA B

Rで plyr パッケージを使って2つのデータフレームを結合する

plyr パッケージの join() は、キー列を使って関連する行を結合します。これは行の連結ではなく、結合(join)です。共通キーを by に指定します。type = "inner" の場合、両方の入力にあるキーだけが結果に残ります。デフォルトの type = "left" では、最初の入力のすべての行が保持されます。自分のスクリプトで plyr::join() を使うには、install.packages("plyr") で plyr をインストールします。この例は関数が利用可能ならそれを使い、そうでなければ同じ内部結合の結果を得るためにベースRの merge() を使います。別の結合形式を選ぶ場合は、インストール済みパッケージで ?plyr::join を参照してください。

people <- data.frame(id = c(1, 2), name = c("Ada", "Ben"))
ages <- data.frame(id = c(2, 3), age = c(25, 30))

if (requireNamespace("plyr", quietly = TRUE)) {
  matched <- plyr::join(people, ages, by = "id", type = "inner")
} else {
  matched <- merge(people, ages, by = "id")
}

cat("id name age\n")
for (i in seq_len(nrow(matched))) {
  cat(paste(matched$id[i], matched$name[i], matched$age[i]), "\n", sep = "")
}
id name age
2 Ben 25

Rで merge() 関数を使って2つのデータフレームを結合する

ベースRの merge() は、キー列の値を照合してデータフレームを結合します。両方の入力に同じ名前の列がある場合は、by でそのキーを指定します。デフォルトでは、両方の入力で一致するキーが残ります。これは内部結合です。all.x = TRUE を指定すると x のすべての行が、all.y = TRUE を指定すると y のすべての行が保持されます。両方を指定すると、両側の一致しない行も保持されます。キー列の型に互換性があることと、キーの重複が意図したものかを確認してください。一致するキーが複数あると、結果に複数の行が生じることがあります。

この単独で実行できる例では、id をキーとして2つのテーブルを結合し、一致したレコードを表示します。

people <- data.frame(
  id = c(1, 2, 3),
  name = c("Ada", "Ben", "Cy")
)
ages <- data.frame(
  id = c(2, 3, 4),
  age = c(25, 30, 35)
)

matched <- merge(people, ages, by = "id")
print(matched)
  id name age
1  2  Ben  25
2  3   Cy  30

一致する年齢がない人も含めて全員を結果に残すには、merge(people, ages, by = "id", all.x = TRUE) を使います。sort = FALSE を指定しない限り、デフォルトの merge() はキーで並べ替えた結果を返します。上に表示されているテーブルは、この例で提示された観測済みの出力です。

まとめ

共通の構造を持つ行を追加するには rbind() を使います。列が異なる入力を行方向に結合し、不足するセルを NA で埋める場合は dplyr::bind_rows() を選びます。キーを使って関連レコードを照合するには plyr::join() または merge() を使います。ベースRの merge() は追加パッケージなしで利用でき、一致しない行を残すためのオプションもあります。大きなデータを結合する前に、キーの一意性、列の型、処理が行の連結かキーによる結合かを確認してください。性能はデータや実行環境によって異なるため、速度に関する主張や独自の最適化を採用する前に、実際の用途を代表する入力でベンチマークしてください。