Python で解析 9

“Advent Calendar 2013 - Python で解析!” の九日目。DataFrame - 6

1. お決まりの準備を…

今回は DataFrame のマージについて。マージとは、二つのデータフレームを結合することで、SQL で言うところの join と同等のもので、数学的には集合演算とかベン図とか…を思い出してもらえればいいだろう。さて、その前にいつものように準備を。

In [1]: import pandas as pd

In [2]: df1 = pd.DataFrame({
   ...: u'名前': [u'山田', u'鈴木', u'佐藤', u'木村'],
   ...: u'性別': [u'男', u'男', u'女', u'女'] ,
   ...: u'身長': [181, 173, 159, 164],
   ...: u'体重': [79, 71, 51, 52]
   ...: })

In [3]: df2 = pd.DataFrame({
   ...: u'名前': [u'田中', u'山田', u'鈴木', u'佐藤'],
   ...: u'性別': [u'男', u'女', u'男', u'女'] ,
   ...: u'誕生日': ['1981/01/01', '1982/02/02', '1983/03/03', '1984/04/04']
   ...: })

In [4]: df1
Out[4]:
   体重  名前 性別   身長
0  79  山田  男  181
1  71  鈴木  男  173
2  51  佐藤  女  159
3  52  木村  女  164

In [5]: df2
Out[5]:
   名前 性別         誕生日
0  田中  男  1981/01/01
1  山田  女  1982/02/02
2  鈴木  男  1983/03/03
3  佐藤  女  1984/04/04

今回はマージを取り扱うので、データは二つ用意する。誕生日のデータの方には、田中くんが増えていて、山田さんが女性になっていて、木村さんがいないという違いがある。そして、データの順番が一致していない。マージの振る舞いを確認するために、わざと少しばかりややこしくしている。

1. 何も考えずにマージ

In [6]: pd.merge(df1, df2)
Out[6]:
   体重  名前 性別   身長         誕生日
0  71  鈴木  男  173  1983/03/03
1  51  佐藤  女  159  1984/04/04

件数が少なくなってしまったが、表示されている各数値に間違いはないので、どうやらマージはできたようだ。

2. キーカラムを指定してマージ

Pandas の merge は、指定したそれぞれの DataFrame の同じカラム名をキーにしてマージする。なので、実は次のようにカラム名を指定したのと同じことなのだ。

In [7]: pd.merge(df1, df2, on=[u'名前', u'性別'])
Out[7]:
   体重  名前 性別   身長         誕生日
0  71  鈴木  男  173  1983/03/03
1  51  佐藤  女  159  1984/04/04

SQL もこのぐらい便宜を計らってほしいものだが…というのはさておき…。

試しに性別がキーに含まれないようにして、名前だけでマージすると次のようになる。

In [8]: pd.merge(df1, df2, on=u'名前')
Out[8]:
   体重  名前 性別_x   身長 性別_y         誕生日
0  79  山田    男  1811982/02/02
1  71  鈴木    男  1731983/03/03
2  51  佐藤    女  1591984/04/04

性別が一致しなくても、名前だけで一致すればいいので、山田氏のレコードが増えた。その一方で、性別項目が二つできている。性別のサフィックスの '_x' が左に書いた df1 のもので、'_y' が右に書いた df2 のもの。

3. レコード数の差異を制御

df1 と df2 が完全に一致してないので、それをどう取り扱うかを制御してみる。SQL で言うところの left join とか outer join とか…のことだ。

最初の例では 2 件になってしまったが、それは両方に存在するレコードのみが抽出されたからで、以下と同等。

In [9]: pd.merge(df1, df2, how='inner')
Out[9]:
   体重  名前 性別   身長         誕生日
0  71  鈴木  男  173  1983/03/03
1  51  佐藤  女  159  1984/04/04

左側 (すなわち df1) さえあれば、右側 (すなわち df2) がなくてもかまわないなら、次のように書く。

In [10]: pd.merge(df1, df2, how='left')
Out[10]:
   体重  名前 性別   身長         誕生日
0  79  山田  男  181         NaN
1  71  鈴木  男  173  1983/03/03
2  51  佐藤  女  159  1984/04/04
3  52  木村  女  164         NaN

逆に左側がなくても、右側さえあれば良いなら、次のように書く。

In [11]: pd.merge(df1, df2, how='right')
Out[11]:
   体重  名前 性別   身長         誕生日
0  71  鈴木  男  173  1983/03/03
1  51  佐藤  女  159  1984/04/04
2 NaN  田中  男  NaN  1981/01/01
3 NaN  山田  女  NaN  1982/02/02

どちらか片方にさえあれば有効とするなら、how に 'outer' を指定する。

In [12]: pd.merge(df1, df2, how='outer')
Out[12]:
   体重  名前 性別   身長         誕生日
0  79  山田  男  181         NaN
1  71  鈴木  男  173  1983/03/03
2  51  佐藤  女  159  1984/04/04
3  52  木村  女  164         NaN
4 NaN  田中  男  NaN  1981/01/01
5 NaN  山田  女  NaN  1982/02/02

これが最大件数になる。

いずれの例でも同じく、データが存在しないところは NaN (Not a Number) となる。

なお、ここまでの例で 'pd.mege()' としているが、'df1.merge(df2)' のように書くことともできる。今回の説明の on や how の意味も同じように使える。'df1.merge(df2)' の方がオブジェクト指向らしい書き方なのだが、軽く調べた限りではマイノリティーな感じ。データマイニングする人は、オブジェクト指向にこだわらない傾向があるのかもしれない。

今回はこんなところで。