「データ視覚化のデザイン #1」をmatplotlibで実装する


データ視覚化のいろはを無視したグラフはニュースや学術論文によく現れます。いろんな資料からかきあつめたり苦労して測定したデータ、あるいは自分の部署の成果をかっこよく見せたい気持ちはわかりますが、たいていの場合「よく見せる」という欲求は色の濫用や3D化などデザイン要素の足し算として現れがちです。結果としてよく見せたいデータがごちゃごちゃした印象になってしまい、メッセージを読み取りにくいだけでなく時に誤解を生む図に仕上がってしまっていることも多いでしょう。

「データ視覚化のいろは」とは書きましたが、自分は実際に体系的に学んだことがあるわけではなく、ウェブや論文などで目にした良い例と悪い例からぼんやりと「こうすべきなのかな」という指針を認識している程度です。そんな折に、UXやUIを突き詰めたサービスで有名なTHE GUILDの方がデータ視覚化に関するnote記事を公開しているのをみつけました。
[データ視覚化のデザイン #1｜Go Ando / THE GUILD｜note](https://note.mu/goando/n/neb6ea35f1da3)
どれも私が実際に使うことはあまりないタイプの可視化の話ですが、こういう図を作る機会のある人は多いようでTwitterでも好評です。
[#goando_datasketch hashtag on Twitter](https://twitter.com/hashtag/goando_datasketch?vertical=default&src=hash)
[データ視覚化のデザイン - Twitter Search](https://twitter.com/search?vertical=default&q=%E3%83%87%E3%83%BC%E3%82%BF%E8%A6%96%E8%A6%9A%E5%8C%96%E3%81%AE%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3&src=typd)

そんな中で以下のツイートが目にとまりました。
<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">こういう知見をMatplotlibで実現する方法を誰か書いて欲しい / “データ視覚化のデザイン #1｜Go Ando / THE GUILD｜note” <a href="https://t.co/AkKTMhZBdA">https://t.co/AkKTMhZBdA</a></p>&mdash; からあげ (@karaage0703) <a href="https://twitter.com/karaage0703/status/998185762364448768?ref_src=twsrc%5Etfw">May 20, 2018</a></blockquote>

確かに指針を知るのと実装するのは別ですね[^1]。可視化の例を見る限り、ウェブに散らばる断片的情報を頼りにmatplotlibを使ってきた人がこれを実現するとなると時間がかかりそうです。以前Qiitaに書いた[Artistに関する基礎知識](https://qiita.com/skotaro/items/08dc0b8c5704c94eafb9)があればいろいろ自由に設定できることを示す良い例だなと思ったので、いくつか実装してみました。みなさん見慣れていると思われるデフォルトの見た目が下の画像のようになります。設定の解説もついているのでぜひ今後の参考にしてください。[ggplotは別の方が書いてくれることを期待しましょう。](https://twitter.com/goando/status/998237671985070080)

<img width="901" alt="手入れ前後の比較" src="https://qiita-image-store.s3.amazonaws.com/0/222849/6344f755-1ec7-a9d9-2d7a-4e6b07ab100c.png">


_見た目調整の個別相談を始めました。記事の最後にリンクがあります。_

# 準備
Jupyter notebook, python 3.6, matplotlib 2.2.0を使っています。

```python
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

matplotlib._version.get_versions()['version']
# 2.2.0
```

色や要素の数を勝手にいい感じにしてくれるseabornで実現できる部分もあるかもしれませんが、私は全然使ったことがないのでmatplotlibを直にいじっていきます[^2]。

noteの記事と同じようなデータを使って同じような図を作るので日本語フォントが使えるようにしましょう。matplotlibへの日本語フォント導入は[matplotlibで日本語 - Qiita](https://qiita.com/yniji/items/3fac25c2ffa316990d0c)を参照してください。

```python
matplotlib.font_manager._rebuild() # 問題の原因になることが多いフォントキャッシュを再構築しておきます
plt.rcParams['font.family'] = 'IPAexGothic'
```

# すっきりバープロット
<img src="https://qiita-image-store.s3.amazonaws.com/0/222849/f13f7a41-b040-3cbf-694c-08c2a8102027.png" width=500>

フリーザの戦闘力がいかに突出しているかを水平方向の視線移動だけで示した図です。以下の三点が考慮されています。

> 1. 伝えたいメッセージを明確にする
> 2. データの大きさを表すには比較する
> 3. どういう形が一番メッセージを表現できるか考える

これの右側の図を作ります。まずは戦闘力データを用意して、デフォルト設定のまま水平バープロットを作ってみます。

```python
combatpower = pd.DataFrame([530000, 120000, 10000],
                           index=['フリーザ', 'ギニュー', 'クリリン'],
                           columns=['戦闘力'])
# pd.Seriesでも可能（columnsの代わりにname）

combatpower.plot.barh()
# combatpower.plot(kind='barh') # これも同じ
```

![download-1.png](https://qiita-image-store.s3.amazonaws.com/0/222849/d38bb58f-614e-e4ed-6d5f-d8b30ad87e56.png)

全然だめですね。例と同じにするためには主に以下の設定が必要です。

1. 凡例を消す
2. バーの間を詰める
3. y軸の順番を逆に
4. 四方の枠(spines)を消す
5. y軸x軸のtickを消す
6. x軸のtick label(10000など)を消す
7. y軸のラベルサイズを大きく
8. バーの右側に実際の値を表示


```python
# 1. 凡例を消す legend=False
# 2. バーの間を詰める width=0.8
fig, ax = plt.subplots(figsize=(6, 3))
combatpower.plot.barh(legend=False, ax=ax, width=0.8)

# 3. y軸の順番を逆に
ax.invert_yaxis()

# 4. 四方の枠(spines)を消す
[spine.set_visible(False) for spine in ax.spines.values()]
# 以下でも可能
# sides = ['left', 'right', 'top', 'bottom']
# [ax.spines[side].set_visible(False) for side in sides] 

# 5. y軸x軸のtickを消す
# 6. x軸のtick label(10000など)を消す
ax.tick_params(bottom=False, left=False, labelbottom=False)

# 7. y軸のラベルサイズを大きく
ax.tick_params(axis='y', labelsize='x-large')

# 8. バーの右側に実際の値を表示
vmax = combatpower['戦闘力'].max()
for i, value in enumerate(combatpower['戦闘力']):
    ax.text(value+vmax*0.02, i, f'{value:,}', fontsize='x-large', va='center', color='C0')
```

![download-3.png](https://qiita-image-store.s3.amazonaws.com/0/222849/4dbfca79-e648-3581-70e2-e3d1f2db01bf.png)

フォントが違うので少し印象が異なりますが、だいたい再現できました。

## 解説
### barプロットの幅は`width`と`figsize`で調整
各バーの幅はwidthオプションによりx軸のスケールを使って指定できます。[matplotlibのドキュメント](https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.bar.html)によると`plt.bar/barh`のデフォルトはwidth/height=0.8ですが、pandasの`plot.bar/barh`ではwidth=0.45程度の値がデフォルトになっています[^3]。今回のようにカテゴリ名が各バーに対応する場合は各バーの位置は整数値が振られているので、横長の画像に対してwidth=0.8がちょうどよいです。同じ`width/height`でも`figsize`の縦横比によって印象が変わります。

### 四方の枠は`ax.spines`
[`Artist`の基礎](https://qiita.com/skotaro/items/08dc0b8c5704c94eafb9)でも書きましたが、[公式ドキュメント](https://matplotlib.org/tutorials/introductory/usage.html#parts-of-a-figure)を見ると通常「グラフの枠」と呼ばれるパーツはmatplotlibではspine（脊柱、背骨）と呼ばれていることがわかります。
<img width="400" alt="anatomy1.png" src="https://qiita-image-store.s3.amazonaws.com/0/222849/fb5b0e8d-88f4-eb9f-11cc-8509006fdbe9.png">

このオブジェクトは`ax.spines`というリストに`OrderedDict`として格納されています。使い方は通常の辞書と同じです。今回は`dict.values`を使い辞書の値である`Spine`オブジェクトをリスト内包表記でイテレートし非表示設定しています。色を変える`set_color`なども同様の手法で使えます。

```python
ax.spines
```
```:output
OrderedDict([('left', <matplotlib.spines.Spine at 0x1135c41d0>),
             ('right', <matplotlib.spines.Spine at 0x11338df28>),
             ('bottom', <matplotlib.spines.Spine at 0x1135d9080>),
             ('top', <matplotlib.spines.Spine at 0x1135d9198>)])
```

### tickとtick labelの調整は`ax.tick_params`が便利
tick（軸目盛り）とtick label（軸目盛りラベル）を消したり見た目を調整する際は、`Tick`オブジェクトなどに直接アクセスせずとも、`ax.tick_params`でたいていのことが済みます。x軸だけ、両軸、メジャー、マイナーなどの対象の指定方法や調整可能項目は[公式ドキュメント](https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.tick_params.html)を参照してください。

### `ax.text`の位置とフォーマット
バーの右側に数値を表示する際にバーとの間に一定の余白を確保するためにバーの最大値（ここではフリーザの戦闘力）を使って必要な余白を算出して位置をずらしています[^4]。また、単純に戦闘力を文字列にして表示するとコンマがないので、Python 3.6のf-strings機能を使ってカンマ付き数字`f'{value:,}'`にフォーマットしています。

### `'CN'`記法による色指定
ver.2から導入された便利な色指定方法です。詳しくは[こちらの記事](https://qiita.com/skotaro/items/5c9893d186ccd31f459d#cn%E8%A8%98%E6%B3%95%E3%82%92%E4%BD%BF%E3%81%86)を参照してください。

# 凡例は使わない
<img src="https://qiita-image-store.s3.amazonaws.com/0/222849/d6ee1114-1ed5-10ca-32c0-1c05bf213439.png" width=500>

これも右側の図を再現してみます[^5]。まずは図からだいたいの数字を目で読み取りDataFrameにします。データ表示した際に見やすいので会社名をindexにしていますが、もしかしたらデータ解析のお作法的には会社名をcolumnにすべきかもしれません。

```python
carsales = pd.DataFrame([[970, 1010, 1015, 1008],
                         [975, 1020, 1002, 1035],
                         [975, 985, 995, 999]],
                        index=['Toyota', 'VW', 'GM'], columns=[2013, 2014, 2015, 2016])
carsales
```
<img width="214" alt="Screen Shot 2018-05-23 at 15.08.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/222849/74a7a037-7315-c6e6-7adb-10e1c1dd8094.png">

初期設定のままプロットしてみます。データを表示した際の見やすさを優先して会社をindexにしたので、転置してxとyを入れ替える必要があります。

```python
carsales.T.plot()
```
![download.png](https://qiita-image-store.s3.amazonaws.com/0/222849/a3961e02-74f0-1571-612c-d8ad107306b7.png)

図の再現のためにいじるべき点は以下です。

1. 色を原色のRGBとは違ういい感じの赤青緑に
2. 凡例を消す
3. 線幅を太くする
4. y軸ラベルを表示
5. x軸y軸の表示範囲を変更
6. y軸ラベルの色を変更
7. x軸y軸のtick位置を変更
8. x軸y軸のtickを消す
9. x軸y軸のtick labelの色を変更
10. y軸のグリッドを表示
11. 左右と上の枠を消す
12. 下の枠の色を変更
13. 通常の凡例の代わりにプロットの右側にindexを表示


```python
# tickの位置、tick labelのフォーマットの設定はtickerを使う
# https://matplotlib.org/api/ticker_api.html 参照
from matplotlib.ticker import MultipleLocator

# 1. 色を原色のRGBとは違ういい感じの赤青緑に
# Set1 https://matplotlib.org/examples/color/colormaps_reference.html
# https://stackoverflow.com/questions/46148193/how-to-set-default-matplotlib-axis-colour-cycle
from cycler import cycler
c = plt.get_cmap('Set1').colors
plt.rcParams['axes.prop_cycle'] = cycler(color=c)

fig, ax = plt.subplots(figsize=(5, 3))
# 2. 凡例を消す
# 3. 線幅を太くする
carsales.T.plot(ax=ax, linewidth=4, legend=False)

# 4. y軸ラベルを表示
# 5. x軸y軸の表示範囲を変更
year = carsales.columns.values
ax.set(ylabel='販売台数（万）', ylim=(950, 1050), xlim=(year.min(), year.max()))

# 6. y軸ラベルの色を変更
ax.yaxis.label.set_color('gray')

# 7. x軸y軸のtick位置を変更
ax.yaxis.set_major_locator(MultipleLocator(50))
ax.xaxis.set_major_locator(MultipleLocator(1))

# 8. x軸y軸のtickを消す
ax.tick_params(bottom=False, left=False)

# 9. x軸y軸のtick labelの色を変更
ax.tick_params(axis='y', colors='gray') # colorではなくcolors
ax.tick_params(axis='x', colors='dimgray') # colorではなくcolors

# 10. y軸のグリッドを表示
ax.grid(axis='y')

# 11. 左右と上の枠を消す
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['top'].set_visible(False)

# 12. 下の枠の色を変更
ax.spines['bottom'].set_color('dimgray')

# 13. 通常の凡例の代わりにプロットの右側にindexを表示
for i, name in enumerate(carsales.index.values):
    ax.text(year.max()+0.03, ax.lines[i].get_data()[1][-1], name,
            color=f'C{i}', fontsize='large', va='center')
```
![download.png](https://qiita-image-store.s3.amazonaws.com/0/222849/46747f1d-6a4b-6f3b-b009-e29920b6b6be.png)


色の再現はかなり手抜きです。また、IPAexGothicフォントにはボールドがないので、weightは指定していません。

次は一本だけ目立たせる場合です。
<img src="https://qiita-image-store.s3.amazonaws.com/0/222849/9dda18fc-8060-2bf6-4b66-56771f937015.png" width=500>

前の例との違いは線と凡例代わりの文字列の色指定だけです。ここでは

1. ひとまず全部灰色でプロット
2. 注目したい線だけ色を変更
3. 文字列はforループ内でifを使って色指定

という方針で実装しました。

```python
fig, ax = plt.subplots(figsize=(5, 3))

# 1. ひとまず全部灰色でプロット
carsales.T.plot(ax=ax, linewidth=4, legend=False, color='lightgray')

# 2. 注目したい線だけ色を変更
# 目立たせたいデータのLine2Dオブジェクトをax.linesリストから取得。
# 今回のGMはリストの最後にあるので[-1]
ax.lines[-1].set_color('limegreen')

### 上と同じ部分は省略 ###

# 3. 文字列はforループ内でifを使って色指定
for i, name in enumerate(carsales.index.values):
    if name == 'GM':
        color = 'limegreen'
        size = 'x-large'
    else:
        color = 'gray'
        size = 'large'
    ax.text(year.max()+0.03, ax.lines[i].get_data()[1][-1], name,
            color=color, fontsize=size, va='center')
```
![download-1.png](https://qiita-image-store.s3.amazonaws.com/0/222849/c0308bc5-8fed-b404-243c-1a94a513e040.png)

## 解説
### color cycleに使う色の変更
color cycleとは複数データをプロットした際に自動的に割り振られる色のことです。設定されている色数以上のデータに対して最初の色から順に繰り返し使えるようにCyclerオブジェクトというものが使われています。詳しくは[この記事](https://qiita.com/skotaro/items/5c9893d186ccd31f459d#color-cycle%E3%81%A8%E3%81%AF)を参照してください。デフォルトでは`tab10`という10色が設定されていますが、これを変えるには以下のコマンドを実行します。

```python
plt.rcParams['axes.prop_cycle'] = cycler(color=色リスト)
```

色リストは自分で作っても良いのですが、ここではとりあえず原色ではない赤青緑がほしかったので[ドキュメント](https://matplotlib.org/tutorials/colors/colormaps.html)に載っているcolormap一覧から、離散的な色のリストであるQualitative colormapから`Set1`を選びました。`Colormap.colors`により色のリストが取得できます。

### `ax.set`で`set_*`系を一括設定
見た目を整えたグラフを作るとどうしても`ax.set_***`がずらっと並んでしまいます。何をしているかが一瞥できるので悪くはないのですが、行数を減らして短く書く方法を知っておくのもよいでしょう。`ax.set_hoge=fuga`系のコマンドは`ax.set({'hoge':fuga})`という形で辞書に入れて一行で書けます。`set_***`コマンド以外にも使えるようですが、詳しくは知りません。

### `Line2D.get_data`でプロットに使った数値を取得する
matplotlibでは全てのパーツが`Artist`と呼ばれるオブジェクトであり、描画に使う位置や見た目の数値は（おそらく）全て属性として各オブジェクトが保持しています。つまり一度プロットした線の元になったxy形式のデータや色も線の実体である`Line2D`オブジェクトにアクセスすると取得できます。今回は

```python
ax.lines[i].get_data()[1][-1]
```
の部分で`ax.lines`リストの`i`番目にある`Line2D`オブジェクトの`get_data`メソッドによりxyデータを取得し、yデータの一番最後の値を`ax.text`の位置に使っています。

# 目盛りラベルは傾けない
<img src="https://qiita-image-store.s3.amazonaws.com/0/222849/8b3218c7-7d68-9e18-fa69-628aefa08a71.png" width=500>


それっぽいデータを適当に作り、初期設定のままbarプロットを作ってみます。color cycleはデフォルトのtab10に戻してあります。

```python
mau = np.linspace(450, 990, 12) + np.random.randint(-50, 50, 12)
timeindex = pd.date_range('2017/5', periods=12, freq='MS')
# `freq='M'`だと月末の日付になってしまうので注意
mau = pd.Series(mau, index=timeindex, name='MAU')
mau.plot.bar()
```
![download-1.png](https://qiita-image-store.s3.amazonaws.com/0/222849/62d989f8-dbb7-1acd-33f7-39681da992e5.png)
色とx軸のtick labelが派手なことになってしまいました。実は、色の統一はcolorオプションで簡単にできるのですが、`DatetimeIndex`のtick labelが曲者です。pandasで時系列データを扱っている人の間では常識なのかもしれませんが、どうもpandasの`DatetimeIndex`はmatplotlib側に`datetime`型として渡されないためmatplotlib側の`datetime`フォーマット関連機能（今回は月のみ抽出）が使えないようです。
 [python - Matplotlib DateFormatter for axis label not working - Stack Overflow](https://stackoverflow.com/a/33746112/9131000)

したがって、上記SOの回答にもあるように直接matplotlibを使ってbarプロットを作る必要があります。

```python
plt.bar(mau.index, mau, width=10) 
# widthに何も指定しないと初期値width=0.8（0.8日分）となりほぼ線になってしまう。
```
![download-2.png](https://qiita-image-store.s3.amazonaws.com/0/222849/d57fe84c-460e-36f0-08cf-abd1625c01d1.png)

手を加えるべき点は以下です。

1. バーの色と幅を変更
2. y軸ラベルを表示、色を指定
3. y軸のtick位置の変更
4. x軸y軸のtickを消す
5. x軸y軸のtick labelの色を変更
6. y軸のグリッドを表示
7. 左右と上の枠を消す
8. 下の枠の色を変更
9. x軸のtick labelを月だけにする
10. x軸のtickの位置を毎月ごとにする
11. 各年の最初の月の下に年を表示


```python
import matplotlib.dates as mdates

fig, ax = plt.subplots(figsize=(5, 3))

# 1. バーの色と幅を変更
ax.bar(mau.index, mau, width=20, color='coral', zorder=2, align='center')

# 2. y軸ラベルを表示、色を指定
ax.set_ylabel(mau.name, color='gray')

# 3. y軸のtick位置の変更
ax.yaxis.set_major_locator(MultipleLocator(500))

# 4. x軸y軸のtickを消す
ax.tick_params(bottom=False, left=False)

# 5. x軸y軸のtick labelの色を変更
ax.tick_params(axis='x', colors='dimgray')
ax.tick_params(axis='y', colors='gray')

# 6. y軸のグリッドを表示
ax.grid(axis='y')

# 7. 左右と上の枠を消す
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

# 8. 下の枠の色を変更
ax.spines['bottom'].set_color('dimgray')

# ax.xaxis_date() # 必要ないっぽい

# 9. x軸のtick labelを月だけにする
# '%-m'はstrftimeのleading zeroなしmonth表記
ax.xaxis.set_major_formatter(mdates.DateFormatter('%-m')) 

# 10. x軸のtickの位置を毎月ごとにする
ax.xaxis.set_major_locator(mdates.MonthLocator())

# 一度figをdrawしないとmajor tickのpositionが更新されない
fig.canvas.draw()

# 11. 各年の最初の月の下に年を表示
for key, gr in mau.groupby(mau.index.year):
    i = np.where(mau.index == gr.index[0])[0][0]
    pos = ax.get_xmajorticklabels()[i].get_position()
#    ax.text(pos[0]-15, -200, key) # 線なしバージョン
# Axesの外に線を書くのは非常にめんどくさいのでannotateで代用
# annotateも矢印の始点の調整が意味不明なので空白で無理やり調整
# textcoordsを指定せずにデフォルトのデータ座標を使うとなぜか空白が反映されない。
    ax.annotate(f'         {key}', xy=(pos[0]-15, 0), xycoords='data',
                xytext=(0, -30), textcoords='offset points', color='dimgray',
                ha='center', va='bottom',
                arrowprops={'arrowstyle':'-', 'color':'dimgray'})
```
![download-4.png](https://qiita-image-store.s3.amazonaws.com/0/222849/eaedb3ba-6a39-7b6e-40c9-1afa65254c75.png)

## 解説
### グリッドの線がバーの上に重ならないように`zorder`を指定
グラフの描画は基本的にパーツを重ねていくだけなので、例えば複数の`ax.plot`は実行順によって線の重なり順をコントロールできます。しかし、plotとbarのように異なるオブジェクトはオブジェクトごとに重なり順が定義されているようで、実行順を変えても重なり順は変化しません。これは`zorder`オプションでどうにか調整が可能です。「どうにか」というのは`zorder`自体もあまり振る舞いがはっきりせず、とりあえず上にもってきたいパーツでは`zorder=10`にしたり、全て順番を指定する場合は`zorder`を1ずつ増やすのではなく5ずつ増やすなどのある程度の試行錯誤が必要です。今回は`zorder`の指定をしない状態だと`ax.grid`が上に来てしまったので`ax.bar`を`zorder=2`にしたら解決しました。

### すでに表示されている要素の位置に合わせて新しい要素を追加する
例えば、後から追加したcolorbarの高さをcontourfの図と同じにしたい場合などに、すでにあるcontourfのAxesの位置を取得してcolorbarの高さ設定に使うと、ちょうどいい数値を探るために試行錯誤を繰り返す必要がなく楽です。今回の例では、2017と2018の数字を表示する際に「各年の最初の月の半月分前の位置にannotateで線を引く」という部分にこの考え方が使われています。

### `get_position`は一度`fig.canvas.draw()`しないとダメ
描画した各オブジェクトの位置はそのオブジェクトに対して`get_position`メソッドを使えば取得できるのですが、一度描画しないと正しい値を取得できません。Jupyter notebookのinline表示を使っている場合は`get_position`で正しい数値を得るために二つの方法があります。一つは、セル実行後に`Figure`が表示されてから次のセルで目的のオブジェクトに対して`get_position`を実行する方法です。もう一つは`get_position`の前に`fig.canvas.draw()`を実行して`Figure`を表示することなく仮想的に一旦描画する方法です。今回は一つのセルで完結する後者の方法を使っています。

### Axesの外に線を引く
この部分はちょっと手こずりました。今回は`Axes`内の点から線を引きたかったので`ax.annotate`を使えましたが、以下のSOの回答によると、一般に`Axes`の外に線を引くには専用の`Axes`を作るかなりめんどうなテクニックを使う必要があるようです。どうしても`Axes`の外に線を引きたい場合は、ここだけ手で加工するのも悪くない選択肢だと思います。
[pandas - How to add hierarchical axis across subplots in order to label groups? - Stack Overflow](https://stackoverflow.com/a/44486955/9131000)
また、`ax.annotate`の矢印の始点は以下の回答のように調整できるはずなのですが、`Axes`の外だとうまくいきませんでした[^6]。
[python - How to set starting points of arrows in matplotlib's annotate? - Stack Overflow](https://stackoverflow.com/a/43800620/9131000)

---

元のnote記事は第一回目ということなので、またいい例があればこのような記事を書きます。

# 個別相談始めました
[matplotlib 最後の一歩](https://sites.google.com/view/matplotlib-last-one-step/)
見た目の微調整に苦労してる方、コーヒー1杯で相談に乗ります。

[^1]: 特にmatplotlibは「ここをこうやってこうしたいのにさっぱりわからない…」ということが何度も発生します。
[^2]: seabornなどのmatplotlibラッパーがいい感じにしてくれなかった場合は結局matplotlibを直にいじる必要が多いようなので、seabornユーザーにも役立つ話だと思います。
[^3]: 大変ややこしいことに、バー幅の調整は`plt.bar`では`width`、`plt.barh`では`height`を使わなければなりません。一方、pandasは`plot.bar`でも`plot.barh`でも`width`を使えます。
[^4]: textのpaddingに関するパラメータがあるかもしれませんが、みつかりませんでした。
[^5]: 原点が0でないグラフをけしからんとする向きもあるでしょうが、こういう業界ビッグ3の覇権争いみたいな図は順位の変動がはっきりわかった方が適切な文脈もあるでしょうしこのまま再現します。
[^6]: ちなみにこれら二つの回答はmatplotlibリポジトリでもたまに見かける名物回答者ImportanceOfBeingErnestさんのもので、彼の回答はいつも丁寧かつ正確でかなり信頼できます。