seabornでhueを指定しながら、重ねてプロットしようとしたらハマった話

# 注意
【matplotlib v2.1.1以前】の時のエラーです。
【matplotlib v2.1.2以降】の方はあまり関係ないかもしれません。


# 経緯

雑にpythonのseabornを使ってグラフを書いていたら、
**同じカテゴリカルデータを使って書いた２つのグラフを重ねて表示したくなった**。

```python3
sns.pointplot(x="x", y="fitting_y", hue="y_limit", data=result, markers="")
sns.pointplot(x="x", y="y", hue="y_limit", data=result, join=False)
```

と書いたら、以下のようなエラーが出てきて処理が止まった。

```python
File "${HOME}/anaconda3/lib/python3.6/site-packages/matplotlib/legend.py", line 1344, in _in_handles
     if f_h.get_facecolor() != h.get_facecolor():
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```

上記のエラーを修正しようとした。

# 経緯（図解）

こういう２つのグラフがある
これらを……


![くっつける前](https://gist.githubusercontent.com/nukisashineko/6f6e29664d28510dee5eef1ced78be68/raw/969a39fac2af9373f88ef3eacf0db1b89c346862/blog1.png)

↓　こうしたい


![くっつけた後](https://gist.githubusercontent.com/nukisashineko/6f6e29664d28510dee5eef1ced78be68/raw/969a39fac2af9373f88ef3eacf0db1b89c346862/blog2.png)


# 解決法（正攻法）
skotaroさんにコメントでコードを教えてもらったため、記述させていただきます。
`groupby`を利用して描画した後、legendを利用して凡例を記述するのが正攻法です。

```python3
fig, ax = plt.subplots()
grouped = result.groupby('y_limit')
for key, gr in grouped:
    gr.plot('x', 'fitting_y', ax=ax)
    color = ax.lines[-1].get_color()
    gr.plot.scatter('x', 'y', marker='o', ax=ax, color=color)

ax.legend(ax.collections, list(grouped.groups.keys()), loc='upper left', bbox_to_anchor=(1,1))
```



# 解決法(邪道)
どうしても`seaborn`を利用して、プロットしたい人向け

```python3
ax = sns.pointplot(x="x", y="fitting_y", hue="y_limit", data=result, markers="")
ax.collections.clear() # 「今回の肝」 コレが無いとエラーで落ちる。
sns.pointplot(x="x", y="y", hue="y_limit", data=result, join=False, ax=ax)
```
hueを連続して使いたいなら、ax.collectionsに溜まる色付きラベル`PathCollection`を末尾以外で随時削除するべし。  

# 原因【matplotlib v2.1.1以前】

~~seabornで hueを指定した際、legend（凡例設定）が自動的に作成され、hueから自動生成された`PathCollection`（色付きラベル） [※１] が凡例表示用にaxに保存される。
連続してplotする際に、二度目の関数でもhueが指定されている場合、やはりlegendを自動生成・追記しようとする。
**hueから`PathCollection`が自動生成されるが、これは保存済みの※１と同じもの** である。
ここで **問題となるのが、axの凡例表示用`PathCollection`の保存時に重複チェックがある** こと。
**凡例に同じ色を保存しようとすると `AttributeError`（属性エラー）系で落ちてしまう 重複が取り除かれる予定だった**
コレが今回エラーで落ちた原因だったようだ。~~
追記（2018/04/23）：`PathCollection`は描画処理に関するものらしく記述が怪しすぎるので削除。
（実際のエラーは上記にも書いたように`ValueError`）

### 追記
本当の原因は`ValueError`。
`np.repeat([[True,True,True, False]],19,axis=0)`
のような値を取る`f_h.get_facecolor() != h.get_facecolor()`について
`any()` or `all()` してせずにif文にぶち込んでいること
要するにライブラリーのバージョン固有のバグでした。

## 解決法の説明【matplotlib v2.1.1以前】
**コレを防ぐためには、** 直前のseabornのplotでaxに保存されている **凡例表示用の`PathCollection`と衝突を回避する必要がある** 。
**今回は同じカテゴリカル変数を利用した描画のため、`PathCollection`の削除を選択した。**
凡例表示用の`PathCollection`は ax.collections (type:list) に保存されているため削除関数clearを利用する

```python3
ax.collections.clear()
```

clear後は、その直後のseabornの関数でhue（とpalette）を指定して、グラフを重ねようとした場合にエラー落ちしなくなるようだ。

全部コードを読んだわけじゃないので推測も混じってます。
何か見識あればコメントでお願いします。


#【matplotlib v2.1.2以降】のお話(追記)

```python3 
    ax = sns.pointplot(x="x", y="fitting_y", hue="y_limit", data=result, markers="")
    sns.pointplot(x="x", y="y", hue="y_limit", data=result, join=False)
```
と連続して記述してもエラー落ちはしなくなりました。
ごっそり重複チェックの部分のコードが消去されたためです(`_in_handles`関数)。
しかし、凡例は壊れますので、`groupby`を利用した正攻法を利用したほうが良いでしょう。


※一応、ax.collection.clear()を挟むことで解決することができます。しかし、今後安定した動作であるかの保証は無いです。詳細はコメント欄にて。

###壊れる凡例の例

![壊れる凡例の例](https://gist.githubusercontent.com/nukisashineko/6f6e29664d28510dee5eef1ced78be68/raw/b3d0bca904c6819d7da705275449894960266611/blog3.png)





### `_in_handles`関数が消去でなくエラー修正であったらならの妄想
ちなみに`matplotlib v2.1.1`の`legend.py`内にある`_in_handles`関数をおいて`any()`関数を利用してエラー部分を修正していたとしたら、`PathCollection`の指定時に`ax.collections.clear()`を使用せずとも、連続で書かれた`seaborn.pointplot`にて、壊れずに凡例の表示ができそうであった。

### その他雑記(alpha値の扱いとか)
`ax.collections[0].get_facecolor() != ax.collections[1].get_facecolor()`
が
`np.repeat([[True,True,True, False]],19,axis=0)`
とほぼ等しくなっていた原因は、`get_facecolor()`関数と`seaborn.pointplot`の色の管理の方法に問題があった。
`get_facecolor()`では`alpha`値まで管理されているが、`seaborn.pointplot`内の色の管理は`seaborn.color_palette`で行われる。
そのため、`palette=`の指定を行った場合は、必ず`alpha`値が`1`に統一されてしまう。
どういうことかというと、内部で`seaborn.color_palette`に変換されるが、`seaborn.color_palette`はRGB変換処理により`alpha`値の情報が削ぎ落とされてしまう。そのため、同じラベルに違う色を付けたところでalpha値の部分で常にFalseが出てしまう。
現在,`seaborn.pointplot`ではalpha値を制御して描画する機能はついていないようだ。（たぶん）

ただし`seaborn.regplot`などをみる限り`alpha`値を関数描画時に`scatter_kws`等の引数を用いて指定できるようになっているようだ。

`seaborn.pointplot`もそのうち機能がつくかも……しれないこともないかもしれない。


# 解決法の見つけ方
探し方が悪かったのかどこにも載ってなかったので、
pycharmでbreakpoint debugしながら、いじいじしてた。  

# ソースコード

詳細なソースコードは以下のgistのURLに置いておきますので気になった方は参照ください。
[seabornでhueを使いながら、複数のグラフを重ねたかった](https://gist.github.com/nukisashineko/6f6e29664d28510dee5eef1ced78be68)

# その他の解法とか？

- hueを使わずgroup_byしてfor文で回して、legendを自作する（上記記載済み）  
- jupter-notebook（web brower）では、一部エラーが出てもそのまま描画処理をしてくれるため、実用途に足るなら利用する