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

# データ視覚化のデザインって？
UXやUIを突き詰めたサービスで有名なTHE GUILDのGo Andoさんがnoteで公開した、データの視覚化のポイントをまとめたもの。

https://note.mu/goando/n/n4a2735c3d69d

# #1 は？
* #1については以下を参照

https://qiita.com/skotaro/items/cdb0732ad1ad2a4b6236

# 注意事項
* フォントについて、macでは動きますが他OSだと多分動かないので別のフォントを指定してください。

```python
plt.rcParams['font.family'] = 'Hiragino Sans'  
```
の部分です。


# 8. 第2軸は使わない
![picture_pc_35cbe51d3cf1f3331ad189ac806eae85.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196857/a1e7dbfe-60e7-0750-8b89-6420903af77f.png)

```python
# ライブラリインポート
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.ticker import MultipleLocator


# データ
mau = [450,480,500,470,520,550,530,580,620,700,690,720]
g_rate = [0,8,0,-4,10,9,-6,12,4,12,-1,8]
g_rate = [one/100 for one in g_rate]
timeindex = pd.date_range('2019/1', periods=12, freq='MS')
mau = pd.Series(mau, index=timeindex, name='MAU(M)')
g_rate = pd.Series(g_rate,index=timeindex,name='前月成長率')

# pandasでのエラー回避用
from pandas.plotting import register_matplotlib_converters
register_matplotlib_converters()

# フォント設定
plt.rcParams['font.family'] = 'Hiragino Sans'  
plt.rcParams['font.weight'] = 'heavy'

# 上下に分割
fig = plt.figure(figsize=(8,5))
ax1 = fig.add_subplot(2, 1, 1)
ax2 = fig.add_subplot(2, 1, 2)

# 1. バーの色と幅を変更
ax1.plot(g_rate.index, g_rate,linewidth=5.,color="saddlebrown")
ax2.bar(mau.index, mau, width=20., color='darkorange', zorder=2, align='center')

# 2. y軸ラベルを表示、色、フォントサイズを指定
ax1.set_ylabel(g_rate.name,color="saddlebrown",fontsize=14,fontweight='bold')
ax2.set_ylabel(mau.name, color='gray',fontsize=14,fontweight='bold')

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

# 4. x軸y軸のtickを消す, 上のグラフに関してはx軸全て消す
ax1.tick_params(labelbottom="off",bottom=False, left=False) 
ax1.set_xticklabels([]) 
ax2.tick_params(bottom=False, left=False)

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

# 6. y軸のグリッドを表示、上のグラフに関しては色を黒に
ax1.grid(axis='y',color="black")
ax2.grid(axis='y')

# 7. 右枠を消す
ax1.spines['right'].set_visible(False)
ax2.spines['right'].set_visible(False)

# 8. 枠の色を変更
ax1.spines['bottom'].set_color('dimgray')
ax1.spines['top'].set_color('dimgray')
ax1.spines['left'].set_color('dimgray')
ax2.spines['bottom'].set_color('dimgray')
ax2.spines['top'].set_color('dimgray')
ax2.spines['left'].set_color('dimgray')

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

# 10. x軸のtickの位置を毎月ごとにする
ax2.xaxis.set_major_locator(mdates.MonthLocator())
ax2.xaxis.set_tick_params(labelsize=12)

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

# 11. 縦軸の範囲指定
ax1.set_ylim(-0.20,0.20)
ax2.set_ylim(0,800)
    
# 12. 縦軸tickの変更
vals_1 = [-0.2,0,0.2]
ax1.set_yticks(vals_1)
ax1.set_yticklabels(['{:,.0%}'.format(x) for x in vals_1],color="saddlebrown",fontsize=12,fontweight='bold')
vals_2 = [0,400,800]
ax2.set_yticks(vals_2)
ax2.set_yticklabels(['{}'.format(x) for x in vals_2] ,fontsize=12,fontweight='bold')

# 13. ylabelを揃える
axs = [ax1,ax2]
fig.align_ylabels(axs)
```

![temp_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196857/fe4aa9e3-edc2-bba1-c457-9f510bb62780.png)


 # 12.における縦軸を%にする方法についてはstackoverflowの回答を参考にした[^1]。
 # 13.はmatplotlibの公式docを参考にした[^2]。
またyラベルについて、1文字ずつ改行することで縦書きにすることも出来るが、コードがダサいのでしなかった。
（スマートなやり方があれば教えて下さい）

# 9. 要素は最低限に減らす
![picture_pc_a37bd978a068d0840f04250ca5b05a3a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196857/004f108f-cba8-4b05-254a-e2122fe09476.png)


```python
# データ
shipment_n = pd.DataFrame([52217, 9113, 4078],
                           index=['iPhone', 'iPad', 'Mac'],
                           columns=['amount'])

fig, ax = plt.subplots(figsize=(6, 3))
ax.bar(shipment_n.index,shipment_n["amount"])

# 1. bottom以外の枠を消す
sides = ['left','right', 'top']
[ax.spines[side].set_visible(False) for side in sides] 

# 2. bottom枠の色をグレーに
ax.spines['bottom'].set_color("dimgray")

# 3. y軸のtickを消す
ax.tick_params(left=False, labelleft=False)

# 4. x軸のラベルサイズを大きく、色もグレーに
ax.tick_params(axis='x', labelsize='xx-large',color="dimgray",labelcolor="dimgray")

# 5. バーの上側に実際の値を表示
vmax = shipment_n['amount'].max()
for i, value in enumerate(shipment_n['amount']):
    ax.text(i, value+vmax*0.06, f'{value:,}', fontsize='xx-large', va='center', ha='center', color='C0')
```
![tmp6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196857/81aa94cc-6713-9563-05eb-7a5879acbfc2.png)

 #4. はvmaxを使ってオフセットかけることにより実現しています。

# 10. キーメッセージを一番大きく書く
![picture_pc_c0ccda82a2e6153a56d22b40e1f7d12b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196857/a16f64d7-7c25-3a76-0446-931dd1819cf6.png)



```python
# 色
ori_blue = "#3674b3"
ori_lightblue = "#b3daff"


# 1. 全体に色を塗る
plt.rcParams["axes.facecolor"] = (1,1,1,0)

# 2. 背景を青に
fig, ax = plt.subplots(figsize=(6, 4),facecolor=ori_blue)

# 3. タイトル
fig.suptitle("iPhoneが出荷量の8割を占める",color="white",size=24,fontweight='bold',ha='center')

# 4. 薄い青でプロット
ax.bar(shipment_n.index,shipment_n["amount"],color=ori_lightblue)

# 5. 説明を追加
ax.text(-0.4,shipment_n["amount"].max()*1.2,"図表： 2017 Appleの出荷デバイス数",color=ori_lightblue,fontsize=12)

#6. bottom以外のframeを削除
sides = ['left','right', 'top']
[ax.spines[side].set_visible(False) for side in sides] 

# 7. bottom frameの色設定
ax.spines['bottom'].set_color(ori_lightblue)

# 8. y軸の範囲設定
ax.set_ylim(0,80000)

# 9. y軸のtick label を消す
ax.tick_params(left=False, labelleft=False)
    
# 10. x軸のラベル設定
ax.tick_params(axis='x', labelsize='xx-large',color=ori_lightblue,labelcolor=ori_lightblue)

# 11. バーの上側に実際の値を表示
vmax = shipment_n['amount'].max()
for i, value in enumerate(shipment_n['amount']):
    ax.text( i,value+vmax*0.06, f'{value:,}', fontsize='xx-large', va='center', ha='center', color=ori_lightblue)

# 保存するとき
plt.savefig("savefig_with_facecolor.png",facecolor=fig.get_facecolor())
```
![tmp8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196857/b860a097-5307-49aa-9911-82e06c923777.png)

色については、なんとなくで設定しています。
本家では背景の角が丸角になっていましたが、これは再現できませんでした。（結構調べても出てこなかった…）
またplt.savefig()で保存するときはfacecolor=で背景色を指定してやる必要があります[^3]。


[^1]: https://stackoverflow.com/questions/31357611/format-y-axis-as-percent
[^2]: https://matplotlib.org/3.1.1/gallery/pyplots/align_ylabels.html
[^3]: https://stackoverflow.com/questions/4804005/matplotlib-figure-facecolor-background-color