R言語でデータフレームを内部結合(inner join)する方法について解説します。データサイエンスにおいては、基準となる列の値を元に2つのデータフレームを結合して新たな1つのデータフレームを作成する操作を頻繁に使います。このような操作は結合の仕方によりいくつか種類がありますが、ここではその中でも内部結合と呼ばれる結合の仕方についてお伝えします。
内部結合は、指定した条件に基づいてデータフレームを結合し、条件に一致する行のみを取得する方法です。主に、次のような特徴があります。

  • 指定した列の値が条件を満たす行のみを取得します
  • 指定した条件を満たさない行は除外されます
  • 結果のデータフレームの行数は、元のデータフレームの行数以下になります

内部結合を行うには、tidyverseパッケージに含まれているdplyrパッケージのinner_join()を使用します。

inner_join()の概要

inner_join()は、内部結合するための関数です。inner_join()は、2つのデータフレームでそれぞれ結合の対象となる列を指定し、それぞれの列に同じ値となる行を結合して、新たなデータフレームを取得します。結合の対象となる列の値が一致しない行は、結果に含まれないことに注意してください。

inner_join()の使い方

dplyr::inner_join()の使い方は次になります。


inner_join(
  x,
  y,
  by = NULL,
  copy = FALSE,
  suffix = c(".x", ".y"),
  ...,
  keep = NULL
)

## S3 method for class 'data.frame'
inner_join(
  x,
  y,
  by = NULL,
  copy = FALSE,
  suffix = c(".x", ".y"),
  ...,
  keep = NULL,
  na_matches = c("na", "never"),
  multiple = "all",
  unmatched = "drop",
  relationship = NULL
)

inner_join()の引数の意味

x

データフレームまたはデータフレーム拡張(例:tibble)、遅延データフレーム(例:dbplyrまたはdtplyrから)を指定します。

y

データフレームまたはデータフレーム拡張(例:tibble)、遅延データフレーム(例:dbplyrまたはdtplyrから)を指定します。

by = NULL

join_by()で作成された結合仕様または結合する変数の文字ベクトルを指定します。
NULLの場合は、xとyに共通するすべての変数を使用して自然結合を実行します。メッセージには変数がリストされるため、変数が正しいことを確認できます。明示的にbyを指定してメッセージを抑制します。
xとyの間の異なる変数を結合するには、join_by()を使用します。たとえば、join_by(a == b)はx$aとy$b に一致します。
複数の変数で結合するには、複数の式を持つjoin_by()を使用します。たとえば、join_by(a == b, c == d)はx$aとy$bに、x$cはy$d に一致します。xとyの間で列名が同じ場合は、join_by(a, c)のように変数名のみをリストすることで、これを短縮できます。
join_by()は、次のような不等式結合およびローリング結合、オーバーラップ結合を実行するためにも使用できます。

  • 等価結合(Equality joins): 1つ以上の列のペア間でキーが等しくなることが必要であり、最も一般的なタイプの結合です。join_by()を使用して等価結合を構築するには、結合する2つの列名を==で区切って指定します。1つの名前を指定すると、同じ名前の2つの列間の等価結合として解釈されます。たとえば、join_by(x)はjoin_by(x == x)と等価です。
  • 不等式結合(Inequality joins): >、>=、<、<= などの不等式の一致で結合します。時系列分析やゲノミクスで一般的です。join_by()を使用して不等式結合を作成するには、不等式のいずれかで区切られた2つの列名を指定します。
  • ローリング結合(Rolling joins): 不等式結合条件から返される結果を制限する不等式結合の一種です。これらは、完全一致がない場合に、最も近い一致を前後に「ローリング」する場合に便利です。ローリング結合を構築するには、不等式をclosest()でラップします。
    • closest(expr)
      • exprは、>、>=、<、<=のいずれかを含む不等式でなければなりません。たとえば、closest(x >= y) は「xの各値について、そのx値以下のyの最も近い値を見つける」と解釈されます。
      • closest()は、不等式がどのように指定されているかに関係なく、常に左側のデータフレームxを主として使用し、右側のデータフレームyを最も近い一致を見つけるデータフレームとして使用します。たとえば、closest(y$a >= x$b)は、常にclosest(x$b <= y$a)と解釈されます。
  • オーバーラップ結合(Overlap joins): 左側のデータフレームの1つまたは2つの列が、右側のデータフレームの2つの列で定義された範囲とオーバーラップする不等式結合の特殊な場合です。join_by()がオーバーラップ結合の構築を支援するために認識するヘルパーは3つあり、これらはすべて単純な不等式から構築できます。
    • between(x, y_lower, y_upper, …, bounds = “[]”)
      • xの各値について、値が[y_lower, y_upper]の間にあるすべての場所を検索します。デフォルトではx >= y_lower、x <= y_upperと同等です。
      • 境界は、閉区間”[]”、半開区間”[)”または”(]”、開区間”()” のいずれかで、下限と上限の包括性を変更できます。これにより、上記の不等式を構築するために>=または>と<= または<のどちらを使用するかが変わります。
      • ドットは将来の拡張用であり、空である必要があります。
    • within(x_lower, x_upper, y_lower, y_upper)
      • [x_lower, x_upper]の各範囲について、範囲が[y_lower, y_upper]に完全に収まるすべての場所が検索されます。x_lower >= y_lower、x_upper <= y_upperに相当します。
      • within() の構築に使用される不等式は、指定された範囲の包括性に関係なく同じです。
    • overlaps(x_lower, x_upper, y_lower, y_upper, …, bounds = “[]”)
      • [x_lower, x_upper]の各範囲について、範囲が任意の容量で[y_lower, y_upper]と重なるすべての場所が検索されます。デフォルトではx_lower <= y_upper、x_upper >= y_lowerに相当します。
      • 境界は、閉区間”[]”、半開区間”[)”または”(]”、開区間”()”のいずれかで、下限と上限の包括性を変更できます。”[]”は<=と>= を使用しますが、他の3つのオプションは<と>を使用し、まったく同じ不等式を生成します。
      • ドットは将来の拡張用であり、空である必要があります。
    • これらの条件は、範囲が整形式で空でない、つまり、境界が”[]”として扱われる場合はx_lower <= x_upper、それ以外の場合はx_lower < x_upperされることを前提としています。
  • 単純な等価結合の場合は、代わりに、結合する変数名の文字ベクトルを指定できます。たとえば、by = c(“a”, “b”)は、x$aをy$aに結合し、x$bをy$bに結合します。変数名がxとyで異なる場合は、by = c(“x_a” = “y_a”, “x_b” = “y_b”)のような名前付き文字ベクトルを使用します。
copy = FALSE

xとyが同じデータソースからのものではなく、copyがTRUEの場合、yはxと同じsrcにコピーされます。これにより、src間でデータフレームを結合できますが、コストがかかる可能性があるため、選択する必要があります。

suffix = c(“.x”, “.y”)

xとyに結合されていない重複列名がある場合、これらの接尾辞は出力に追加され、曖昧さが解消されます。長さ2の文字ベクトルである必要があります。

メソッドに渡されるその他のパラメーターです。

keep = NULL

xとyの両方の結合キーを出力に保持する必要がありますかどうかを指定します。

  • [デフォルト]NULLの場合: 等価結合は x のキーのみを保持しますが、不等価結合は両方の入力のキーを保持します。
  • TRUEの場合: 両方の入力のすべてのキーが保持されます。
  • FALSEの場合: xのキーのみが保持されます。右結合と完全結合の場合、yにのみ存在する行に対応するキー列のデータは、xのキー列にマージされます。不等式条件で結合する場合は使用できません。
na_matches = c(“na”, “never”)

2つのNA値または2つのNaN値を一致させる必要があるかどうかを指定します。

  • [デフォルト]na: %in%、match()、merge()のように、2つのNAまたは2つのNaN値を等しいものとして扱います。
  • never: 2つのNA値または2つのNaN値を異なる値として扱い、それらを一致させたり、他の値と一致させたりすることはありません。
multiple = “all”

xの行とyの複数の一致の処理を指定します。
xの各行について、次のようになります。

  • [デフォルト]all: yで検出されたすべての一致を返します。これはSQLと同じ動作です。
  • any: yで検出された1つの一致を返しますが、どの一致が返されるかは保証されません。多くの場合、少なくとも1つの一致があるかどうかを検出する必要がある場合は、「first」と 「last」よりも高速です。
  • first: yで検出された最初の一致を返します。
  • last: yで最後に検出された一致を返します。
unmatched = “drop”

行がドロップされる原因となる一致しないキーはどのように処理するかを指定します。

  • [デフォルト]drop: 一致しないキーを結果から削除します。
  • error: 一致しないキーが検出された場合にエラーをスローします。

unmatchedは、結合中に誤って元データが削除されるのを防ぐことを目的としています。入力内の一致しないキーのみをチェックし、行をドロップする可能性があります。

  • 左外部結合(left join)の場合は、y をチェックします。
  • 右外部結合(right join)の場合は、x がチェックされます。
  • 内部結合(inner join)の場合は、xとyの両方がチェックされます。この場合、unmatchedは、xとyの動作を個別に指定するために、長さ2の文字ベクトルにすることもできます。
relationship = NULL

xとyのキー間の予期される関係の処理を指定します。
以下のリストから選択された期待値が無効になると、エラーがスローされます。

  • [デフォルト]NULL: xとyの間に関係があることを想定していません。ただし、等価結合の場合、多対多のリレーションシップ(通常は予期しない)がチェックされ、発生した場合は警告が表示されるため、入力を詳しく調べるか、”many-to-many””を指定してこのリレーションシップを明示的にすることをお勧めします。
  • one-to-one: xの各行はyの最大1行と一致し、yの各行はxの最大1行と一致します。
  • one-to-many: yの各行はxの最大1行と一致します。
  • many-to-one: xの各行はyの最大1行と一致します。
  • many-to-many: リレーションシップのチェックは行われませんが、このリレーションシップが存在することがわかっている場合に、このリレーションシップについて明示的に指定できるようにするために提供されています。

リレーションシップでは、一致が0の場合は処理されません。それについては、unmatchedを参照してください。

準備

あらかじめ、tidyverseパッケージを読み込んでおきます。


library(tidyverse)

解説のために、次のデータフレームを準備します。


df_l <- data.frame(
    ID1 = seq(1, 7),
    L1 = seq(1, 7),
    ID2 = rep(TRUE, 7),
    L2 = rep(TRUE, 7),
    L3 = paste(letters[1:7], seq(1, 7), sep = "_")
  )
df_r <- data.frame(
    ID1 = seq(3, 9),
    R1 = seq(3, 9),
    ID2 = c(F, F, F, T, T, T, F),
    R2 = c(F, F, F, T, T, T, F),
    R3 = paste(LETTERS[3:9], seq(3, 9), sep = "_")
  )

df_l

  ID1 L1  ID2   L2  L3
1   1  1 TRUE TRUE a_1
2   2  2 TRUE TRUE b_2
3   3  3 TRUE TRUE c_3
4   4  4 TRUE TRUE d_4
5   5  5 TRUE TRUE e_5
6   6  6 TRUE TRUE f_6
7   7  7 TRUE TRUE g_7

df_r

  ID1 R1   ID2    R2  R3
1   3  3 FALSE FALSE C_3
2   4  4 FALSE FALSE D_4
3   5  5 FALSE FALSE E_5
4   6  6  TRUE  TRUE F_6
5   7  7  TRUE  TRUE G_7
6   8  8  TRUE  TRUE H_8
7   9  9 FALSE FALSE I_9

1つの列で列名が同じ場合の内部結合

データフレームdf_lとdf_rに対して、df_lの列ID1とdf_rの列ID1を基準として内部結合するには次のようにします。


df_l |>
  inner_join(df_r, by = join_by(ID1))

  ID1 L1 ID2.x   L2  L3 R1 ID2.y    R2  R3
1   3  3  TRUE TRUE c_3  3 FALSE FALSE C_3
2   4  4  TRUE TRUE d_4  4 FALSE FALSE D_4
3   5  5  TRUE TRUE e_5  5 FALSE FALSE E_5
4   6  6  TRUE TRUE f_6  6  TRUE  TRUE F_6
5   7  7  TRUE TRUE g_7  7  TRUE  TRUE G_7

1つの列で列名が異なる場合の内部結合

データフレームdf_lとdf_rに対して、df_lの列L1とdf_rの列R1を基準として内部結合するには次のようにします。


df_l |>
  inner_join(df_r, by = join_by(L1 == R1))

  ID1.x L1 ID2.x   L2  L3 ID1.y ID2.y    R2  R3
1     3  3  TRUE TRUE c_3     3 FALSE FALSE C_3
2     4  4  TRUE TRUE d_4     4 FALSE FALSE D_4
3     5  5  TRUE TRUE e_5     5 FALSE FALSE E_5
4     6  6  TRUE TRUE f_6     6  TRUE  TRUE F_6
5     7  7  TRUE TRUE g_7     7  TRUE  TRUE G_7

複数の列で列名が同じ場合の内部結合

データフレームdf_lとdf_rに対して、df_lの列ID1とdf_rの列ID1およびdf_lの列ID2とdf_rの列ID2を基準として内部結合するには次のようにします。


df_l |>
  inner_join(df_r, by = join_by(ID1, ID2))

  ID1 L1  ID2   L2  L3 R1   R2  R3
1   6  6 TRUE TRUE f_6  6 TRUE F_6
2   7  7 TRUE TRUE g_7  7 TRUE G_7

複数の列で列名が異なる場合の内部結合

データフレームdf_lとdf_rに対して、df_lの列L1とdf_rの列R1およびdf_lの列L2とdf_rの列R2を基準として内部結合するには次のようにします。


df_l |>
  inner_join(df_r, by = join_by(L1 == R1, L2 == R2))

  ID1.x L1 ID2.x   L2  L3 ID1.y ID2.y  R3
1     6  6  TRUE TRUE f_6     6  TRUE F_6
2     7  7  TRUE TRUE g_7     7  TRUE G_7
R入門 データフレームを内部結合(inner join)する方法