今回はPandasのDataFrameをマージ・結合する方法について解説していきます。
この記事で使用するデータはこちらに格納してありますので事前にダウンロードしておいてください。
また、DataFrameの作成方法やCSVデータの読み込み方法についてはこちらの記事で解説していますので、わからない方は事前に確認しておいてください。

目次
DataFrame同士を縦に結合
まずは結合するデータを読み込みましょう。今回のサンプルデータは各CSVとも「name(名前)」「age(年齢)」「sex(性別)」「birthday(生年月日)」のカラムを持つ10行のデータになります。
user_group_a = pd.read_csv('user_group_a.csv')
user_group_b = pd.read_csv('user_group_b.csv')
user_group_a
name | age | sex | birthday | |
---|---|---|---|---|
0 | 及川 | 22 | 男 | 1998/5/25 |
1 | 上島 | 32 | 女 | 1987/12/19 |
2 | 小塚 | 41 | 男 | 1979/3/3 |
3 | 吉村 | 22 | 男 | 1998/7/31 |
4 | 川越 | 33 | 男 | 1986/11/13 |
user_group_b
name | age | sex | birthday | |
---|---|---|---|---|
0 | 品川 | 37 | 男 | 1982/12/5 |
1 | 千野 | 52 | 女 | 1967/9/28 |
2 | 北野 | 59 | 女 | 1961/5/5 |
3 | 今西 | 24 | 男 | 1996/3/7 |
4 | 金 | 36 | 女 | 1983/11/30 |
上記の2つのDataFrameを縦に結合してみましょう。DataFrameを縦に結合するにはconcat関数を使います。concat関数には結合したいDataFrameをリストにしたものを引数に入れます。
users = pd.concat([user_group_a, user_group_b])
name | age | sex | birthday | |
---|---|---|---|---|
0 | 及川 | 22 | 男 | 1998/5/25 |
1 | 上島 | 32 | 女 | 1987/12/19 |
2 | 小塚 | 41 | 男 | 1979/3/3 |
3 | 吉村 | 22 | 男 | 1998/7/31 |
4 | 川越 | 33 | 男 | 1986/11/13 |
0 | 品川 | 37 | 男 | 1982/12/5 |
1 | 千野 | 52 | 女 | 1967/9/28 |
2 | 北野 | 59 | 女 | 1961/5/5 |
3 | 今西 | 24 | 男 | 1996/3/7 |
4 | 金 | 36 | 女 | 1983/11/30 |
これで簡単にDataFrameを結合することができました。ただ、これだと見ての通り、Index(行番号)が重複してしまっているため、後々の処理で都合が悪いです。結合時にIndex(行番号)を再度割り振るにはignore_indexという引数を渡します。
users = pd.concat([user_group_a, user_group_b], ignore_index=True)
name | age | sex | birthday | |
---|---|---|---|---|
0 | 及川 | 22 | 男 | 1998/5/25 |
1 | 上島 | 32 | 女 | 1987/12/19 |
2 | 小塚 | 41 | 男 | 1979/3/3 |
3 | 吉村 | 22 | 男 | 1998/7/31 |
4 | 川越 | 33 | 男 | 1986/11/13 |
5 | 品川 | 37 | 男 | 1982/12/5 |
6 | 千野 | 52 | 女 | 1967/9/28 |
7 | 北野 | 59 | 女 | 1961/5/5 |
8 | 今西 | 24 | 男 | 1996/3/7 |
9 | 金 | 36 | 女 | 1983/11/30 |
- DataFrameを縦に結合するには、DataFrameのリストをconcat関数に入れる
- 結合時にIndexを付け替えるにはignore_index=Trueを引数に指定する。(デフォルトではignore_index=False)
階層インデックス(MultiIndex)
階層インデックスとは、その名の通り、インデックスを階層的に管理することができるようになります。
上記の例ではインデックスが0~9といったような、連続する行番号的な意味を持ったインデックスになっています。これの上位階層として、各グループに対してインデックスを付与することが可能です。まずは例をみてみてください。
users = pd.concat([user_group_a, user_group_b], keys=['group_a', 'group_b'])
name | age | sex | birthday | ||
---|---|---|---|---|---|
group_a | 0 | 及川 | 22 | 男 | 1998/5/25 |
1 | 上島 | 32 | 女 | 1987/12/19 | |
2 | 小塚 | 41 | 男 | 1979/3/3 | |
3 | 吉村 | 22 | 男 | 1998/7/31 | |
4 | 川越 | 33 | 男 | 1986/11/13 | |
group_b | 0 | 品川 | 37 | 男 | 1982/12/5 |
1 | 千野 | 52 | 女 | 1967/9/28 | |
2 | 北野 | 59 | 女 | 1961/5/5 | |
3 | 今西 | 24 | 男 | 1996/3/7 | |
4 | 金 | 36 | 女 | 1983/11/30 |
contan関数にkeysという引数を指定することで、各DataFrameごとにインデックスを付与することができます。こうすることで、DataFrameを結合しつつ、元のユーザーグループがどれに当てはまるのかがわかるようになります。
例えば、DataFrameを結合後にuser_group_bだけを抽出したい場合は下記のようにします。
users.loc['group_b']
name | age | sex | birthday | |
---|---|---|---|---|
0 | 品川 | 37 | 男 | 1982/12/5 |
1 | 千野 | 52 | 女 | 1967/9/28 |
2 | 北野 | 59 | 女 | 1961/5/5 |
3 | 今西 | 24 | 男 | 1996/3/7 |
4 | 金 | 36 | 女 | 1983/11/30 |
結合したusersというDataFrameから「loc[‘group_b’]」と指定することでuser_gruop_bのデータを抽出することができました。結構便利なので覚えておいて損はないと思います。
appendによる結合
縦に結合する方法のもう一つにappned関数があります。こちらでもconcat同様にDataFrameを結合することができます。
users = user_group_a.append(user_group_b)
name | age | sex | birthday | |
---|---|---|---|---|
0 | 及川 | 22 | 男 | 1998/5/25 |
1 | 上島 | 32 | 女 | 1987/12/19 |
2 | 小塚 | 41 | 男 | 1979/3/3 |
3 | 吉村 | 22 | 男 | 1998/7/31 |
4 | 川越 | 33 | 男 | 1986/11/13 |
0 | 品川 | 37 | 男 | 1982/12/5 |
1 | 千野 | 52 | 女 | 1967/9/28 |
2 | 北野 | 59 | 女 | 1961/5/5 |
3 | 今西 | 24 | 男 | 1996/3/7 |
4 | 金 | 36 | 女 | 1983/11/30 |
DataFrameを横に結合
次はDataFrameを横に結合する方法について解説していきます。
今度は生徒の一覧のDataFrame(students)に対して、点数表(points)のDataFrameを紐付けてみましょう。
students = pd.DataFrame({
'name': ['品川', '千野', '北野', '今西', '金'],
'sex': ['男', '女', '女', '男', '女']
})
name | sex | |
---|---|---|
0 | 品川 | 男 |
1 | 千野 | 女 |
2 | 北野 | 女 |
3 | 今西 | 男 |
4 | 金 | 女 |
points = pd.DataFrame({
'name': ['千野', '北野', '山田'],
'point': [40, 80, 60]
}, index=[1, 2, 5])
name | point | |
---|---|---|
1 | 千野 | 40 |
2 | 北野 | 80 |
5 | 山田 | 60 |
こちらのデータを使って「外部結合」と「内部結合」のやり方について解説していきます。
DataFrameの外部結合
外部結合は2つのDataFrameを同じインデックスをもとに結合する方法の一つで、インデックスの値が一致するものと、一致しないものの両方を結合することができます。SQLを書いたことがある場合、「OUTER JOIN」がこれに該当します。
result = pd.concat([students, points], axis=1, join='outer')
name | sex | name | point | |
---|---|---|---|---|
0 | 品川 | 男 | NaN | NaN |
1 | 千野 | 女 | 千野 | 40.0 |
2 | 北野 | 女 | 北野 | 80.0 |
3 | 今西 | 男 | NaN | NaN |
4 | 金 | 女 | NaN | NaN |
5 | NaN | NaN | 山田 | 60.0 |
- axis=1を指定することで横に結合することができます。デフォルトはaxis=0となっており、縦の結合となります。
- 外部結合をする場合は、join=’outer’と指定します。
- pointsのDataFrameにはインデックス1, 2, 5のデータが存在しており、それぞれインデックスに対応したstudentsのDataFrameにデータが結合されていることがわかります。pointsのDataFrameに存在しない場合は、NaNで補完されます。
内部結合
次は内部結合のやり方について解説します。
外部結合はインデックスが一致しないものも出力するのに対し、内部結合はインデックスが一致するものだけの結果を取得することができます。SQLでいうところの「INNER JOIN」になります。
result = pd.concat([students, points], axis=1, join='inner')
name | sex | name | point | |
---|---|---|---|---|
1 | 千野 | 女 | 千野 | 40 |
2 | 北野 | 女 | 北野 | 80 |
- 内部結合をする場合は、join=’inner’と指定します。
- students, pointsの各DataFrameに存在するインデックスのデータのみ結果を取得することができます。
merge関数を使った結合
最後にmerge関数の使い方について解説していきます。
concatの結合はインデックスをキーに同様のインデックス持ったデータ同士を結合するのに対し、merge関数はDataFrameの指定したカラムをキーに結合することができます。よりSQLライクにDataFrame同士を結合することができます。今度は下記のCSVを読み込んでみてください。
students = pd.read_csv('students.csv')
id | name | sex | |
---|---|---|---|
0 | 1 | 詩織 | 女 |
1 | 2 | 楓華 | 女 |
2 | 3 | 雅也 | 男 |
3 | 4 | 比呂 | 男 |
4 | 5 | 梨乃 | 女 |
points = pd.read_csv('points.csv')
id | point | |
---|---|---|
0 | 2 | 40 |
1 | 3 | 80 |
2 | 6 | 60 |
外部結合
pd.merge(students, points, how='outer', on=['id'])
id | name | sex | point | |
---|---|---|---|---|
0 | 1 | 詩織 | 女 | NaN |
1 | 2 | 楓華 | 女 | 40.0 |
2 | 3 | 雅也 | 男 | 80.0 |
3 | 4 | 比呂 | 男 | NaN |
4 | 5 | 梨乃 | 女 | NaN |
5 | 6 | NaN | NaN | 60.0 |
- 外部結合をするにはhow=’outer’と指定します。
- onの引数に結合するためのキーを指定します。複数キーがある場合は複数指定してください。
内部結合
pd.merge(students, points, how='inner', on=['id'])
id | name | sex | point | |
---|---|---|---|---|
0 | 2 | 楓華 | 女 | 40 |
1 | 3 | 雅也 | 男 | 80 |
- 内部結合をするにはhow=’outer’と指定します。
ちなみに上記のmerge関数をSQLで表すと下記のようになります。
SELECT students.*, points.point FROM students INNER JOIN points ON students.id = points.id;