pandasのmerge, join, concatの違いに関する実践的な解説
1. 要件から始める
最近、ちょっとした作業が必要なデータが大量にあり、それは非常に一般的な要件である
数字でいっぱいのデータセットがあり、データセット内のセグメントの数を区間ごとに数え、各区間の割合を計算する必要があります。つまり、本質的にはパーセントを数えるようなものです。
問題は、第一に、プログラマーは.xxxファイルでエクセルを使うことを嫌います。.xxxは汎用性が低く、それを開くには特定のソフトウェアプログラムを使わなければならないことを意味します。第二に、開発環境のほとんどはLinuxやmacos、Windowsとは異なり、2つの開発環境は、オフィスシリーズのサポートは非常に貧弱であり、オフィスシリーズのものはかなりリソース集約型のCPUメモリ、個人的に非常に嫌なのは、特に重いクライアントソフトを開くためにデータに対処するためである。第三に、Excelは再び強力な、すべての後に、柔軟なコードを書いていない、通常はまだ奇妙な、奇妙なニーズがたくさんある、特に文字列処理、コード処理とまたは優先されます。
それでは、さっそくコードを見て、上記のことをどのように修正するか見てみましょう。
2. pandas cut concat
import pandas as pd
def t1():
data1 = [552, 462, 565, 810, 720, 753, 602, 485, 475, 380, 590, 402, 501]
data2 = [553, 362, 585, 710, 720, 559, 760, 785, 375, 680, 690, 403, 512]
bins = [300, 400, 450, 550, 650, 750, 850]
cut1 = pd.cut(data1, bins)
cut2 = pd.cut(data2, bins)
ret1 = pd.value_counts(cut1, ascending=False)
ret2 = pd.value_counts(cut2, ascending=False)
nret1 = pd.value_counts(cut1, normalize=True, ascending=False)
nret2 = pd.value_counts(cut2, normalize=True, ascending=False)
concat1 = pd.concat([ret1, nret1, ret2, nret2], axis=1)
print(concat1)
t1()
上の出力です。
0 1 2 3
(300, 400] 1 0.076923 2 0.153846
(400, 450] 1 0.076923 1 0.076923
(450, 550] 4 0.307692 1 0.076923
(550, 650] 4 0.307692 3 0.230769
(650, 750] 1 0.076923 4 0.307692
(750, 850] 2 0.153846 2 0.153846
上記のコードの中で、pd.cut、pd.value_countsの使い方は以前にも紹介しましたので、今回は割愛します。ただ一つ、pd.value_countsメソッドでnormalize=Trueの場合、出力はパーセント、それ以外は数値になるということだけは再度触れておく必要があります。
この行に注目
concat1 = pd.concat([ret1, nret1, ret2, nret2], axis=1)
pandasのConcatは単純に2つのテーブルを結合するものです。この処理は結合または積み重ねと呼ばれます。上の例では、"rows" は同じまま、"columns" を一緒に追加したいので、axis=1 と設定しています。
axisパラメータを指定しない場合、デフォルトはaxis=0で、これは"columns"を変更せず、行ごとにアペンドすることを意味します。簡単な例を見てみましょう。
def t2():
data1 = {"A": [1, 2, 3], "B": [4, 5, 6]}
data2 = {"C": [7, 8, 9], "D": [10, 11, 12]}
df1 = pd.DataFrame(data1)
df2 = pd.DataFrame(data2)
print(pd.concat([df1, df2]))
t2()
その結果、次のようになります。
A B C D
0 1.0 4.0 NaN NaN
1 2.0 5.0 NaN NaN
2 3.0 6.0 NaN NaN
0 NaN NaN 7.0 10.0
1 NaN NaN 8.0 11.0
2 NaN NaN 9.0 12.0
ignore_index は、連結されるフレームのインデックスを無視し、元のインデックスに特に意味がない場合に使用できます。
上記のコードを少し修正すると、以下のようになります。
print(pd.concat([df1, df2], ignore_index=True))
すると、結果は次のようになります。
A B C D
0 1.0 4.0 NaN NaN
1 2.0 5.0 NaN NaN
2 3.0 6.0 NaN NaN
3 NaN NaN 7.0 10.0
4 NaN NaN 8.0 11.0
5 NaN NaN 9.0 12.0
3. マージ方法
その他、実際にデータを扱う際によく必要になるのが結合です。結合とは、特定のカラムに基づいて2つのテーブルのデータをつなぎ合わせるデータベース結合操作のことです。
もう一度、ごく一般的な例を見てみましょう。
def t3():
agedata = {"name": ["lucy", "lili", "xiaoming"], "age": [15, 18, 21]}
citydata = {"name": ["lucy", "lili", "xiaohua"], "city": ["beijing", " shanghai", "guangzhou"]}
df1 = pd.DataFrame(agedata)
df2 = pd.DataFrame(citydata)
ret = pd.merge(df1, df2, on="name", how="left")
print(ret)
t3()
出力は次のようになります。
name age city
0 lucy 15 beijing
1 lili 18 shanghai
2 xiaoming 21 NaN
上の例は、sqlのjoinです。2つのデータフレームを、nameフィールドに基づいて左結合し、最終的な結果を得ます。
merge メソッドのシグネチャは以下のとおりです。
@Substitution("\nleft : DataFrame")
@Appender(_merge_doc, indents=0)
def merge(
left,
right,
how: str = "inner",
on=None,
left_on=None,
right_on=None,
left_index: bool = False,
right_index: bool = False,
sort: bool = False,
suffixes=("_x", "_y"),
copy: bool = True,
indicator: bool = False,
validate=None,
) -> "DataFrame":
op = _MergeOperation(
left,
right,
how=how,
on=on,
left_on=left_on,
right_on=right_on,
left_index=left_index. right_index=right_index, left_index=left_index,
right_index=right_index,
sort=sort,
suffixes=suffixes,
copy=copy,
indicator=indicator,
validate=validate,
)
return op.get_result()
4. マージの各パラメータの役割
left_indexとright_indexというパラメータですが、最初は何をするのかわかりませんでしたが、次のように調べてみました。
上記の例では、接続の主キーを指定するためにonを使用しています。onで指定するだけでなく、left_indexとright_indexパラメータをtrueに設定するだけで、そのインデックスを結合の主キーとして使用することができます。
def t4():
agedata = {"name": ["lucy", "lili", "xiaoming"], "age": [15, 18, 21]}
citydata = {"name": ["lucy", "lili", "xiaohua"], "city": ["beijing", " shanghai", "guangzhou"]}
df1 = pd.DataFrame(agedata)
df2 = pd.DataFrame(citydata)
ret = pd.merge(df1, df2, left_index=True, right_index=True)
print(ret)
t4()
出力は
name_x age name_y city
0 lucy 15 lucy beijing
1 lili 18 lili shanghai
2 xiaoming 21 xiaohua guangzhou
df1とdf2のデフォルトのインデックスは0,1,2なので、接続するインデックスに従って、2つのdfが完全にスプライスされます。サフィックスのデフォルトが("_x", "_y")の場合、同じフィールド名があれば結合時に自動的に適切なサフィックスに追加されます。
howパラメータはスプライスの方法を制御し、デフォルトはinnerです。結合はsqlと同じで、left、right、inner、outerです。
5. ジョインメソッド
上記の例で、sqlにおける伝統的なjoinが、実はpandasではmergeメソッドによって実装されていることが簡単に分かります。しかし、pandasにはjoinメソッドがあります。では、pandasのjoinメソッドは何を実現するのでしょうか?
まず、結論から。
joinメソッドは、主に行インデックスでのマージに基づいてカラムをスプライシングします。
いくつかの例を見てみましょう。
def t5():
agedata = {"name": ["lucy", "lili", "xiaoming"], "age": [15, 18, 21]}
citydata = {"name": ["lucy", "lili", "xiaohua"], "city": ["beijing", " shanghai", "guangzhou"]}
df1 = pd.DataFrame(agedata)
df2 = pd.DataFrame(citydata)
df1.join(df2)
このメソッドはエラーで実行されます
ValueError: columns overlap but no suffix specified: Index(['name'], dtype='object')
上記のコードを少し修正する
def t5():
agedata = {"name": ["lucy", "lili", "xiaoming"], "age": [15, 18, 21]}
citydata = {"name": ["lucy", "lili", "xiaohua"], "city": ["beijing", " shanghai", "guangzhou"]}
df1 = pd.DataFrame(agedata)
df2 = pd.DataFrame(citydata)
ret = df1.join(df2, lsuffix="_x", rsuffix="_y")
print(ret)
結果は次のようになります。
name_x age name_y city
0 lucy 15 lucy beijing
1 lili 18 lili shanghai
2 xiaoming 21 xiaohua guangzhou
これは、行インデックスに基づく上記のマージ方法と一致していますか?
実際にjoinメソッドのソースコードを見てみます。
def join(
self, other, on=None, how="left", lsuffix="", rsuffix="", sort=False
) -> "DataFrame":
"""
Join columns of another DataFrame.
Join columns with `other` DataFrame either on index or on a key
Efficiently join multiple DataFrame objects by index at once by
passing a list.
.......
return self._join_compat(
other, on=on, how=how, lsuffix=lsuffix, rsuffix=rsuffix, sort=sort
)
def _join_compat(
self, other, on=None, how="left", lsuffix="", rsuffix="", sort=False
):
from pandas.core.reshape.merge import merge
from pandas.core.reshape.concat import concat
if isinstance(other, Series):
if other.name is None:
raise ValueError("Other Series must have a name")
other = DataFrame({other.name: other})
if isinstance(other, DataFrame):
return merge(
self,
other,
left_on=on,
how=how,
left_index=on is None,
right_index=True,
suffixes=(lsuffix, rsuffix),
sort=sort,
)
else:
if on is not None:
raise ValueError(
"Joining multiple DataFrames only supported for joining on index"
)
frames = [self] + list(other)
can_concat = all(df.index.is_unique for df in frames)
# join indexes only using concat
if can_concat:
if how == "left":
res = concat(
frames, axis=1, join="outer", verify_integrity=True, sort=sort
)
return res.reindex(self.index, copy=False)
else:
return concat(
frames, axis=1, join=how, verify_integrity=True, sort=sort
)
joined = frames[0]
for frame in frames[1:]:
joined = merge(
joined, frame, how=how, left_index=True, right_index=True
)
return joined
上記のコードで、join メソッドが、実際には、merge メソッドを同様に呼び出すことになるのは、簡単にわかります。というか、joinメソッドはmergeの特殊なケースに過ぎないのです。
6. 結論
まとめとして
1. sqlでjoinを実装するには、mergeメソッドを使用する必要があります。
2. pandasのjoinメソッドはmergeに比べると弟分的な存在で、利用シーンが限定されています。
3. concat は、2つの df を行または case 列で単純につなげる機能を実装しており、sql の join 関数は実装していない。
関連
-
[解決済み】TypeError: imshow()で配列をプロットすると、画像データの寸法が不正になる
-
[解決済み] IPython, "名前 'plt' が定義されていません".
-
[解決済み] super() は、新しいスタイルのクラスに対して "TypeError: must be type, not classobj" を送出する。
-
[解決済み] Pythonは長い数値をfloatに変換すると思います(例:x.xxxe+yy)。
-
[解決済み] Pandasシリーズの最後の要素にアクセスする方法は?
-
[解決済み] Python TypeError: サポートされていないオペランドタイプ(s) for ^: 'float' および 'int'
-
[解決済み] EC.presence_of_element_located((By.ID, "myDynamicElement")) は、IDではなくクラスを指定する以外にどのように使用するのでしょうか?
-
[解決済み] tkinterのウィンドウを閉じるにはどうしたらよいですか?
-
[解決済み】Jupyterコマンド'notebook'の実行に失敗しました。[Errno 'jupyter-notebook' not found] 2
-
[解決済み] Pythonでファイルをロックする
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Youtube_dl : ERROR : YouTubeは言った。動画データを抽出できない
-
RuntimeError: 反復中に辞書のサイズが変更された Solution
-
[解決済み] pip の `--no-cache-dir` は何のためにあるのですか?
-
[解決済み] python - ログで無効な値が検出されました。
-
[解決済み] 'WSGIRequest' オブジェクトに 'user' 属性がない Django 管理者
-
python TypeError: type 'float' の引数は反復可能ではありません。
-
致命的なエラーが発生しました。ESP32への接続に失敗しました:パケットヘッダの待ち時間がタイムアウトしました
-
args','**','**kwargs' を理解する。
-
python sklearn decision_function, predict_proba, predict
-
[解決済み] Pythonのクラスファイル名もキャメルケースにすべきですか?