pandasを使ってDataFrameを結合しようとしたら、ValueError: len(left_on) must equal the number of levels in the index of "right"
という見慣れないエラーが出てきた。
mergeを使うべきところでjoinを使ったのが原因だった。
以下のStackOverflowを見たら疑問は解決したけど、自分の頭を整理するためにまとめておく。
python - Why does Pandas inner join give ValueError: len(left_on) must equal the number of levels in the index of "right"? - Stack Overflow
また、joinのon
キーワードがどのように使われているのかについても調べたので、併せてまとめる。
- mergeを使うべきところでjoinを使ったのが原因だった
- DataFrameを結合する関数、mergeとjoinの違い。
- joinにはleft_on, right_onがない
- joinでonを1列だけ指定した場合のエラー
- joinのonキーワードは一体何をしているのか?
- エラーの意味を解明する
- 参考文献
mergeを使うべきところでjoinを使ったのが原因だった
- DataFrameを結合するためにmergeを使うべきところで、joinを使う
- onに複数列を指定する
以上の条件で発生するエラーであった。
pandasのコミッターであるsinhrksさんの記事がとても秀逸なので、DataFrameを連結・結合する処理で困ったらここを見よう。
Python pandas 図でみる データ連結 / 結合処理 - StatsFragments
その後、公式ドキュメントにもこの記事が追加された。上記は2015年の記事なので、最新の仕様については公式ドキュメントを見たほうが良い。
本記事の例は公式ドキュメントのものを一部変更している。
Merge, join, concatenate and compare — pandas 1.2.3 documentation
import pandas as pd import numpy as np pd.options.display.notebook_repr_html = False # jupyter notebook上での出力形式を制御するために書いています。無くても動きます。
# 動作環境の確認 print(pd.__version__) print(np.__version__) # -------------------- 1.1.2 1.19.1
df_left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'], 'key2': ['K0', 'K1', 'K0', 'K1'], 'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3']}) df_right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'], 'key2': ['K0', 'K0', 'K0', 'K0'], 'C': ['C0', 'C1', 'C2', 'C3'], 'D': ['D0', 'D1', 'D2', 'D3']}) print(df_left) print("~~~~~~") print(df_right) # -------------------- key1 key2 A B 0 K0 K0 A0 B0 1 K0 K1 A1 B1 2 K1 K0 A2 B2 3 K2 K1 A3 B3 ~~~~~~ key1 key2 C D 0 K0 K0 C0 D0 1 K1 K0 C1 D1 2 K1 K0 C2 D2 3 K2 K0 C3 D3
この2つのDataFrameを['key1', 'key2']という2つの列を基準に結合する。これはmergeを使うのが正しいやり方だ。
result = pd.merge(df_left,df_right, on=['key1', 'key2']) result # -------------------- key1 key2 A B C D 0 K0 K0 A0 B0 C0 D0 1 K1 K0 A2 B2 C1 D1 2 K1 K0 A2 B2 C2 D2
さらに、DataFrameに対してmerge関数を適用することもできる。結果は全く同じになる。
result = df_left.merge(df_right, on=['key1', 'key2']) result # -------------------- key1 key2 A B C D 0 K0 K0 A0 B0 C0 D0 1 K1 K0 A2 B2 C1 D1 2 K1 K0 A2 B2 C2 D2
上記において、間違えてmergeではなくjoinを使ってしまった。
result = df_left.join(df_right, on=['key1', 'key2']) # -------------------- --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-23-302ff90d7a3b> in <module> ----> 1 result = df_left.join(df_right, on=['key1', 'key2']) /usr/local/lib/python3.8/site-packages/pandas/core/frame.py in join(self, other, on, how, lsuffix, rsuffix, sort) 7867 5 K5 A5 NaN 7868 """ -> 7869 return self._join_compat( 7870 other, on=on, how=how, lsuffix=lsuffix, rsuffix=rsuffix, sort=sort 7871 ) /usr/local/lib/python3.8/site-packages/pandas/core/frame.py in _join_compat(self, other, on, how, lsuffix, rsuffix, sort) 7883 7884 if isinstance(other, DataFrame): -> 7885 return merge( 7886 self, 7887 other, /usr/local/lib/python3.8/site-packages/pandas/core/reshape/merge.py in merge(left, right, how, on, left_on, right_on, left_index, right_index, sort, suffixes, copy, indicator, validate) 72 validate=None, 73 ) -> "DataFrame": ---> 74 op = _MergeOperation( 75 left, 76 right, /usr/local/lib/python3.8/site-packages/pandas/core/reshape/merge.py in __init__(self, left, right, how, on, left_on, right_on, axis, left_index, right_index, sort, suffixes, copy, indicator, validate) 643 warnings.warn(msg, UserWarning) 644 --> 645 self._validate_specification() 646 647 # note this function has side effects /usr/local/lib/python3.8/site-packages/pandas/core/reshape/merge.py in _validate_specification(self) 1233 if self.right_index: 1234 if len(self.left_on) != self.right.index.nlevels: -> 1235 raise ValueError( 1236 "len(left_on) must equal the number " 1237 'of levels in the index of "right"' ValueError: len(left_on) must equal the number of levels in the index of "right"
はい。ValueError: len(left_on) must equal the number of levels in the index of "right" というエラーが発生した。
一応注記しておくと、エラー文の中の"right"はDataFrameの変数名とは無関係である。
join中で右側に指定したDataFrame、という意味であろう。
上記のコードを正しく動かすためには、「joinではなくmergeを使えば良い」で終わりである。
が、どうしてこんなエラーになったのか、いまいちよく分からん。
では、他の場合でもmergeではなくjoinにしてしまったら、どういうエラーが出るのだろうか?試してみよう。
DataFrameを結合する関数、mergeとjoinの違い。
その前に一旦、mergeとjoinの違いを見ておこう。
Merge, join, concatenate and compare — pandas 1.2.3 documentation
DataFrame.join() is a convenient method for combining the columns of two potentially differently-indexed DataFrames into a single result DataFrame.
拙訳:DataFrame.join()は、異なるindexの可能性のある2つの列を結合し、1つのDataFrameを結果として出力する、便利なメソッドである。
joinはあくまで便利メソッドなんだよね。
mergeは列やindexを使ってDataFrameを結合できる。joinはindexを使ってDataFrameを結合できる。
mergeはjoinを含んでjoinより広い範囲のことができるので、joinを使うところはmergeで置き換えることもできる。
なお、pandasのドキュメントからソースコードに飛んで、
pandas/frame.py at v1.1.2 · pandas-dev/pandas · GitHub
を見ると、joinはその内部でmerge関数を実行しているのが分かる。
joinにはleft_on, right_onがない
mergeにはleft_on, right_onというパラメータが指定できるが、joinにはこれらのパラメータがない。 したがって、joinでleft_on, right_onを指定すると「そんなパラメータは指定できません」というエラーを返す。これは分かりやすいね。
# https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.merge.html df1 = pd.DataFrame({'lkey': ['foo', 'bar', 'baz', 'foo'], 'value': [1, 2, 3, 5]}) df2 = pd.DataFrame({'rkey': ['foo', 'bar', 'baz', 'foo'], 'value': [5, 6, 7, 8]})
df1 # -------------------- lkey value 0 foo 1 1 bar 2 2 baz 3 3 foo 5
df2 # -------------------- rkey value 0 foo 5 1 bar 6 2 baz 7 3 foo 8
df1.merge(df2, left_on='lkey', right_on='rkey') # -------------------- lkey value_x rkey value_y 0 foo 1 foo 5 1 foo 1 foo 8 2 foo 5 foo 5 3 foo 5 foo 8 4 bar 2 bar 6 5 baz 3 baz 7
df1.join(df2, left_on='lkey', right_on='rkey') # -------------------- --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-7-b2a9def786b8> in <module> ----> 1 df1.join(df2, left_on='lkey', right_on='rkey') TypeError: join() got an unexpected keyword argument 'left_on'
joinでonを1列だけ指定した場合のエラー
onを複数列指定したときは最初に書いたエラーになる。1列だけの場合はどうなるだろうか?
df1 = pd.DataFrame({'key': ['foo', 'bar', 'baz', 'foo'], 'value_df1': [1, 2, 3, 5]}) df2 = pd.DataFrame({'key': ['foo', 'bar', 'baz', 'foo'], 'value_df2': [5, 6, 7, 8]})
df1.merge(df2, on='key') # -------------------- key value_df1 value_df2 0 foo 1 5 1 foo 1 8 2 foo 5 5 3 foo 5 8 4 bar 2 6 5 baz 3 7
df1.join(df2, on='key') # -------------------- --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-10-3d5d0d53e524> in <module> ----> 1 df1.join(df2, on='key') /usr/local/lib/python3.8/site-packages/pandas/core/frame.py in join(self, other, on, how, lsuffix, rsuffix, sort) 7867 5 K5 A5 NaN 7868 """ -> 7869 return self._join_compat( 7870 other, on=on, how=how, lsuffix=lsuffix, rsuffix=rsuffix, sort=sort 7871 ) /usr/local/lib/python3.8/site-packages/pandas/core/frame.py in _join_compat(self, other, on, how, lsuffix, rsuffix, sort) 7883 7884 if isinstance(other, DataFrame): -> 7885 return merge( 7886 self, 7887 other, /usr/local/lib/python3.8/site-packages/pandas/core/reshape/merge.py in merge(left, right, how, on, left_on, right_on, left_index, right_index, sort, suffixes, copy, indicator, validate) 72 validate=None, 73 ) -> "DataFrame": ---> 74 op = _MergeOperation( 75 left, 76 right, /usr/local/lib/python3.8/site-packages/pandas/core/reshape/merge.py in __init__(self, left, right, how, on, left_on, right_on, axis, left_index, right_index, sort, suffixes, copy, indicator, validate) 654 # validate the merge keys dtypes. We may need to coerce 655 # to avoid incompatible dtypes --> 656 self._maybe_coerce_merge_keys() 657 658 # If argument passed to validate, /usr/local/lib/python3.8/site-packages/pandas/core/reshape/merge.py in _maybe_coerce_merge_keys(self) 1163 inferred_right in string_types and inferred_left not in string_types 1164 ): -> 1165 raise ValueError(msg) 1166 1167 # datetimelikes must match exactly ValueError: You are trying to merge on object and int64 columns. If you wish to proceed you should use pd.concat
んーー?
ちょっと言ってることがよく分からない。
mergeを使うべきところでうっかりjoinを使ってしまったので「you should use pd.merge
」って言ってくれるのが一番ありがたい。しかし、「you should use pd.concat
」と言われてしまった。一体何で……?
joinのonキーワードは一体何をしているのか?
join関数は何に基づいてDataFrameを結合するのか?
それはindexである。joinを使った時点で、indexに基づいて結合すると決まっている。
じゃあわざわざonで指定する必要も無いのではないか?
joinのonキーワードについてドキュメントを見てみよう。
以下2つの操作は完全に等価である、とドキュメントには書いてある(Merge, join, concatenate and compare — pandas 1.2.3 documentation)。
left.join(right, on=key_or_keys)
pd.merge(left, right, left_on=key_or_keys, right_index=True, how='left', sort=False)
公式ドキュメントを色々読むと、どうやら以下の仕様であると分かった。
- joinはDataFrame.join()の形式で使う。pd.joinの形式では使えない。
- joinの中で
on
を指定した場合、joinの左側のDataFrameで使う列名(またはindexレベル名)となる。 - joinの中で
on
を指定した場合でも、joinの右側のDataFrameでindexを基準に結合するということは変わらない。(常にright_index=True
になることに注意!) - joinの中で
on
に複数の値を指定した場合、右側のDataFrameはMultiIndexでなければならない。 - Merge, join, concatenate and compare — pandas 1.2.3 documentationには、joinの中で
on
に単一の値、複数の値を指定した例がある。
エラーの意味を解明する
joinのon
の意味を把握すると、ようやく最初に書いた謎めいたエラーの意味がわかってくる。最初の例からコードとエラー文を再掲する。
result = df_left.join(df_right, on=['key1', 'key2']) # -------------------- ValueError: len(left_on) must equal the number of levels in the index of "right"
pandasの気持ちになると、こういう思考過程でエラーを上げている。
「joinに2つの要素からなるon
が指定されているから、右側のDataFrameは2段階のindexからなるMultiIndexのDataFrameのはずだよね。あれ、それなのに実際には右側のDataFrameはMultiIndexではないぞ。これはおかしいぞ、エラーだ。onの中の要素数(len(left_on)) ≠ 右側のindexの段階数'(the number of levels in the index of "right")なので、エラーを出そう。」
内部のコードを見てはいないが、エラー文章とも整合するので、多分こうだろう。
次のエラー。コードとエラー文を再掲する。
df1.join(df2, on='key') # -------------------- ValueError: You are trying to merge on object and int64 columns. If you wish to proceed you should use pd.concat
pandasの気持ちになると、こういう思考過程でエラーを上げている。
「joinに1つの要素からなるon
が指定されている。左側のDataFrameのkey列と、右側のDataFrameのindexに基づいてデータを結合しよう。あれ、左側のDataFrameのkey列はobject型(文字列が入っている)、右側のDataFrameのindexはint64型じゃないか。これじゃデータを結合できるわけが無いや。エラーを出そう。」
内部のコードをあまり見ていないが、エラー文章とも整合するので、多分こうだろう。
(付け加えると、pandas内部の_maybe_coerce_merge_keys()関数で、違うdtypeでもうまく変換してdtypeを揃えられないか試しているようだ。そのおかげで、例えばint64の列とint8の列はうまくmergeできる。だが、文字列とint64ではdtypeを揃えるのはどうやっても無理なので、結局エラーになる。)
参考文献
今回参考にしたページ。どれもここまでに既に挙げたものだ。
エラーメッセージでググると真っ先に出てくるStackOverflow。
python - Why does Pandas inner join give ValueError: len(left_on) must equal the number of levels in the index of "right"? - Stack Overflow
DataFrameの結合について。 Python pandas 図でみる データ連結 / 結合処理 - StatsFragments その後、この記事を元に公式ドキュメントにも英語の記事が追加された。 Merge, join, concatenate and compare — pandas 1.2.3 documentation
今回公式ドキュメントを少し詳しく読んでみたが、説明の充実度は公式ドキュメントのほうが圧倒的に高い。
もとはこのブログ記事であるとはいえ、英語に翻訳したあとに大幅な加筆修正が入っている。
その分、どこに何が書いてあるかがすぐには分かりづらい面もある。
初心者は日本語のPython pandas 図でみる データ連結 / 結合処理 - StatsFragments
少し慣れてきて詳しいことを知りたかったら、内容たっぷりの公式ドキュメントのMerge, join, concatenate and compare — pandas 1.2.3 documentation
が良いと思った。
今回調べてみて、RやSQLでいくらjoinが使われていても、pandasではmergeを使うんだと覚えておこう、と思った。
それでは。