[Python / pandas] concat 使用時の InvalidIndexError: Reindexing only valid with uniquely valued Index objects エラー発生原因パターン

# 1. 概要

pandas の `concat` 関数を使用していて、 `InvalidIndexError: Reindexing only valid with uniquely valued Index objects` に出会したのだが、心当たりがなく、原因究明に時間を要したので記事にしておく。

# 2. 前提

item|version など
--|--
python|3.10
pandas|2.2.0


# 3. 発生原因

## 3-1. エラーにならないパターン

これは OK。

```python
import pandas as pd

# サンプルのDataFrame
data = pd.DataFrame({
    'A': [1, 2, 3],
    'B': [4, 5, 6],
    'C': [7, 8, 9]
},index=pd.RangeIndex(0, 3))

pd.concat([data, data], axis=0)

# 結果
	A	B	C
0	1	4	7
1	2	5	8
2	3	6	9
0	1	4	7
1	2	5	8
2	3	6	9
```

エラーメッセージには `Index` という文言が入っているが、 **Index が重複していてもエラーにはならない**。

そして、なんと驚くことに、単に列名が重複していている場合にもエラーにならない。

```python
pd.concat([data, data], axis=1)

# 結果
	A	B	C	A	B	C
0	1	4	7	1	4	7
1	2	5	8	2	5	8
2	3	6	9	3	6	9
```

じゃあ何がエラー原因なんだい？


## 3-2. わかりやすいエラー原因


### 3-2-1. concat 前から Index 重複

元々 Index が1つ以上重複する DataFrame を `axis=1` 方向に concat しようとすると発生する。以下のような状況。


```python
df1 = pd.DataFrame(
    {"体重": [50.1, 69.3], "身長": [163.4, 178.1]},
    index =["たじまさん", "Kevin"]
)
df2 = pd.DataFrame(
    {"学歴": ["大卒", "大卒"], "総資産": [1e10, 1.2e12]},
    index =["たじまさん", "たじまさん"]  # concat 前から index 重複
)

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

# 結果
InvalidIndexError: Reindexing only valid with uniquely valued Index objects
```

どうやら、 **concat で結合する前から index が重複している**とエラーになるらしい。

### 3-2-2. concat 前から columns 重複

元々 Columns が名1つ以上重複する DataFrame を `axis=0` 方向に concat しようとしてもエラーが発生する。以下のような状況。

サンプルデータを作るのが少し難しいが、以下のようなパターンがありうる。

```python
df1 = pd.DataFrame(
    {"体重": [50.1, 69.3], "身長": [163.4, 178.1]},
    index =["たじまさん", "Kevin"]
)
df1

# 結果
    	体重 	身長
たじまさん 50.1	163.4
Kevin    69.3	178.1
```

上の DataFrame はよくある形式。

一方、下のような DataFrame は、手を入れないと作れない。

```python
seriesA = pd.Series([42.6, 48.5], name="体重")
seriesB = seriesA.copy()
df2 = pd.concat([seriesA, seriesB], axis=1)
df2.index = ["よこたさん", "Comet"]
df2

# 結果
             体重	体重
よこたさん	42.6	42.6
Comet   	48.5	48.5
```

こういうのを縦 (`axis=0`) に concat するとエラーが出る。

```python
pd.concat([df1, df2], axis=0)

# 結果
InvalidIndexError: Reindexing only valid with uniquely valued Index objects
```

ここまでは色々な記事に書かれているので、これが原因だったらすぐに気がつけると思う。

## 3-3. 見落としがちなパターン

ここからは、なかなか気がつきにくいパターン。

再度、同じ列名を含む DataFrame に登場してもらう。

```python
seriesA = pd.Series([1, 2, 3], name='A')
seriesB = pd.Series([4, 5, 6], name='A')
df_A = pd.concat([seriesA, seriesA, seriesB], axis=1)
df_A

# 結果
	A	A	A
0	1	1	4
1	2	2	5
2	3	3	6
```

実は、こういう DataFrame も、縦 (axis=0) 方向に結合できる。
```python
pd.concat([df_A, df_A], axis=0)

# 結果
	A	A	A
0	1	1	4
1	2	2	5
2	3	3	6
0	1	1	4
1	2	2	5
2	3	3	6
```

自分の場合はこうした上記のような処理を書いていた。

ところが、入力データの形式によって、たまたま、1列だけ列名が変わることがあった。
出来上がったデータはこんな感じ。

```python
seriesA = pd.Series([1, 2, 3], name='A')
seriesC = pd.Series([7, 8, 9], name='C')
df_C = pd.concat([seriesA, seriesA, seriesC], axis=1)
df_C

# 結果
	A	A	C
0	1	1	7
1	2	2	8
2	3	3	9
```

これを df_A と縦に concat したら、以下のようになりそうなのだけど、、、

```python
	A	A	A	C
0	1	1	4	Nan
1	2	2	5	Nan
2	3	3	6	Nan
0	1	1	Nan	7
1	2	2	Nan	8
2	3	3	Nan	9
```

実際は InvalidIndexError になる。

```python
pd.concat([df_A, df_C], axis=0)

# 結果
InvalidIndexError: Reindexing only valid with uniquely valued Index objects
```

# 4. 考察


要は、 **「Reindexing (自動での Index や Columns 書き換え)」** が肝なのだろう。

- 「Reindexing」がなければ、 Index、 Columns が重複してようが問題ない
- **「Reindexing」が pandas 内で自動で実行されるときは、重複した Index や Columns を持ってきてはならない**

ということだと思われる。

つまり、 Columns や Index の書き換えが発生せずに、元のままで結合後の DataFrame が作られる時は、エラーは発生しない。