matplotlibの箱ひげ図でもx軸を間隔尺度にしたい！

　分布を含めてデータを一覧したい時、箱ひげ図はかなり強力な武器になります。

　しかし、基本的には箱ひげ図は、各データ間の大小をグラフに表しません（名義尺度）。順序を揃えることはあるかもしれませんが、それでもあくまで順序尺度どまりです。場合によっては、x軸同士の関係も見たいということがあるかもしれません。

# やってみる

　まず、以下のようなデータを考えます。あるゲームのランキングを国別にまとめたものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/e42789cb-ee81-c913-59ba-c10860c7ed0d.png)

　コードは次のような感じになります。（以下、importとかは省略）

```python
fig,ax = plt.subplots()
ax.boxplot(distributions)
ax.set_xticklabels(countries)
plt.show()
```

　例えば、国別に人口データあった場合、x軸を人口にしたいと考えます。`boxplot`では、`positions`を指定することによってできます。

```python
fig,ax = plt.subplots()
ax.boxplot(distributions, positions=populations) //positions
plt.show()
```

　結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/4f820e3f-caf8-ccf6-63ab-7106ccbe01a3.png)

　x軸のオーダーが大きすぎるため、箱ひげが針みたいになってしまいました。あと中国の人口がすごすぎて、それがなくとも純粋に見づらいです。箱の太さはデフォルトでは`0.5` に指定されているため、データに合わせて調整が必要になりそうです。今回は、人口の方に`log`をかけたり標準化をしたりして調整してみます。（箱の太さを調整したい場合、`widhts`というパラメータで操作することができます。）

```python
log_populations = np.log(populations)
fig,ax = plt.subplots()
ax.boxplot(distributions,positions=log_populations)
xfloor = math.floor(np.min(log_populations))
xceil = math.ceil(np.max(log_populations))
plt.xlim(xfloor,xceil)
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/95bd8aee-eba7-bde5-a2ba-2afbb1e19cae.png)

　**見づらい！！**

　箱ひげ図の横軸を自由にしてしまうと、このようなオーバーラップがあるから使われなかったのですね。あと、元々は単なる見栄えの問題であったはずの箱の太さが、まるで何か統計的意味を持ってしまうかのように見えてしまいます。これはグラフとしてあまりよくないです。

　ちなみに、分布x分布というデータであった場合、[二次元箱ひげ図](https://twitter.com/fronori/status/720170104831381504)というのも考案されていますが、同様の問題がある（オーバーラップのリスクが高い）ので実用的ではないようです。

# 悪あがき

　とりあえずx軸がすごいことになっているので直します。

```python
log_populations = np.log(populations)
fig,ax = plt.subplots()
ax.boxplot(distributions,positions=log_populations)
xfloor = math.floor(np.min(log_populations))
xceil = math.ceil(np.max(log_populations))
ax.set_xticks(range(xceil))
ax.set_xticklabels(range(xceil))
plt.xlim(xfloor,xceil)
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/d7847533-b633-6629-5707-f875e9c3854a.png)

　あと棒も微妙に細くします。

```python
log_populations = np.log(populations)
widths = np.ones(len(distributions))*0.1
fig,ax = plt.subplots()
ax.boxplot(distributions,positions=log_populations,widths=widths)
xfloor = math.floor(np.min(log_populations))
xceil = math.ceil(np.max(log_populations))
ax.set_xticks(range(xceil))
ax.set_xticklabels(range(xceil))
plt.xlim(xfloor,xceil)
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/68f88aaa-919e-c058-1e48-36db15b02f3b.png)

　今更ですが、どのデータがどこの国のものかわかりづらいので、グラフ上にラベルを表示することにします。実はけっこうこれが面倒くさくて、`boxplot`の命令一発でやるのが難しいので、for文の中で処理することになります。

```python
log_populations = np.log(populations)
fig,ax = plt.subplots()
for d,c,l in zip(distributions,countries,log_populations):
    ax.boxplot(d,positions=[l],widths=[0.1]) # リストで指定することに注意
    pos = (l+0.1,np.median(d))
    ax.annotate(c,pos)
xfloor = math.floor(np.min(log_populations))
xceil = math.ceil(np.max(log_populations))
ax.set_xticks(range(xceil))
ax.set_xticklabels(range(xceil))
plt.xlim(xfloor,xceil)
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/ce149e16-16fa-4f67-1c2e-e01499cca683.png)

　うーん、最大限努力したけど、オーバーラップがある以上、やっぱり見づらい。

# 結論

　箱ひげ図のx軸を間隔尺度にするのは、技術的には可能ですが、

- オーバーラップがあると根本的に見づらい
- 箱の太さに統計的な意味があるように見えてしまう

　というデメリットがあるので、あまり良いソリューションではなさそうです。ラベル別の分布を一覧するなら強力なツールなので、使い分けが重要ですね。