pandasで ValueError: cannot reindex from a duplicate axisのエラー

ValueError: cannot reindex from a duplicate axis

というエラーが、pandasにある。 エラーについて色々調べた結果、分からなかったこともあるけど、だいたい以下のとおりだろうか。

  • 行もしくは列の名前に重複がある場合に、特定の操作で発生するようだ?
  • 「複数の行/列のうちどれを使えばよいか分かりませんでした」というような意味かな?
  • pandasのバグを踏んでいる可能性もあるようだ?

最初の質問はエラー再現コードがない……

このエラーに関係するStack Overflowの質問のうち、一番閲覧数が多いのはこれだ。

https://stackoverflow.com/questions/27236275/what-does-valueerror-cannot-reindex-from-a-duplicate-axis-mean

……しかし、この質問にはエラーを再現させるコードが付属していない。 質問を投稿した人がエラーを出す短いコードを作ろうとしたが、うまく作れなかった、と書いてある。
俺自身も、質問者が書いたコードをちょっと変えてエラーを発生させてみようとしたけど、どうもうまく行かなかった。うーん。

(最初の質問が書かれたのが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が発生するケースはいくつかあるようだ。

https://stackoverflow.com/questions/27236275/what-does-valueerror-cannot-reindex-from-a-duplicate-axis-mean

を見ると、データフレームの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

その他?

https://stackoverflow.com/questions/30788061/valueerror-cannot-reindex-from-a-duplicate-axis-using-isin-with-pandas

では以下のように書かれている。

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? なんじゃ、そりゃ」となるかもしれない。

参考資料

https://stackoverflow.com/questions/27236275/what-does-valueerror-cannot-reindex-from-a-duplicate-axis-mean

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(筆者)にお知らせください。 必要に応じて加筆修正します。)

それでは。