最近、Youtubeをみていると下で見るような「 ぬるぬる動く棒グラフ 」を見かけるようになりました。
自分でも、この「 ぬるぬる動く棒グラフ 」を作りたくて調べてみました。
これらのグラフは、常に上位数個の情報のみを表示させて時系列的に順番が入れ替えるように動的に表示させます。情報量を抑えつつも、経時変化も追えるし思わず見入ってしまうグラフです。
「 ぬるぬる動く棒グラフ 」の正体は?
さて、この「ぬるぬる動く棒グラフ」ですが、ネットで調べてみると「Bar Chart Race」と呼ばれており、PythonのMatplotlibを用いて作成が可能です。
matplotlibについては、公式ページとこのまとめページを参考に作成しました。
以下にコードと説明を示していきます。
「ぬるぬる動くグラフ」のコード
matplotlib, pandasのインポート、データの読み込み
描画、データ処理に必要なmatplotlib, pandasをインポートします。
matplotlibの結果をインラインでjupyter notebook上に表示させるために’%matplotlib inline’を追加しております。
今回用いるデータは’city populations dataset‘でGithub上で公開されているデータをpandasで読み込みます。
このdatasetの中の’name’, ‘group’, ‘year’, ‘value’の4つのcolumnを取り出します。
1 2 3 4 5 6 7 8 9 10 11 |
import pandas as pd import matplotlib.pyplot as plt import matplotlib.ticker as ticker import matplotlib.animation as animation from IPython.display import HTML %matplotlib inline df = pd.read_csv('https://gist.githubusercontent.com/johnburnmurdoch/4199dbe55095c3e13de8d5b2e5e5307a/raw/fa018b25c24b7b5f47fd0568937ff6c04e384786/city_populations', usecols=['name', 'group', 'year', 'value']) df.head(3) |
name | group | year | value | |
---|---|---|---|---|
0 | Agra | India | 1575 | 200.0 |
1 | Agra | India | 1576 | 212.0 |
2 | Agra | India | 1577 | 224.0 |
Datasetの準備
pandasの機能を使って指定した年(例では2020年)のうち’value’の大きい「Top10について表示します。
1 2 3 4 5 |
current_year = 2020 dff = (df[df['year'].eq(current_year)] .sort_values(by='value', ascending=False) .head(10)) dff |
name | group | year | value | |
---|---|---|---|---|
6047 | Tokyo | Asia | 2020 | 38323.0 |
1326 | Delhi | India | 2020 | 29348.0 |
5549 | Shanghai | Asia | 2020 | 27137.0 |
691 | Beijing | Asia | 2020 | 24201.0 |
3750 | Mumbai | India | 2020 | 22838.0 |
5447 | Sao Paulo | Latin America | 2020 | 22119.0 |
3576 | Mexico City | Latin America | 2020 | 21868.0 |
1338 | Dhaka | Asia | 2020 | 20989.0 |
1197 | Cairo | Middle East | 2020 | 20568.0 |
4681 | Osaka | Asia | 2020 | 20523.0 |
基本の棒グラフ
棒グラフと軸の作成を行う。棒グラフの作成には、’ax.barh(x, y)’を使用します。
1 2 3 4 5 6 |
fig, ax = plt.subplots(figsize=(15, 8)) ax.barh(dff['name'], dff['value']) # matplotlibで作成した図を画像として保存した場合はfig.savingを使う。 # fig.savefig('1-1_a.png', # facecolor=fig.get_facecolor(), edgecolor=fig.get_edgecolor()) |
この設定では最も’Value’の高いものが一番下に表示されてしまいます。
棒グラフの色設定, ラベル設定
次に、棒グラフに色をつけて表示させ、かつラベルとして’Value’と’Group’を各棒グラフに表示させます。
これにはmatplotlibの[ colors ]と[ group_lk ]を用います。
1 2 3 4 5 6 7 |
colors = dict(zip( ['India', 'Europe', 'Asia', 'Latin America', 'Middle East', 'North America', 'Africa'], ['#adb0ff', '#ffb3ff', '#90d595', '#e48381', '#aafbff', '#f7bb5f', '#eafb50'] )) group_lk = df.set_index('name')['group'].to_dict() |
[ group_lk ]は’name’と’group’をまとめて表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
fig, ax = plt.subplots(figsize=(15, 8)) dff = dff[::-1] # flip values from top to bottom # pass colors values to `color=` ax.barh(dff['name'], dff['value'], color=[colors[group_lk[x]] for x in dff['name']]) # iterate over the values to plot labels and values (Tokyo, Asia, 38194.2) for i, (value, name) in enumerate(zip(dff['value'], dff['name'])): ax.text(value, i, name, ha='right') # Tokyo: name ax.text(value, i-.25, group_lk[name], ha='right') # Asia: group name ax.text(value, i, value, ha='left') # 38194.2: value # Add year right middle portion of canvas ax.text(1, 0.4, current_year, transform=ax.transAxes, size=46, ha='right') |
表の書式を整える
続いては、’ draw_barchart ‘を用いて、以下の表の書式を整えます。
- Text: Update font sizes, color, orientation (フォントサイズ、色、向きの調整)
- Axis: Move X-axis to top, add color & subtitle (x軸を上に移動し、subtitleを追加)
- Grid: Add lines behind bars (棒グラフの背景にグリッド線を追加)
- Format: comma separated values and axes tickers (‘Value’と’x軸’の数字の表示をコンマで示す Ex. 10000 → 10,000)
- Add title, credits, gutter space (タイトル・クレジット・スペースの追加)
- Remove: box frame, y-axis labels (枠線、y軸のラベルの除去)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
fig, ax = plt.subplots(figsize=(15, 8)) def draw_barchart(year): dff = df[df['year'].eq(year)].sort_values(by='value', ascending=True).tail(10) ax.clear() ax.barh(dff['name'], dff['value'], color=[colors[group_lk[x]] for x in dff['name']]) dx = dff['value'].max() / 200 for i, (value, name) in enumerate(zip(dff['value'], dff['name'])): ax.text(value-dx, i, name, size=14, weight=600, ha='right', va='bottom') ax.text(value-dx, i-.25, group_lk[name], size=10, color='#444444', ha='right', va='baseline') ax.text(value+dx, i, f'{value:,.0f}', size=14, ha='left', va='center') # ... polished styles ax.text(1, 0.4, year, transform=ax.transAxes, color='#777777', size=46, ha='right', weight=800) ax.text(0, 1.06, 'Population (thousands)', transform=ax.transAxes, size=12, color='#777777') ax.xaxis.set_major_formatter(ticker.StrMethodFormatter('{x:,.0f}')) ax.xaxis.set_ticks_position('top') ax.tick_params(axis='x', colors='#777777', labelsize=12) ax.set_yticks([]) ax.margins(0, 0.01) ax.grid(which='major', axis='x', linestyle='-') ax.set_axisbelow(True) ax.text(0, 1.12, 'The most populous cities in the world from 1999 to 2020', transform=ax.transAxes, size=24, weight=600, ha='left') ax.text(1, 0, 'by @Mark; credit @https://life100create.com/', transform=ax.transAxes, ha='right', color='#777777', bbox=dict(facecolor='white', alpha=0.8, edgecolor='white')) plt.box(False) draw_barchart(2020) |
棒グラフを動かす
棒グラフを動かすには、FuncAnimation を使用します。
今回の例の場合は、繰り返し操作は’draw_barchart’です。
そして、’frames’という引数も使用します。これは、どのパラメータで’draw_barchart’を実行するかを指定することができます。
今回の例では、’year’を指定して範囲を1999 から 2020までとしました。
1 2 3 4 |
fig, ax = plt.subplots(figsize=(15, 8)) animator = animation.FuncAnimation(fig, draw_barchart, frames=range(1999, 2021)) HTML(animator.to_jshtml()) # or use animator.to_html5_video() or animator.save() |
最後に
今回は「ぬるぬる動く棒グラフ」である「Bar chart race」をPythonのmatplotlibを用いて描いてみました。
このグラフは、常に上位数個の情報のみを表示させて時系列的に順番が入れ替えるように動的に表示させるため情報量を抑えつつも、経時変化を追えるインタラクティブなグラフです。
今回は、ベーシックな棒グラフの情報のみでしたが、次は別のグラフ情報で経時的なグラフを作成したり、最終的には複数のグラフ要素を取り込みたいなと考えています。
コメント