LoginSignup
5

More than 3 years have passed since last update.

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

Posted at

データ視覚化のデザインって?

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

#1, #2 は?

注意事項

  • フォントについて、macでは動きますが他OSだと多分動かないので別のフォントを指定してください。
plt.rcParams['font.family'] = 'Hiragino Sans'  

の部分です。

13. 表にグラフを入れると効果的

picture_pc_1bd02bdb34795599c91bc4e22a5657e2.png

pandasを利用。

import pandas as pd
import numpy as np

%matplotlib inline
# data
apple_products = pd.DataFrame({"プロダクト":["iPhone","iPad","Mac","Services","Other"],
                              "売上(Mドル)":[141319,19222,25859,29980,12863],
                             "ユニット":[216756,43753,19251,np.nan,np.nan]})

# 値のフォーマット
format_dict = {'売上(Mドル)':'{0:,.0f}', 'ユニット':'{0:,.0f}'}

# グラフの設定をしながら表示
(apple_products
 .style
 .format(format_dict)
 .hide_index()
 .bar(color="#99ceff", vmin=0, subset=['売上(Mドル)'], align='zero')
 .bar(color="#ff999b", vmin=0, subset=['ユニット'], align='zero'))

スクリーンショット 2019-12-03 21.55.48.png

  • 値のフォーマットの部分では3桁ごとにカンマ区切りに1
  • 表についてはpandasのstyling機能を使用2
  • nanについて、0で埋めても良かったがユニットにservicesとotherは該当しないだろうという理由からnanのままにした
  • .bar()の部分について、align='zero'と指定することで、グラフが適度な大きさになる

15. 割合の比較には積み上げグラフが便利

picture_pc_ef8b0b193e70633750354d6f86d444df.png

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates


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

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

# データ
music_env_df = pd.DataFrame({"ラジオ":[0.4,0.21],"CD・ダウンロード":[0.22,0.44],"動画配信":[0.2,0.3],"音楽配信":[0.18,0.05]},
            index=["GLOBAL","JAPAN"])
# 積み上げグラフ用
music_env_df.T.cumsum()
# グラフの色
bar_colors = ["#3B7780","#98C550","#7FC2CB","#E9C645"]

# x ticklabel を取得
x = music_env_df.index
# グラフの各項目名
keys = music_env_df.keys()

fig,ax = plt.subplots(figsize=(7,7))

# 1. 左右の枠を消去
sides = ['left','right']
[ax.spines[side].set_visible(False) for side in sides] 

# 2. 左軸メモリ、メモリラベル削除
ax.tick_params(left=False, labelleft=False)

# 3. 上下枠線の色変更
ax.spines['bottom'].set_color("dimgray")
ax.spines['top'].set_color("dimgray")

# 4. x軸メモリ設定
ax.tick_params(axis='x', labelsize='x-large',color="dimgray",labelcolor="dimgray")

# 5. 積み上げグラフをプロットおよびプロットの情報を格納
bar_info = []
for i in range(len(keys)):
    if i == 0:
        bar_info.append(ax.bar(x, music_env_df.T.iloc[i],width=0.5,color=bar_colors[i]))
    else:
        bar_info.append(ax.bar(x, music_env_df.T.iloc[i], bottom=music_env_df.T.cumsum().iloc[i-1],width=0.5,color=bar_colors[i]))

# 6. 各グラフの項目
for i,one in enumerate(bar_info):
    # %の数値を格納
    bar_center = [[0,0],[0,0]]
    #  棒グラフ同士の差を強調する線の座標を格納
    bar_line = [[0,0],[0,0]]
    for j,one_bar in enumerate(one):
        bar_center[j][0] =  one_bar.xy[0]+one_bar.get_width()/2
        bar_center[j][1] =  one_bar.xy[1]+one_bar.get_height()/2
        # 項目名を表示
        if j == 0:    
            ax.annotate(keys[i],xy=(0,0),xycoords="data",
                       xytext=(-0.4,bar_center[j][1]),
                       ha='right',color=bar_colors[i],fontsize=16)
            bar_line[j][0] = one_bar.xy[0]+one_bar.get_width()
            bar_line[j][1] = one_bar.xy[1]
        else:
            bar_line[j][0] = one_bar.xy[0] - bar_line[0][0]
            bar_line[j][1] = one_bar.xy[1] - bar_line[0][1]
        # 割合の数値(%)を表示
        ax.annotate(f'{one_bar.get_height():.0%}',xy=(0,0),xycoords="data",
                   xytext=(bar_center[j][0],bar_center[j][1]),
                   ha="center",va="center",color="white",fontsize=16)
        # 強調の線を表示
        ax.arrow(bar_line[0][0],bar_line[0][1], bar_line[1][0], bar_line[1][1], head_width=0, head_length=0, ec='dimgray')

# 7. 縦軸の領域を設定
ax.set_ylim(0,1)

tmp9.png

  • 積み上げ用に累和データを作成(music_env_df.T.cumsum())
  • #6の強調の線については、GLOBAL棒グラフの右上からJAPAN棒グラフの左上を結ぶようにしている

番外編 #1

DjHmYHNUcAE0dto.jpeg

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates


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

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

# データ
icing_method = pd.DataFrame([0.35, 0.19, 0.13,0.05,0.03,0.02],
                           index=['アイスバス 2℃', 'アイスバス 8℃', '水掛け+アイスマッサージ','水掛け','扇風機','静脈アイシング'],
                           columns=['冷却スピード'])

icing_detail = ["2℃のアイスバスに全身浸かる","8℃のアイスバスに全身浸かる","12℃の水掛け+アイスマッサージ",
                "15℃の水道水を全身にかけ続ける","室温22℃で扇風機の風にあたる","(頸部・腋部・鼠径部)"]

# バーの色だけオリジナルで指定
ori_blue = "#71C0F9"

fig, ax = plt.subplots(figsize=(12, 6))

icing_method.plot.barh(legend=False, ax=ax, width=0.8,color=ori_blue)

# 1. タイトル設定
plt.title("冷却方法と冷却スピード",fontsize=24,fontweight='bold',color="dimgray")

# 2. 左に余白を多めに作る
plt.subplots_adjust(left=0.35)

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

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

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

# 6. x軸値ラベルの設定
ax.set_xticks([i*0.1 for i in range(5)])
ax.tick_params(axis='x', labelcolor="silver")

# 7. x軸の範囲設定(0.4までにするとx=0.4のグリッドが出てこないため)
ax.set_xlim(0,0.41)

# 8. x軸グリッドの設定
ax.grid(axis="x")

# 9. x軸のラベル設定
ax.set_xlabel("10秒あたりに下がる体温(℃)",fontsize="x-large",fontweight="bold",color="silver")

# 10. バーの右側に実際の値、右側に項目と補足説明を表示
vmax = icing_method['冷却スピード'].max()
for i, (value,main_label,sub_label) in enumerate(zip(icing_method['冷却スピード'],icing_method.index,icing_detail)):
    ax.text(value+vmax*0.02, i, f'{value:,} ℃', fontsize='x-large', va='center', color=ori_blue)
    ax.text(-0.01, i-0.1,main_label  , fontsize='xx-large', va='center',ha='right',color="dimgray")
    ax.text(-0.01,i+0.25, sub_label, fontsize='x-large' ,va='center',ha='right', color="silver")

tmp12.png

  • 項目と補足説明のように、同じtext内でフォントサイズを変えることが出来ないので、別々に分けて表示している

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5