1. はじめに
pandas
のmerge
の挙動についてよく忘れて何回も調べるはめになるのでまとめておく.(pandas
に限った話ではないが)
2.環境
- python 3.6.0
- pandas 0.23.4
3.merge
実際にどう処理されるかは別として,以下のように考えるとわかりやすい.
-
on
で指定した列からインデックス集合を作る. - インデックス集合の要素を一つとり,
on
で指定した列がその値をとる行を抽出. -
on
で指定した列以外の列の直積集合がmerge後のdatadrameの行として追加される. - 2,3をインデックス集合の全ての要素に対して繰り返す.
1のインデックス集合の作り方によって,inner
, outer
, left
, right
がある.
それ以降についてはどれも共通.
例となるdataframeを準備
import pandas as pd
left_df = pd.DataFrame({"idx": [1,1,1], "left_val": [100,101,102]})
right_df = pd.DataFrame({"idx": [1,1,1], "right_val": [103,104,105]})
# In [9]: left_df
# Out[9]:
# idx left_val
# 0 1 100
# 1 1 101
# 2 1 102
# In [10]: right_df
# Out[10]:
# idx right_val
# 0 1 103
# 1 1 104
# 2 1 105
とりあえず結果の確認
how = "inner"
merged_df = left_df.merge(right_df, on="idx", how=how)
# In [11]: merged_df
# Out[11]:
# idx left_val right_val
# 0 1 100 103
# 1 1 100 104
# 2 1 100 105
# 3 1 101 103
# 4 1 101 104
# 5 1 101 105
# 6 1 102 103
# 7 1 102 104
# 8 1 102 105
1.on
で指定した列からインデックス集合を作る.
on="idx"
とした時,インデックス集合は
set(left_df["idx"]) | set(right_df["idx"])
の部分集合となるように作る.
how
オプションごとに以下の評価式の右辺のようにインデックス集合を作る.
以下の評価は,全てTrue
となる.
# inner
set(merged_df["idx"]) == set(left_df["idx"]) & set(right_df["idx"])
# outer
set(merged_df["idx"]) == set(left_df["idx"]) | set(right_df["idx"])
# left
set(merged_df["idx"]) == set(left_df["idx"])
# right
set(merged_df["idx"]) == set(right_df["idx"])
2.インデックス集合の要素を一つとり,on
で指定した列がその値をとる行を抽出.
idxset = set(merged_df["idx"])
# In [20]: idxset
# Out[20]: {1}
idx = idxset.pop()
# idx = 1
left_df_idx = left_df.query(f"idx == {idx}")
right_df_idx = right_df.query(f"idx == {idx}")
# In [12]: left_df_idx
# Out[12]:
# idx left_val
# 0 1 100
# 1 1 101
# 2 1 102
# In [13]: right_df_idx
# Out[13]:
# idx right_val
# 0 1 103
# 1 1 104
# 2 1 105
ここで,left_df
またはright_df
に対応する行がない時,NaN
となる.
inner
の時は常にlen(left_df_idx) > 0
かつlen(right_df_idx) > 0
なので問題ないが,それ以外にした時はlen(left_df_idx) >= 0
かつlen(right_df_idx) >= 0
となるのであり得る.
3.on
で指定した列以外の列の直積集合がmerge後のdatadrameの行として追加される.
merged_df_idx = merged_df.query(f"idx == {idx}")
# In [23]: merged_df_idx
# Out[23]:
# idx left_val right_val
# 0 1 100 103
# 1 1 100 104
# 2 1 100 105
# 3 1 101 103
# 4 1 101 104
# 5 1 101 105
# 6 1 102 103
# 7 1 102 104
# 8 1 102 105
直積集合なので以下の関係が成り立つ.
len(merged_df_idx) == len(left_df_idx) * len(right_df_idx)
# 9 = 3*3
この例の場合,
{100, 101, 102} × {103, 104, 105} = {(100, 103), (100, 104), (100, 105),
(101, 103), (101, 104), (101, 105),
(102, 103), (102, 104), (102, 105)}
となっている.
4. 2,3をインデックス集合の全ての要素に対して繰り返す.
これを全てのインデックス集合に対して繰り返したdataframeが,merge後のdataframeになる.(この例だと1回で終了してるが)