PandasでDataFrameをマージする方法 – merge(), join(), append(), concat() and update()

Pandas は DataFrame のマージを含む、データを操作するための膨大なメソッドや関数を提供しています。

DataFrame をマージすることで、元のデータソースを変更することなく新しい DataFrame を作成したり、元のデータソースを変更したりすることができます。

SQLやそれに類する表形式のデータに慣れている人なら、DataFrameを結合して新しいDataFrameを作る「join」という言葉を知っていることでしょう。

初心者の場合、結合の種類(内側、外側、左、右)を完全に把握するのは難しいかもしれません。

このチュートリアルでは、例を使って結合の種類を説明します。

主な内容は merge()concat() 関数の使用法です。

しかし、できるだけ多くの実用的な選択肢を提供するために、他のマージ方法についても説明します。

このチュートリアルでは、Pandas バージョン 1.1.4 と NumPy バージョン 1.19.4 を使用しています。

merge() を使ってデータフレームをマージする

まず最初に、このチュートリアルの残りの部分で使用する DataFrame をセットアップしましょう。

df1` には、名前、メールアドレス、ID を含む架空のユーザーリストが含まれます。

import pandas as pd


df1 = pd.DataFrame({'user_id': ['id001', 'id002', 'id003', 'id004', 'id005', 'id006', 'id007'],
                    'first_name': ['Rivi', 'Wynnie', 'Kristos', 'Madalyn', 'Tobe', 'Regan', 'Kristin'],
                    'last_name': ['Valti', 'McMurty', 'Ivanets', 'Max', 'Riddich', 'Huyghe', 'Illis'],
                    'email': ['rvalti0@example.com', 'wmcmurty1@example.com', 'kivanets2@example.com',
                              'mmax3@example.com', 'triddich4@example.com', 'rhuyghe@example.com', 'killis4@example.com']
                    })


データベースを設計するとき、プロファイルの設定(背景色、アバター画像のリンク、フォントサイズなど)をユーザーデータ(メール、追加日など)とは別のテーブルに保存するのがよい習慣とされています。

これらのテーブルは、一対一のリレーションシップを持つことができます。

このシナリオをシミュレートするために、画像の URL とユーザー ID を含む df2 を作成して、同じことを行ってみます。



df2 = pd.DataFrame({'user_id': ['id001', 'id002', 'id003', 'id004', 'id005'],
                    'image_url': ['http://example.com/img/id001.png', 'http://example.com/img/id002.jpg',
                                  'http://example.com/img/id003.bmp', 'http://example.com/img/id004.jpg',
                                  'http://example.com/img/id005.png']
                    })


DataFrameはこのようになります。

# df1
  user_id first_name last_name                  email
0   id001       Rivi     Valti    rvalti0@example.com
1   id002     Wynnie   McMurty  wmcmurty1@example.com
2   id003    Kristos   Ivanets  kivanets2@example.com
3   id004    Madalyn       Max      mmax3@example.com
4   id005       Tobe   Riddich  triddich4@example.com
5   id006      Regan    Huyghe    rhuyghe@example.com
6   id007    Kristin     Illis    killis4@example.com


#df2
  user_id                         image_url
0   id001  http://example.com/img/id001.png
1   id002  http://example.com/img/id002.jpg
2   id003  http://example.com/img/id003.bmp
3   id004  http://example.com/img/id004.jpg
4   id005  http://example.com/img/id005.png


これらのDataFrameを merge() 関数で結合してみましょう。

まず、この関数が受け取ることができるオプションの一覧をご覧ください。

pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,
         left_index=False, right_index=False, sort=True,
         suffixes=('_x', '_y'), copy=True, indicator=False,
         validate=None)


これらのオプションのほとんどはデフォルト値を持ちますが、left と right は例外です。

この2つのパラメータは、これからマージするDataFrameの名前です。

この関数自体は新しいDataFrameを返すので、それを変数 df3_merged に格納します。

Pythonシェルに以下のコードを入力してください。

df3_merged = pd.merge(df1, df2)


両方の DataFrame が同じ名前の user_id カラムを持っているので、 merge() 関数は自動的にそのキーに一致する 2 つのテーブルを結合します。

もし、異なる名前のカラムが2つあれば、 left_on='left_column_name'right_on='right_column_name' を使って、両方の DataFrame で明示的にキーを指定することができます。

それでは、変数 df3_merged を出力して、その中身を見てみましょう。

  user_id first_name last_name                  email                         image_url
0   id001       Rivi     Valti    rvalti0@example.com  http://example.com/img/id001.png
1   id002     Wynnie   McMurty  wmcmurty1@example.com  http://example.com/img/id002.jpg
2   id003    Kristos   Ivanets  kivanets2@example.com  http://example.com/img/id003.bmp
3   id004    Madalyn       Max      mmax3@example.com  http://example.com/img/id004.jpg
4   id005       Tobe   Riddich  triddich4@example.com  http://example.com/img/id005.png


元の df1 には 7 行あったのに、df3_merged には 5 行しかないことに気がつくと思います。

howパラメータのデフォルト値がinnerに設定されている場合、新しい DataFrame は左と右の DataFrame の交点から生成されます。

したがって、どちらかのテーブルでuser_id` が欠落していると、マージされたDataFrameには含まれない。

これは、左右の行の位置が入れ替わっても同じです。

df3_merged = pd.merge(df2, df1)


結果は、やはり

  user_id                         image_url first_name last_name                  email
0   id001  http://example.com/img/id001.png       Rivi     Valti    rvalti0@example.com
1   id002  http://example.com/img/id002.jpg     Wynnie   McMurty  wmcmurty1@example.com
2   id003  http://example.com/img/id003.bmp    Kristos   Ivanets  kivanets2@example.com
3   id004  http://example.com/img/id004.jpg    Madalyn       Max      mmax3@example.com
4   id005  http://example.com/img/id005.png       Tobe   Riddich  triddich4@example.com


IDが 'id006''id007' のユーザーは、両方のテーブルで交差していないので、マージされたDataFrameの一部ではありません。

しかし、どちらかのDataFrameをメインDataFrameとして使用し、たとえすべての行が互いに交差していなくても、その行をすべて含めたい場合があります。

つまり、image_urlはオプションとして、すべてのユーザーを表示させたいということです。

どうやって?merge()を使うことで、howパラメータに‘left’` という引数を渡すことができます。

df_left_merge = pd.merge(df1, df2, how='left')


print(df_left_merge)


左結合では、左側のDataFrame (df1) のすべての要素と右側のDataFrame (df2) のすべての要素を取り込みました。

上記のコードを実行すると、このように表示されます。

  user_id first_name last_name                  email                         image_url
0   id001       Rivi     Valti    rvalti0@example.com  http://example.com/img/id001.png
1   id002     Wynnie   McMurty  wmcmurty1@example.com  http://example.com/img/id002.jpg
2   id003    Kristos   Ivanets  kivanets2@example.com  http://example.com/img/id003.bmp
3   id004    Madalyn       Max      mmax3@example.com  http://example.com/img/id004.jpg
4   id005       Tobe   Riddich  triddich4@example.com  http://example.com/img/id005.png
5   id006      Regan    Huyghe    rhuyghe@example.com                               NaN
6   id007    Kristin     Illis    killis4@example.com                               NaN


左のDataFrameと一致する値がないセルは、NaNで埋め尽くされています。

右結合を試してみましょう。

次のようなマージされたDataFrameを作成します。

df_right_merge = pd.merge(df1, df2, how='right')


print(df_right_merge)


予想通り、右結合は左のDataFrameから右のDataFrameにマッチする全ての値を返します。

  user_id first_name last_name                  email                         image_url
0   id001       Rivi     Valti    rvalti0@example.com  http://example.com/img/id001.png
1   id002     Wynnie   McMurty  wmcmurty1@example.com  http://example.com/img/id002.jpg
2   id003    Kristos   Ivanets  kivanets2@example.com  http://example.com/img/id003.bmp
3   id004    Madalyn       Max      mmax3@example.com  http://example.com/img/id004.jpg
4   id005       Tobe   Riddich  triddich4@example.com  http://example.com/img/id005.png


df2のすべての行はdf1の値を持つので、この場合、右結合はinner`結合と似ています。

外側joinについて見てみましょう。

左結合と外側結合の両方を行うために、DataFrameの場所を入れ替えて、2つの新しい変数を作成しましょう。

df_left = pd.merge(df2, df1, how='left', indicator=True)
df_outer = pd.merge(df2, df1, how='outer', indicator=True)


print(df_left)
print(df_outer)


左側のデータフレームは df2 で、右側のデータフレームは df1 であることを覚えておいてください。

how=’outer’` を使用すると、キーに一致するDataFrameがマージされますが、欠落している値や一致しない値も含まれます。

また、 indicator フラグを追加して True に設定すると、Pandas は DataFrame の末尾に _merge というカラムを追加してくれます。

この列は、行が左、右、または両方のDataFrameで見つかったかどうかを教えてくれます。

df_left` 変数は以下のようなものです。

  user_id                         image_url first_name last_name                  email _merge
0   id001  http://example.com/img/id001.png       Rivi     Valti    rvalti0@example.com   both
1   id002  http://example.com/img/id002.jpg     Wynnie   McMurty  wmcmurty1@example.com   both
2   id003  http://example.com/img/id003.bmp    Kristos   Ivanets  kivanets2@example.com   both
3   id004  http://example.com/img/id004.jpg    Madalyn       Max      mmax3@example.com   both
4   id005  http://example.com/img/id005.png       Tobe   Riddich  triddich4@example.com   both


しかし、df_outerはこのようなデータになっています。

  user_id                         image_url first_name last_name                  email      _merge
0   id001  http://example.com/img/id001.png       Rivi     Valti    rvalti0@example.com        both
1   id002  http://example.com/img/id002.jpg     Wynnie   McMurty  wmcmurty1@example.com        both
2   id003  http://example.com/img/id003.bmp    Kristos   Ivanets  kivanets2@example.com        both
3   id004  http://example.com/img/id004.jpg    Madalyn       Max      mmax3@example.com        both
4   id005  http://example.com/img/id005.png       Tobe   Riddich  triddich4@example.com        both
5   id006                               NaN      Regan    Huyghe    rhuyghe@example.com  right_only
6   id007                               NaN    Kristin     Illis    killis4@example.com  right_only


df_outerのデータフレームでは、id006id007は右側のデータフレーム (この場合はdf1`) にのみ存在することに注意してください。

もし、場所を入れ替えずに左結合と外側結合を比較しようとすると、どちらも同じ結果になってしまうでしょう。

join() を使ってデータフレームを結合する

Pandasのインスタンスのメソッドである merge() とは異なり、 join() はDataFrame自身のメソッドです。

つまり、DataFrameのスタティックメソッドのように使うことができます。

DataFrame.join(other, on=None, how=’left’, lsuffix=”, rsuffix=”, sort=False)`です。

join()を呼び出したDataFrameは、左側のDataFrameになります。

引数other` の DataFrame は右側の DataFrame になります。

一方、 how パラメータはハンドル引数 (left, right, outer, inner) のいずれかを取り、デフォルトでは left がセットされています。

それでは、df2df1 に結合してみましょう。

df_join = df1.join(df2, rsuffix='_right')


print(df_join)


merge()関数と同様に、join()関数も同じ名前のキー (カラム) を自動的にマッチングしようとします。

この例では、user_id` キーがそれにあたります。

上記のコードはこのように出力されます。

  user_id first_name last_name                  email user_id_right                         image_url
0   id001       Rivi     Valti    rvalti0@example.com         id001  http://example.com/img/id001.png
1   id002     Wynnie   McMurty  wmcmurty1@example.com         id002  http://example.com/img/id002.jpg
2   id003    Kristos   Ivanets  kivanets2@example.com         id003  http://example.com/img/id003.bmp
3   id004    Madalyn       Max      mmax3@example.com         id004  http://example.com/img/id004.jpg
4   id005       Tobe   Riddich  triddich4@example.com         id005  http://example.com/img/id005.png
5   id006      Regan    Huyghe    rhuyghe@example.com           NaN                               NaN
6   id007    Kristin     Illis    killis4@example.com           NaN                               NaN


おそらく、user_id_rightという「重複したカラム」があることにお気づきでしょう。

このカラムを表示したくない場合は、user_id カラムを両方のカラムのインデックスとして設定すれば、サフィックスなしで結合されます。

df_join_no_duplicates = df1.set_index('user_id').join(df2.set_index('user_id'))


print(df_join_no_duplicates)


このようにすることで、user_id カラムを削除し、代わりにインデックスカラムとして設定することができます。

これにより、結果的にきれいなDataFrameが得られます。

        first_name last_name                  email                         image_url
user_id                                                                              
id001         Rivi     Valti    rvalti0@example.com  http://example.com/img/id001.png
id002       Wynnie   McMurty  wmcmurty1@example.com  http://example.com/img/id002.jpg
id003      Kristos   Ivanets  kivanets2@example.com  http://example.com/img/id003.bmp
id004      Madalyn       Max      mmax3@example.com  http://example.com/img/id004.jpg
id005         Tobe   Riddich  triddich4@example.com  http://example.com/img/id005.png
id006        Regan    Huyghe    rhuyghe@example.com                               NaN
id007      Kristin     Illis    killis4@example.com                               NaN


Append() を使ってデータフレームをマージする。

Pandasの公式ドキュメントでも指摘されているように、concat()append()メソッドは新しいDataFrameのコピーを返すので、これらのメソッドを使いすぎるとプログラムのパフォーマンスに影響を与える可能性があります。

appendは、2つのDataFrameを行軸だけでマージしたい場合に非常に有効です。

つまり、列単位でデータを合わせるのではなく、2つのDataFrameの全ての行を含む新しいDataFrameを作りたいのです。

それでは、df2df1に追加して、その結果を表示してみましょう。

df_append = df1.append(df2, ignore_index=True)


print(df_append)


append()`を使用すると、どのキーでもDataFrameにマッチしません。

ただ、最初のデータフレームにもう一つのデータフレームを追加して、そのコピーを返します。

もしDataFrameの形状が一致しない場合、Pandasは一致しないセルをNaNに置き換えます。

2つのDataFrameを足し合わせた出力は以下のようになります。

   user_id first_name last_name                  email                         image_url
0    id001       Rivi     Valti    rvalti0@example.com                               NaN
1    id002     Wynnie   McMurty  wmcmurty1@example.com                               NaN
2    id003    Kristos   Ivanets  kivanets2@example.com                               NaN
3    id004    Madalyn       Max      mmax3@example.com                               NaN
4    id005       Tobe   Riddich  triddich4@example.com                               NaN
5    id006      Regan    Huyghe    rhuyghe@example.com                               NaN
6    id007    Kristin     Illis    killis4@example.com                               NaN
7    id001        NaN       NaN                    NaN  http://example.com/img/id001.png
8    id002        NaN       NaN                    NaN  http://example.com/img/id002.jpg
9    id003        NaN       NaN                    NaN  http://example.com/img/id003.bmp
10   id004        NaN       NaN                    NaN  http://example.com/img/id004.jpg
11   id005        NaN       NaN                    NaN  http://example.com/img/id005.png


ほとんどのユーザーは append() よりも concat() を選びます。

なぜなら、concat()` はキーマッチングと軸のオプションも提供しているからです。

concat() を使ってデータフレームを結合する

連結は、merge()join() に比べて少し柔軟です。

なぜなら、縦方向 (行単位) または横方向 (列単位) にデータフレームを結合することができるからです。

トレードオフは、マッチしないデータはすべて破棄されることです。

以下は、パラメータを含む完全な関数です。

pandas.concat(objs, axis=0, join='outer', ignore_index=False, keys=None,
              levels=None, names=None, verify_integrity=False, sort=False, copy=True)


以下は、 concat() 関数で最もよく使用されるパラメータです。

  • objs は、連結される DataFrame オブジェクト ([df1, df2, …]) のリストです。
  • axis は連結の方向を指定します。0は行単位、1` は列単位です。
  • joininner (交差) または outer (結合) のいずれかになります。
  • デフォルトでは ignore_indexFalse に設定されており、インデックス値を元の DataFrame のまま保持することができます。True` に設定すると、元の値を無視し、インデックス値を順番に再割り当てする。
  • keys を使用すると、階層型インデックスを構築することができます。これは、DataFrameの左外側に追加されるもう一つのレベルのインデックスで、値が一意でない場合にインデックスを区別するのに役立つと考えることができます。

それでは、df2と同じカラムタイプのDataFrameを新規に作成します。

ただし、id006id007には image_url が含まれるようにします。

df2_addition = pd.DataFrame({'user_id': ['id006', 'id007'],
                             'image_url': ['http://example.com/img/id006.png',
                                           'http://example.com/img/id007.jpg']
                             })


df2df2_additionを行単位で結合するには、objs` パラメータにリストで渡して、生成された DataFrame を新しい変数に代入します。

df_row_concat = pd.concat([df2, df2_addition])


print(df_row_concat)


無事、欠損値を埋めることができました。

  user_id                         image_url
0   id001  http://example.com/img/id001.png
1   id002  http://example.com/img/id002.jpg
2   id003  http://example.com/img/id003.bmp
3   id004  http://example.com/img/id004.jpg
4   id005  http://example.com/img/id005.png
0   id006  http://example.com/img/id006.png
1   id007  http://example.com/img/id007.jpg


しかし、一番左の列のインデックスを見てください。

インデックス 01 は繰り返されています。

全く新しいユニークなインデックス値を取得するために、 ignore_index パラメータに True を渡します。

df_row_concat = pd.concat([df2, df2_addition], ignore_index=True)


これで、 df_row_concat はユニークなインデックスを持つようになりました。

  user_id                         image_url
0   id001  http://example.com/img/id001.png
1   id002  http://example.com/img/id002.jpg
2   id003  http://example.com/img/id003.bmp
3   id004  http://example.com/img/id004.jpg
4   id005  http://example.com/img/id005.png
5   id006  http://example.com/img/id006.png
6   id007  http://example.com/img/id007.jpg


先ほど説明したように、連結は水平方向にも垂直方向にも行うことができます。

2つのDataFrameを列方向に結合するには、axisの値をデフォルトの0から1に変更する必要があります。

df_column_concat = pd.concat([df1, df_row_concat], axis=1)


print(df_column_concat)


2つのテーブルをあるキーでマッチさせるマージのようにはいかないことにお気づきでしょう。

  user_id first_name last_name                  email user_id                         image_url
0   id001       Rivi     Valti    rvalti0@example.com   id001  http://example.com/img/id001.png
1   id002     Wynnie   McMurty  wmcmurty1@example.com   id002  http://example.com/img/id002.jpg
2   id003    Kristos   Ivanets  kivanets2@example.com   id003  http://example.com/img/id003.bmp
3   id004    Madalyn       Max      mmax3@example.com   id004  http://example.com/img/id004.jpg
4   id005       Tobe   Riddich  triddich4@example.com   id005  http://example.com/img/id005.png
5   id006      Regan    Huyghe    rhuyghe@example.com   id006  http://example.com/img/id006.png
6   id007    Kristin     Illis    killis4@example.com   id007  http://example.com/img/id007.jpg


もし、右のDataFrameに user_id カラムがなかったとしても、この連結は同じ結果を返します。

concat()` 関数は、DataFrame のインデックスの値とテーブルの形状を考慮して、2つの DataFrame をつなぎ合わせます。

この関数は merge()join() のようなキーマッチングを行うものではありません。

join` パラメータを変更して、いろいろな結合の組み合わせを試してみてください。

combine_first() と update() によるデータフレームの結合

場合によっては、DataFrame を別の DataFrame と結合することで、DataFrame の欠損データを埋めたいことがあります。

そうすることで、最初のDataFrameの欠損していない値をすべて保持しながら、すべての NaN 値を2番目のDataFrameの欠損していない値(もしあれば)に置き換えることができます。

この例では、NumPyをインポートして NaN 値を使用することにします。

Pandasを pip と共にインストールした場合、NumPyは既にインストールされているはずです。

Pythonのシェルやスクリプトファイルに以下のコードを入力します。

import numpy as np


df_first = pd.DataFrame({'COL 1': ['X', 'X', np.nan],
                         'COL 2': ['X', np.nan, 'X'],
                         'COL 3': [np.nan, 'X', 'X']},
                        index=range(0, 3))


df_second = pd.DataFrame({'COL 1': [np.nan, 'O', 'O'],
                          'COL 2': ['O', 'O', 'O']},
                         index=range(0, 3))


print(df_first)
print(df_second)


df_first` DataFrame は3つのカラムを持ち、それぞれに1つの欠損値があります。

  COL 1 COL 2 COL 3
0     X     X   NaN
1     X   NaN     X
2   NaN     X     X


一方、df_secondは2つのカラムを持ち、最初のカラムに1つの欠損値があります。

  COL 1 COL 2
0   NaN     O
1     O     O
2     O     O


df_secondを使って、df_first` の欠損値に対応するすべての値をパッチすることができます。

df_tictactoe = df_first.combine_first(df_second)


print(df_tictactoe)


前述のように、 combine_first() メソッドを使用すると、インデックス順に NaN 値のみを置換し、最初の DataFrame の欠損していない値はすべてそのまま残します。

  COL 1 COL 2 COL 3
0     X     X   NaN
1     X     O     X
2     O     X     X


一方、 df_first の値を df_second の値で上書きしたい場合は (NaN かどうかに関係なく)、 update() メソッドを使用することになります。

まず、別のDataFrameを追加してみましょう。

df_third = pd.DataFrame({'COL 1': ['O'], 'COL 2': ['O'], 'COL 3': ['O']})


print(df_third)


形状は (1, 3) – 1 行 3 列 (インデックスを除く) です。

  COL 1 COL 2 COL 3
0     O     O     O


それでは、df_thirdの値で df_first を更新してみましょう。

df_first.update(df_third)


print(df_first)


combine_first()とは異なり、update()は新しい DataFrame を返さないことに注意してください。

これは、df_first` をインプレースで変更し、対応する値を変更します。

  COL 1 COL 2 COL 3
0     O     O     O
1     X   NaN     X
2   NaN     X     X


update()関数のoverwriteパラメータは、デフォルトでTrueに設定されています。

このため、NaN値だけでなく、対応するすべての値が変更されます。

これをFalseに変更することで、NaN` 値のみを置き換えることができます。

df_tictactoe.update(df_first, overwrite=False)


print(df_tictactoe)


以下は、df_tictactoe DataFrameの最終的な状態です。

  COL 1 COL 2 COL 3
0     X     X     O
1     X     O     X
2     O     X     X


値の更新に成功しただけでなく、三目並べゲームに勝つこともできました。

結論

PandasはDataFrameをマージするための強力なツールを提供しています。

しかし、いつ何を使うか決めるのは難しいかもしれません。

ほとんどの場合は merge() 関数で十分ですが、場合によっては concat() を使って行単位でマージしたり、 join() を使って接尾辞を付けたり、 combine_first()update() を使って欠損値を除去したいかもしれません。

さらに append() でデータ行を追加することもできます。

自分が一番使いやすい、そしてそのタスクに最適な関数を使いましょう。

タイトルとURLをコピーしました