スクラッチでBar Chart Raceを実装(コード供養)

前回の記事で、Bar Chart Raceを作るライブラリを紹介しましたが、実は僕はこのライブラリが登場するよりも前、スクラッチでBar Chart Raceを実装したことがあります。
便利なライブラリが登場したので、今後スクラッチで作ることはおそらく無いのですが、せっかく作ったコードが勿体無いので供養も兼ねて紹介させていただこうと思います。

棒の伸びもライブラリのように滑らかな動きでは無いですし、順位の入れ替わりなどもバーが上下に滑らかに移動して入れ替わるのではなく、パッと切り替わるなど、全体的にパラパラ漫画感が強く出てる出来栄えなのであまり期待せずによろしくお願いします。

データだけではライブラリ付属のコロナウィルス感染者のデータを拝借します。僕のコードはNaNに対応できないので、NaNは0埋めしておきます。

# データだけはライブラリから拝借
import bar_chart_race as bcr
# サンプルデータ読み込み
df = bcr.load_dataset('covid19')
# NaNに対応できてないので0埋めしておく
df.fillna(0, inplace=True)

では、早速作っていきます。実装としては、matplotlibのアニメーション機能を使います。
FuncAnimation を使うので、実装としては次の記事と似ています。
参考: matplotlibの3次元プロットを回転するアニメーションで保存する

まず、パラパラ漫画の各コマを生成する関数を実装します。

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker


def draw_barchart(date):
    target_row = df.loc[date]
    target_data = target_row.T.sort_values(ascending=True).tail(10)

    ax.clear()
    # 棒グラフを描写
    ax.barh(target_data.index, target_data.values)
    dx = target_data.max() / 200

    for i, (name, value) in enumerate(target_data.items()):
        # 棒の先端部に項目名を出力
        ax.text(value-dx, i, name, size=14, ha='right',
                va='bottom', color="white", weight=600)
        # 棒の先に値を出力
        ax.text(value+dx, i, f'{value:,.0f}', size=14, ha='left', va='center')

    # 日付を出力
    ax.text(1, 0.4, date.strftime("%Y-%m-%d"), transform=ax.transAxes,
            color='#777777', size=23, ha='right', weight=800)
    # x軸のメモリの設定
    ax.xaxis.set_major_formatter(ticker.StrMethodFormatter('{x:,.0f}'))
    ax.xaxis.set_ticks_position('top')
    ax.tick_params(axis='x', colors='#777777', labelsize=12)
    # y軸のメモリ(項目名)を消す
    ax.set_yticks([])

    ax.margins(0, 0.01)
    ax.grid(which='major', axis='x', linestyle='-')
    ax.set_axisbelow(True)

    # 外枠を消す
    plt.box(False)

コメントを多めに付けましたが、関数の中で順に、棒グラフを書いたり文字を挿入したりメモリを調整したりとコマを組み立てています。
上記の関数でパラパラ漫画のコマが描写できるので、それを使って、アニメーションにします。

import matplotlib.animation as animation

fig = plt.figure(figsize=(10, 6), facecolor="w")
ax = fig.add_subplot(111)

animator = animation.FuncAnimation(
    fig, draw_barchart, frames=df.index, interval=400)
animator.save('bar-chart-race.mp4', writer="ffmpeg")

これで出力されるのが次の動画です。

やっぱり全体的にカクカクなりますね。
データとデータの間を補完してコマ数をもっと増やすなどしないとなめらなかなアニメーションにならないようです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です