plotly入門 データ可視化

Python Plotly入門 - アニメーション(animation)

今回はちょっと応用ですがPython Plotlyのアニメーション機能をご紹介したいと思います。

自分で分析しているときにはあまり必要ないかもしれませんが、プレゼンなど誰かに見せるときに、強調したい箇所をより強調することが可能ですので、うまく使えば効果的です。

今回作成する棒グラフと線グラフはこのようなものです。

株価の累積リターンを表す棒グラフ

株価の推移を表す線グラフ

その他のプロットと違い、最初は理解が難しいので本記事を読んで理解いただければ幸いです。

公式HPの解説はこちらです。

また、細かいプロパティの説明はこちらにあります。

では、見ていきましょう。

Plotly Expressを使ったアニメーション

まずはPlotly Expressを使った方法です。

これが一番簡単な方法で、非常に簡単にアニメーションを付けることができます。

以下のような、2020年1月から12月までの株価リターンのデータを使用します。

銘柄を列ごとではなく、一つの列にまとめているので、変数名はdf_verticalとしています(ネーミングセンスがなくてすみません)。

まずは、plotly expressをインポートします。

import plotly.express as px

あとは、以下のコードだけです。

1fig = px.bar(df_vertical, x='name', y='return', color='name',
             animation_frame='month'
             range_y=[-0.05, 0.05])
fig.show()

ポイントは2行目です。

animation_frameに"month"を指定することで、"月"単位でアニメーションを付けるということです。

1月から12月まで月単位で動かすということですね。

これだけでスタートボタン、ストップボタン、スライダーがついて、アニメーション機能が実現できます。

なお、スライダーについてはアニメーションがつかないようです。

plotly.Graph_objectsを使ったアニメーション

では、自分で色々細かく設定するためにplotly.graph_objectsを使ってアニメーション付きのグラフを作成しましょう。

以下のような、株価の累積リターンのデータを使用します。

名前はdf_cumsumです。

棒グラフのアニメーションをボタンで実行する

まず、先ほどの例と同様に、棒グラフでボタンを押したらアニメーションで動くようなグラフを作成したいと思います。

とりあえずplotly.graph_objectsインポートしましょう。

import plotly.graph_objects as go

大きく以下の4つのパーツから成ります。

  • 初期表示のデータを設定する(data)
  • レイアウトを設定する(layout)
  • フレームを設定する(frames)
  • グラフを作成する。

3つ目の「フレームを設定する」というのがアニメーションで新しく出てきた部分です。

では一つずつ見ていきましょう。

初期表示のデータを作成する

まずは、初期表示用のデータを作成します。

data = go.Bar(x=df_cumsum.columns,
                      y=df_cumsum.loc[1],
                      width=0.3,
                      marker_color='royalblue',
                       texttemplate='%{y:0.2%}',
                       textposition='outside')

初期表示用なので、データは1つです。

これだけでもgo.Figure(data)とすれば以下の用なグラフが出来上がります。

レイアウトを設定する

次にレイアウトを設定します。

好みですが少しだけ見やすくしましょう。

汎用的な部分については、見やすさのためlayout_settingsという辞書型変数で定義しておきます。

layout_settings = dict(plot_bgcolor='white',
                       xaxis=dict(showline=True,
                              linewidth=1,
                              linecolor='lightgrey'),
                   yaxis=dict(range=(-0.04, 0.04),
                              showline=True,
                              linewidth=1,
                              linecolor='lightgrey',
                              zeroline=True,
                              zerolinecolor='lightgrey',
                              zerolinewidth=1,
                              tickformat='.2%'))

次に実際にレイアウトを設定します。

layout = go.Layout(layout_settings,
                   updatemenus=[dict(type='buttons',
                                     buttons=[dict(label='play',
                                                   method='animate',
                                                   args=[None])],
                                     x=-0.08,
                                     y=1.02)],
                   title=dict(text='2020年の累積リターン<br>1月まで',
                              font_color='grey')
                   )

アニメーションで重要なのは、2行目から7行目です。

updatemenusというプロパティをリストで設定しています。

その中身は辞書形式になっており、設定しているのはtypebuttonsxyです。

typeに"buttons"を設定することでボタンを配置し、それを押すことでグラフが動きます。

なお、"dropdown"を指定するとドロップダウンが作成されます。

続いて、buttonsプロパティでボタンの設定をします

labelでボタンに表示する名称を設定し、methodに"animate"することでアニメーション機能を追加します。

最後のargsはリスト形式で設定し、初めの要素にNoneを指定すると、あとで設定するすべてのデータを順番に表示することになります。

あとで詳しく説明しますが、ドロップダウンでは選んだものだけを表示するのでNoneではなく、個別に設定します。

xとyはボタンを配置する場所です。

これを表示すると以下のようになっています。ただ、まだこの段階ではplayボタンを押しても何も動きません。

フレームを設定する

ここからグラフが動くためのアニメーション機能を設定していきます。

playボタンを押すと動くようにしたいのですが、ボタンを押したら何が動くかを設定しなければなりません。

この表示するグラフの断面をframeと呼びます。

そして、複数のframeがパラパラと動いていくイメージです。

フレームのイメージ

実際のコードで確認した方がわかりやすいと思いますので、コードを見てみましょう。

ここでは、1月から12月までの12個のフレームを作成します。

具体的には1月から12月までの12個のデータとレイアウトの断面を作成します。

frames = []
for month in range(1, 13):
  frame = go.Frame(data=[go.Bar(x=df_cumsum.columns, y=df_cumsum.loc[month])],
                   layout=go.Layout(title_text=f'2020年の累積リターン<br>{month}月まで'),
                   name=month)
  frames.append(frame)  

イメージとしては、通常のグラフだとgo.Figure()でdataとlayoutを渡していましたが、これをgo.Frame()に渡し、このフレームを複数作成するということです。

3行目から5行目で、go.Frameでフレームとしてデータとレイアウトを作成しています。

また、nameにもmonthを指定することで、フレームに名前を付けています。

これはすべての断面を順番に表示する場合には不要ですが、ドロップダウンなどで表示するフレームを指定する場合には必要になってきます。

グラフを作成する

最後に、go.Figure()でグラフを作成します。

今まではgo.Figure()にはdataとlayoutを設定するだけでしたが、ここではframesも追加で指定します。

fig = go.Figure(data=data, layout=layout, frames=frames)
fig.show()

棒グラフのアニメーションをスライダーで実行する

次にボタンではなく、スライダーを使ったアニメーション機能をご紹介します。

dataとframesについてはボタンの場合と同じで、layoutだけが違いますので、そこだけ解説します。

まず、スライダーの各ステップを定義します。

1月から12月までの12個のステップを設定します。

steps = []
for i in range(1, 12):
    step = dict(method="animate",
                args=[[i],
                      {"title": f'<b>{i}月のリターン'}],
                label=f'{i}月')
    steps.append(step)

methodに"animate"を設定することで、アニメーション機能を付けます。

こちらの記事「Python Plotly入門 – スライダー(slider)」にあるように"update"を指定すると通常のアニメーションのない更新になります。

argsはリスト形式で設定しますが、一つ目がframeに渡す値です。

ここでは月を表す数字を設定しており、この月とframeのnameプロパティが一致するframeを表示してくれます

2番目の要素はタイトルを設定し直しています。

この各stepをstepsというリスト変数に追加しています。

次に、作成したステップをスライダーに設定します。

ここではslidersという変数に以下のように設定します。

sliders = [dict(active=0,
                currentvalue={"prefix": "対象月: "},
                steps=steps)]

active=0とすることで一番初めのステップをデフォルト設定にしています。

選択されている値の表示方法をcurrentvlalueで設定しています。

3行目では、その前に作成したステップをstepsというプロパティに設定しています。

これを表示すると以下のようになります。

fig = go.Figure(data=data, layout=layout, frames=frames)
fig.show()

スライダーで月を選択することにより、選択した月のデータがアニメーションとともに表示されます。

棒グラフのアニメーションをドロップダウンで実行する

次に、ドロップダウンで選択した場合にアニメーション機能を追加したいと思います。

こちらも、dataとframesに変更はありません。

ボタンのときにtypeに"button"を設定していましたが、ここでは"dropdown"を設定するようにします。

まずは、ドロップダウンの設定をしたいと思います。

dropdown_list = []
for i in range(1, 13):
  dropdown_dict = dict(args=[[i]],
                      label = f'{i}月',
                      method = "animate",
                      )
  dropdown_list.append(dropdown_dict)

こちらもスライダーのときと同じで、argsの引数の1つ目に月を表す数字を設定します

これにより、例えばラベルが"5"月のところをクリックしたら、5という情報がフレームに渡され、nameが5に対応するフレームが表示されます。

それ以外の設定もスライダーのときと同じです。

最後にdropdown_lsitというリスト変数に各ドロップダウンの設定を追加しています。

続いて、レイアウトのupdatemenusにドロップダウンリストを設定します。

layout = go.Layout(layout_settings,
                   updatemenus=[dict(type='dropdown',
                                     buttons=dropdown_list,
                                     borderwidth=0,
                                     x=-0.08,
                                     y=1.02)],
                   title=dict(text='2020年の累積リターン<br>1月まで',
                              font_color='grey')
                   )

typeに"dropdown"をbuttonsにdropdown_listを設定しています。

borderwidthはボタンの線の太さを設定しています。

これを表示すると以下のようになります。

fig = go.Figure(data=data, layout=layout, frames=frames)
fig.show()

線グラフでアニメーションを作成する

最後に線グラフでアニメーションを追加してみましょう。

ボタンを押すと

データは2020年の各月末の株価を1月末の株価で割って基準化したものを使います。

変数名はdfです。

まずは、今までと同じように初期表示するデータ部分を作成しましょう。

この場合ですと、各銘柄について1番初めの時点の基準化した株価を設定します(つまりすべて100)。

data = []
colors = ['royalblue', 'tan', 'darkorange', 'lightblue']
for i, col in enumerate(df.columns):
  data.append(go.Scatter(x=df.iloc[:1].index,
                      y=df.iloc[:1][col],
                      marker_color=colors[i],
                      marker_size=10,
                      name=col
                 )
  )
go.Figure(data)

この状態で描画すると、2020年1月31日の100のところに4点ともプロットされています。

続いてレイアウトを設定します。

まずは、軸や背景色など一般的な部分です。

アニメーションとは関係ないため、わかりやすさのためにlayout_settingsという辞書型変数で別に設定しています。

layout_settings = dict(plot_bgcolor='white',
                       legend=dict(orientation='h',
                                   x=0, y=1.08),
                       title=dict(text='<b>各社の株価',
                                  font_color='grey'),
                       xaxis=dict(showline=True,
                                  linewidth=1,
                                  linecolor='lightgrey',
                                  range=('2020-1-1', '2021-1-31')),
                       yaxis=dict(showline=True,
                                 linewidth=1,
                                 linecolor='lightgrey',
                                 zeroline=True,
                                 zerolinecolor='lightgrey',
                                 zerolinewidth=1,
                                 range=(0, 200)
                                 )
                       )

では、以下がアニメーションに必要な部分です。

layout = go.Layout(layout_settings,
                  updatemenus=[dict(type='buttons',
                                     buttons=[dict(label='play',
                                                   method='animate',
                                                   args=[None, # すべて実行 
                                                         {"frame": {"duration": 1000}, 
                                                          "transition": {"duration": 500}}])],
                                     x=-0.08,
                                     y=1.02)],
                  )
go.Figure(data, layout)

updatemenusでボタンの設定をしています。

こちらは棒グラフのときと同じです。

argsの値としてリストの一つ目の要素にNoneを指定しているので、ボタンを押すと12ヵ月すべて実行します。

そして、frameのdurationというプロパティに1000を、transitionのdurationというプロパティに500を設定しています。

まず、transitionの方で表示するまでの時間を設定し(この場合だとマーカーが表示されるのにかかる時間)、frameの方でframeが変わる合計の時間を設定します。

なのでtransitionのduration <= frameのdurationとなります。

(言葉で説明するとわかりにくいため、実際に値を変えてみていただけるとよくわかると思います。)

ここまで表示すると以下のようになります。

まだ、この時点ではフレームが設定されていないので、ボタンを教えても何も起こりません。

では最後にフレームを設定しましょう。

棒グラフのときと同じように、各月で表示させたいフレームを設定します。

ここでは以下のように、3月なら3月までの線グラフを表示するようにします。([:date]部分)

frames = []
for date, value in df.iterrows():
  frame_data = []
  for col in df.columns:
    frame_data.append(go.Scatter(x=df.loc[:date].index,
                                     y=df.loc[:date][col],
                                     name=col)
                 )
  
  frame = go.Frame(data=frame_data)
  frames.append(frame)  

では、以下で表示してみましょう。

fig = go.Figure(data, layout, frames)
fig.show()

次のようなグラフが表示されます。

transitionのdurationを長くすると、マーカーが表示されるのにかかる時間が長くなります。

frameのdurationを長くすると、線が表示されるまでの間隔が長くなります。

まとめ

今回は棒グラフ、線グラフにアニメーションを付ける機能を紹介しました。

推移などにうまくアニメーションを付けることで、強調したい部分をより強調することができると思いますので、うまく活用していただければと思います。

では、また会いましょう!

-plotly入門, データ可視化
-, ,