ValueError: cannot reindex from a duplicate axis
というエラーが、pandasにある。 エラーについて色々調べた結果、分からなかったこともあるけど、だいたい以下のとおりだろうか。
- 行もしくは列の名前に重複がある場合に、特定の操作で発生するようだ?
- 「複数の行/列のうちどれを使えばよいか分かりませんでした」というような意味かな?
- pandasのバグを踏んでいる可能性もあるようだ?
最初の質問はエラー再現コードがない……
このエラーに関係するStack Overflowの質問のうち、一番閲覧数が多いのはこれだ。
……しかし、この質問にはエラーを再現させるコードが付属していない。
質問を投稿した人がエラーを出す短いコードを作ろうとしたが、うまく作れなかった、と書いてある。
俺自身も、質問者が書いたコードをちょっと変えてエラーを発生させてみようとしたけど、どうもうまく行かなかった。うーん。
(最初の質問が書かれたのが2014年なので、そこからpandasの仕様が変わってエラーが発生しなくなった、という可能性はある。)
ただ、解答者が名推理をして、「たぶん列名に重複があるんじゃないかな?」と書いたらそれが正解だったらしく、疑問は解決している。
reindexでエラー発生
pandasの公式ドキュメントの中で、ValueError: cannot reindex from a duplicate axis
の記述がある部分は1箇所だけある。それはreindex関数の説明の中だ。
https://pandas.pydata.org/docs/user_guide/indexing.html?highlight=valueerror#reindexing
以下は、上記の公式ドキュメントと同じ内容である。
普通の使い方
まず、reindex()関数の普通の使い方を見てみよう。
import pandas as pd import numpy as np pd.options.display.notebook_repr_html = False # jupyter notebook上での出力形式を制御するために書いています。無くても動きます。
# 動作環境の確認 print(pd.__version__) print(np.__version__) # -------------------- 1.0.1 1.18.1
s = pd.Series([1, 2, 3]) s # -------------------- 0 1 1 2 2 3 dtype: int64
indexを指定して、元のオブジェクトの一部を選択・抽出できる。 もともとのSeriesに無いindex(ここでは3)を指定しても実行できるのが特徴である(該当する行にはNaNが入る)。
s.reindex([1, 2, 3]) # -------------------- 1 2.0 2 3.0 3 NaN dtype: float64
indexに重複があるとエラーが発生する
ここで、元のオブジェクトのindexに重複したものがあると、エラーValueError: cannot reindex from a duplicate axis
が発生する。以下の通りだ。
s = pd.Series(np.arange(4), index=['a', 'a', 'b', 'c']) s # -------------------- a 0 a 1 b 2 c 3 dtype: int64
labels = ['c', 'd']
s.reindex(labels) # -------------------- --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-7-5ba024c16ecb> in <module> ----> 1 s.reindex(labels) /usr/local/lib/python3.7/site-packages/pandas/core/series.py in reindex(self, index, **kwargs) 4028 @Appender(generic.NDFrame.reindex.__doc__) 4029 def reindex(self, index=None, **kwargs): -> 4030 return super().reindex(index=index, **kwargs) 4031 4032 def drop( /usr/local/lib/python3.7/site-packages/pandas/core/generic.py in reindex(self, *args, **kwargs) 4542 # perform the reindex on the axes 4543 return self._reindex_axes( -> 4544 axes, level, limit, tolerance, method, fill_value, copy 4545 ).__finalize__(self) 4546 /usr/local/lib/python3.7/site-packages/pandas/core/generic.py in _reindex_axes(self, axes, level, limit, tolerance, method, fill_value, copy) 4565 fill_value=fill_value, 4566 copy=copy, -> 4567 allow_dups=False, 4568 ) 4569 /usr/local/lib/python3.7/site-packages/pandas/core/generic.py in _reindex_with_indexers(self, reindexers, fill_value, copy, allow_dups) 4611 fill_value=fill_value, 4612 allow_dups=allow_dups, -> 4613 copy=copy, 4614 ) 4615 /usr/local/lib/python3.7/site-packages/pandas/core/internals/managers.py in reindex_indexer(self, new_axis, indexer, axis, fill_value, allow_dups, copy) 1249 # some axes don't allow reindexing with dups 1250 if not allow_dups: -> 1251 self.axes[axis]._can_reindex(indexer) 1252 1253 if axis >= self.ndim: /usr/local/lib/python3.7/site-packages/pandas/core/indexes/base.py in _can_reindex(self, indexer) 3097 # trying to reindex on an axis with duplicates 3098 if not self.is_unique and len(indexer): -> 3099 raise ValueError("cannot reindex from a duplicate axis") 3100 3101 def reindex(self, target, method=None, level=None, limit=None, tolerance=None): ValueError: cannot reindex from a duplicate axis
labelには重複したindexである'a'
が含まれていないが、それでもエラーになっているんだな。
「indexが'a'の箇所が複数あるので、どの'a'を使ったら良いかわからないよ!」というくらいの意味だろうか。
joinでエラー発生……しなかった
以降の話はpandasの公式ドキュメントに書いていない。書いていないけど、reindex()関数以外でも
ValueError: cannot reindex from a duplicate axis
が発生するケースはいくつかあるようだ。
を見ると、データフレームのjoinでエラーが発生すると書いてあった。
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.join.html
ちょっと手元で色々試してみたけど、結果的にはjoinでこのエラーを発生させることはできなかった。
普通のjoin
pandasのコミッターであるsinhrksさんの記事がとても秀逸なので、サンプルコードをお借りいたします。
DataFrameを連結・結合する処理で困ったらここを見ましょう。
http://sinhrks.hatenablog.com/entry/2015/01/28/073327
left = pd.DataFrame({'A': ['A0', 'A1', 'A2'], 'B': ['B0', 'B1', 'B2']}, index=['K0', 'K1', 'K2']) left # -------------------- A B K0 A0 B0 K1 A1 B1 K2 A2 B2
right = pd.DataFrame({'C': ['C0', 'C2', 'C3'], 'D': ['D0', 'D2', 'D3']}, index=['K0', 'K2', 'K3']) right # -------------------- C D K0 C0 D0 K2 C2 D2 K3 C3 D3
left.join(right)
# --------------------
A B C D
K0 A0 B0 C0 D0
K1 A1 B1 NaN NaN
K2 A2 B2 C2 D2
joinは(何も指定しなければ)2つのDataFrameのindexに基づいて、データを結合する。
indexが重複したDataFrameをjoin→エラーにならない……
問題はここからだ。 rightのindexを重複させます。
right_dup_1 = pd.DataFrame({'C': ['C0', 'C2', 'C3'], 'D': ['D0', 'D2', 'D3']}, index=['K0', 'K2', 'K2']) right_dup_1 # -------------------- C D K0 C0 D0 K2 C2 D2 K2 C3 D3
これでエラーになるか?
left.join(right_dup_1)
# --------------------
A B C D
K0 A0 B0 C0 D0
K1 A1 B1 NaN NaN
K2 A2 B2 C2 D2
K2 A2 B2 C3 D3
エラーにはならなかった。 なるほど。rightにはindexが'K2'の行が2つあるから、leftのK2をrightの2行のそれぞれとjoinした結果になるのね。
……その他、以下のような場合を試してみたが、全然エラーにならなかった。
- leftのindexが重複している
- rightのindexが重複している
- leftとrightのindexが重複している
- 複数dtypeのDetaFrameにしてみる
- how='inner'を指定してみる
うーーん、分からない。
indexが重複したDataFrameを使ってjoinしてみたけど、エラーが再現しなかった。
どこかのアップデートでjoinの仕様が変わったってことかもしれない?
新しい行/列の割当て(assign)でエラー発生
これはQiitaに説明があったので、そちらを参照してください。 手元のpandas 1.0.1で試してもエラーが再現しました。良かった。(良かったのか?)
https://qiita.com/waterada/items/c239a6d0424537cfcfb9
その他?
では以下のように書かれている。
The error
ValueError: cannot reindex from a duplicate axis
is one of these very very cryptic pandas errors which simply does not tell you what the error is.
The error is often related to two columns being named the same either before or after (internally in) the operation.拙訳:
ValueError: cannot reindex from a duplicate axis
というエラーは、とっっっても分かりにくいpandasのエラーの一つです。何が誤りだったのか全く教えてくれません。
このエラーは大抵の場合、操作の前や後で同じ名前がついた2つの列があるときに発生します。
(訳注:internally inはよく分からなかったので訳を飛ばしました)
結構頑張って検索しても、どういうときにエラーが発生するのかいまいちよく分からなかった。
reindex関数を明示的に使わなくても(おそらく内部でreindex関数が走って)エラーが上がることがあるから、
「reindex? なんじゃ、そりゃ」となるかもしれない。
参考資料
pandasのissueでこのエラー文章を含むものも多数報告されているので、それを見てみるのも良いだろう:
https://github.com/pandas-dev/pandas/issues?q=is%3Aissue+%22cannot+reindex+from+a+duplicate+axis%22
Bugというタグが付いているissueも多い。このエラーに出くわしたら、pandasのバグを踏んでしまった可能性もあるようだ。
(ここまで読んだ人へ:もし自分のエラーがここまで書いてきた内容に該当しないようなら、twitterの@Linus_MK(筆者)にお知らせください。 必要に応じて加筆修正します。)
それでは。