こんな人にオススメ
plotly
のアニメーションにある引数easing
・duration
って一体何?
duration
は数値でeasing
はPlotly
公式の「Intro to Animations in Python」には"easing": "quadratic-in-out"
ってあったり"easing": "cubic-in-out"
っていあるんだけど。
ということで、今回はplotly
のアニメーションに出てくる引数easing
とduration
について解説とeasing
の一覧グラフを示す。
duration
ってのはアニメーションのフレーム間の間隔(遅延)を表すもの。値が大きくなると間隔が大きくなる。
easing
ってのはアニメーションの動き方を示す引数。直線的に動くのか、最初は速く最後は遅くなのかとか。easing
でアニメーションの雰囲気が変わる。
python環境は以下。
- Python 3.10.1
- numpy 1.21.4
- pandas 1.3.5
- plotly 5.4.0
- plotly-orca 3.4.2
px
、go
でのアニメーショングラフの描き方については以下参照。px
だとサクッとできるが、go
だとかなり面倒になる。んだけどカスタム性はgo
の方が圧倒的に高い。
-
-
【px&animation】plotly.expressでアニメーションのグラフを作成
続きを見る
-
-
【Plotly&animation】Plotlyのgoでアニメーションを作成
続きを見る
作成したコード全文
下準備(import
)
import numpy as np import pandas as pd import plotly.graph_objects as go import plotly.express as px import plotly.io as pio
まずは下準備としてのimport
関連。データ作成のためにnumpy
とpandas
をimport
。また、今回はpx
とgo
の両方を使用するのでどちらもimport
。
pio
はグラフ保存用に使用する。pio
でもグラフ保存については以下の記事参照。
-
-
【plotly&orca】plotlyで静止画保存(orca)
続きを見る
easing
一覧
# easing一覧 easings = [ 'linear', 'quad', 'cubic', 'sin', 'exp', 'circle', 'elastic', 'back', 'bounce', 'linear-in', 'quad-in', 'cubic-in', 'sin-in', 'exp-in', 'circle-in', 'elastic-in', 'back-in', 'bounce-in', 'linear-out', 'quad-out', 'cubic-out', 'sin-out', 'exp-out', 'circle-out', 'elastic-out', 'back-out', 'bounce-out', 'linear-in-out', 'quad-in-out', 'cubic-in-out', 'sin-in-out', 'exp-in-out', 'circle-in-out', 'elastic-in-out', 'back-in-out', 'bounce-in-out' ]
まずはeasing
一覧を文字として取得する。GitHubに使用できる一覧が記載されている。
in
, out
なしのものとin
, out
のどちらかが書かれいているもの、そして両方描かれているものがある。
動きのイメージ図はeasings.netが有益。もはや本記事じゃなくてこのサイトを見た方がいいかもしれん。ただ、上に示した全てのeasing
を網羅しているわけではない。
デフォルトのeasing
の値(px
で作成)
まずはデフォルトの状態をグラフ化しておく。サクッと作成したいのでここではpx
の散布図px.scatter
を使用。また、アニメーションを使用しないとeasing
が使えないのでpx.scatter
の引数animation_frame
を使用。
px.scatter
のグラフ化とアニメーショングラフの作成方法については以下。
-
-
【plotly&px.scatter】pxでの散布図の描き方
続きを見る
-
-
【px&animation】plotly.expressでアニメーションのグラフを作成
続きを見る
easing
はfig
の中のlayout
で使われいている。なのでfig.[’layout’]
(もしくはfig.layout
)でレイアウトにアクセス、そこからslider
, steps
, args
, transition
と辿っていくことでeasing
にたどり着くことができる。
デフォルトではeasing
はlinear
に設定されている。また、duration
はframe
とtransition
のどちらにも使われているが、frame
の方は各フレーム(グラフ下の各スライダーの点)ごとの間隔のこと。
transition
のduration
はPlotly
公式「Python Figure Reference: layout.sliders」で解説があるが、意味が一緒な気がする。frame
については公式で解説がないが、後に作成するグラフからグラフから各フレーム間隔だと思われる。
Sets the easing function of the slider transition
あとはグラフ化するだけだけど、マーカーのサイズ・フォントサイズが小さめなのでfig.update
で修正している。fig.update
シリーズについては以下の記事参照。
-
-
【plotly&fig作成と更新】add_traceやupdate_layoutの使い方
続きを見る
# pxでのデフォルトの状態(linear) # データの作成 x = np.arange(0, 10) data = {'x': x, 'y': x ** 2} df = pd.DataFrame(data) # グラフ化 fig = px.scatter( df, x='x', y='y', animation_frame='x', range_x=(-1, 10), range_y=(-1, 100), ) # レイアウトの中のスライダーの0番目のステップを調べる print(fig['layout']['sliders'][0]['steps'][0]) # layout.slider.Step({ # 'args': [['0'], {'frame': {'duration': 0, 'redraw': False}, 'mode': # 'immediate', 'fromcurrent': True, 'transition': {'duration': 0, # 'easing': 'linear'}}], # 'label': '0', # 'method': 'animate' # }) # ステップの中のアニメーション動作の部分を調べる print(fig['layout']['sliders'][0]['steps'][0]['args'][1]) # {'frame': {'duration': 0, 'redraw': False}, 'mode': 'immediate', 'fromcurrent': True, 'transition': {'duration': 0, 'easing': 'linear'}} # easingを調べる print( fig['layout']['sliders'][0]['steps'][0]['args'][1]['transition']['easing'] ) # linear # マーカーのサイズを変更 fig.update_traces(marker_size=20) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'easing' # 保存ファイル名の接頭辞 save_name = f"{prefix}_px_default" pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{save_name}.html") pio.write_image(fig, f"{save_name}.png")
デフォルトのeasing
の値(go
で作成)
go
でもデフォルトの値を知りたかったが、go
の場合は予めeasing
を設定しないといけない。なのでデフォルトの値を知ることができなかった。
ただ、Plotly
公式の「Python Figure Reference: layout.sliders」のeasing
の解説を見る限りはデフォルトは’cubic-in-out’
のようだ。
px
だとeasing
の変更が反映されない?
ということでpx
で作成したグラフでeasing
を変更してみる。だけど、どうやらpx
では反映されないようだ。ここではfig['layout']
で直接easing
を編集しているけど、fig.update_layout
でも同様、反映されなかった。
なので、easing
を反映させたかったら次で紹介するgo
を使う必要がありそうだ。謎。更新する操作が対応していないのかもしれないが、px
だと更新しか手がないから詰み。
# pxだとeasingの変更が反映されない? # データの作成 x = np.arange(0, 10) data = {'x': x, 'y': x ** 2} df = pd.DataFrame(data) # グラフ化 fig = px.scatter( df, x='x', y='y', animation_frame='x', range_x=(-1, 10), range_y=(-1, 100), ) # easingを変更 fig['layout']['sliders'][0]['steps'][0]['args'][1]['transition']['easing'] = 'elastic-in-out' # マーカーのサイズを変更 fig.update_traces(marker_size=20) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'easing' # 保存ファイル名の接頭辞 save_name = f"{prefix}_px" pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{save_name}.html") pio.write_image(fig, f"{save_name}.png")
go
だとeasing
の変更が反映される
一方で、go
を使ったアニメーションの場合はeasing
が反映される。go
でのアニメーションの作成方法はかなり面倒なので、作成手順をサラッと書いておく。多少、グラフ作成の順番が前後することはあるだろうが大まかには以下。
- 初期表示用のプロット作成
- アニメーション操作用のボタン作成
- ボタンをグラフに配置するための設定
- レイアウト設定
- アニメーションの各フレームでのプロット作成
fig
作成
初期表示のプロットは原点(0, 0)
にして、各フレームで2次関数のプロットを行っている。
また、px
とは異なりレイアウトなどを手動で決めているので、フォントサイズやマーカーサイズを引数layout
やplotly
の中で設定している。
# goではeasingの変更が反映されている # 初期プロット plot = [ go.Scatter( x=(0,), y=(0,), # 初期値は原点にしておく marker_size=20, # マーカーサイズを変更できるように ) ] # ボタンを作成 buttons = [dict( label='BUTTON', method='animate', # ボタンラベルとアニメーションの明示 args=[ None, # プロット部分の変更はなし # ここでeasingを変更 {'transition': {'easing': 'elastic-in-out', }} ], )] # レイアウトにボタンを配置するための設定 updatemenus = [dict( type='buttons', # ボタンの形式 buttons=buttons, # ボタンの設定 x=0, xanchor='left', y=-0.01, yanchor='top', # ボタンをグラフ下に設置 )] # レイアウトの設定 layout = go.Layout( updatemenus=updatemenus, # 作成したボタン xaxis_range=(-1, 10), # 横軸の表示範囲 yaxis_range=(-1, 100), # 縦軸の表示範囲 font_size=20, hoverlabel_font_size=20 # フォントサイズの変更 ) # アニメーションの各フレームの設定 frames = [] # アニメーションを体験するためのデータ配列を設定 arr = np.arange(0, 10) # アニメーション用のプロットを作成 # 配列の各値をxとして、それに対して1フレームを作成 for x in arr: d = go.Scatter(x=(x,), y=(x ** 2,), marker_size=20) frames.append(go.Frame(data=[d])) # グラフの作成 fig = go.Figure( data=plot, # データ部分 layout=layout, # レイアウト部分 frames=frames, # アニメーション部分 ) fig.show() # グラフ保存 prefix = 'easing' # 保存ファイル名の接頭辞 save_name = f"{prefix}_go" pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{save_name}.html") pio.write_image(fig, f"{save_name}.png")
updatemenus
でeasing
一覧をグラフ化
ということで、easing
一覧をグラフ化してみた。ベースはさっきのgo
でのeasing
変更グラフ。これのeasing
部分をfor
で回して1ボタンずつeasing
設定を行う。
ただ、全てのeasing
に対してボタンを1つずつ設定してしまうとボタンが見切れてしまうので、updatemenus
の引数type=’dropdown’
でeasing
一覧を引き出せるようにしている。
これでザックリとeasing
の挙動の違いがわかるようになるだろう。
# easing一覧をグラフ化 # 初期プロット plot = [ go.Scatter( x=(0,), y=(0,), # 初期値は原点にしておく marker_size=20, # マーカーサイズを変更できるように ) ] # ボタンを作成 buttons = [] for easing in easings: # forでボタンの内容を各easingへ変更 # ボタンの挙動 button = dict( label=easing, method='animate', # ボタンラベルとアニメーションの明示 args=[ None, # プロット部分の変更はなし {'transition': {'easing': easing, }} ], ) buttons.append(button) # レイアウトにボタンを配置するための設定 updatemenus = [dict( type='dropdown', # ボタンの形式 buttons=buttons, # ボタンの設定 # x=0, xanchor='left', y=-0.01, yanchor='top', # ボタンをグラフ下に設置 )] # レイアウトの設定 layout = go.Layout( updatemenus=updatemenus, # 作成したボタン xaxis_range=(-1, 10), # 横軸の表示範囲 yaxis_range=(-1, 100), # 縦軸の表示範囲 font_size=20, hoverlabel_font_size=20 # フォントサイズの変更 ) # アニメーションの各フレームの設定 frames = [] # アニメーションを体験するためのデータ配列を設定 arr = np.arange(0, 10) # アニメーション用のプロットを作成 # 配列の各値をxとして、それに対して1フレームを作成 for x in arr: d = go.Scatter(x=(x,), y=(x ** 2,), marker_size=20) frames.append(go.Frame(data=[d])) # グラフの作成 fig = go.Figure( data=plot, # データ部分 layout=layout, # レイアウト部分 frames=frames, # アニメーション部分 ) fig.show() # グラフ保存 prefix = 'easing' # 保存ファイル名の接頭辞 save_name = f"{prefix}_list" pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{save_name}.html") pio.write_image(fig, f"{save_name}.png")
px
だとframe
のduration
が反映されない
easing
一覧を作成したが、ここからはduration
についても見ていく。duration
はframe
に関するものとtransition
に関するものの2種類あるが、ここではまずframe
に関するduration
を変更してみる。
相変わらずframe
についてはデフォルト値がわからないが、そもそもpx
だと反映されないから次のgo
で確認することにする。謎。
変更方法はeasing
と同じ。frame
のduration
を変更できるように中身を見つけにいく。
# pxでframeのdurationを変更できない # データの作成 x = np.arange(0, 10) data = {'x': x, 'y': x ** 2} df = pd.DataFrame(data) # グラフ化 fig = px.scatter( df, x='x', y='y', animation_frame='x', range_x=(-1, 10), range_y=(-1, 100), ) # レイアウトの中のスライダーの0番目のステップを調べる print(fig['layout']['sliders'][0]['steps'][0]) # layout.slider.Step({ # 'args': [['0'], {'frame': {'duration': 0, 'redraw': False}, 'mode': # 'immediate', 'fromcurrent': True, 'transition': {'duration': 0, # 'easing': 'linear'}}], # 'label': '0', # 'method': 'animate' # }) # ステップの中のアニメーション動作の部分を調べる print(fig['layout']['sliders'][0]['steps'][0]['args'][1]) # {'frame': {'duration': 0, 'redraw': False}, 'mode': 'immediate', 'fromcurrent': True, 'transition': {'duration': 0, 'easing': 'linear'}} # durationを調べる print( fig['layout']['sliders'][0]['steps'][0]['args'][1]['frame']['duration'] ) # 0 # durationを変更 fig['layout']['sliders'][0]['steps'][0]['args'][1]['frame']['duration'] = 1000000000 # マーカーのサイズを変更 fig.update_traces(marker_size=20) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'easing' # 保存ファイル名の接頭辞 save_name = f"{prefix}_frame_duration_px" pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{save_name}.html") pio.write_image(fig, f"{save_name}.png")
go
だとframe
のduration
の変更が反映される
go
だとframe
のduration
の変更が反映される。デフォルト値はframe
に関しては謎だが、変更後の値を1000にすると大きく遅延したので1000よりも低い値と考えられる。
指定方法はbuttons
のargs
の2つ目のdict
のframe
で行う。duration
の値を大きくしすぎるといつまでも経ってもアニメーションが終わらないので注意が必要。
# frameのdurationもgoだと反映される # 初期プロット plot = [ go.Scatter( x=(0,), y=(0,), # 初期値は原点にしておく marker_size=20, # マーカーサイズを変更できるように ) ] # ボタンを作成 buttons = [dict( label='BUTTON', method='animate', # ボタンラベルとアニメーションの明示 args=[ None, # プロット部分の変更はなし { # frameのdurationを遅らせる(デフォルト値は謎) 'frame': {'duration': 1000, }, 'transition': {'easing': easing, }, } ], )] # レイアウトにボタンを配置するための設定 updatemenus = [dict( type='buttons', # ボタンの形式 buttons=buttons, # ボタンの設定 x=0, xanchor='left', y=-0.01, yanchor='top', # ボタンをグラフ下に設置 )] # レイアウトの設定 layout = go.Layout( updatemenus=updatemenus, # 作成したボタン xaxis_range=(-1, 10), # 横軸の表示範囲 yaxis_range=(-1, 100), # 縦軸の表示範囲 font_size=20, hoverlabel_font_size=20 # フォントサイズの変更 ) # アニメーションの各フレームの設定 frames = [] # アニメーションを体験するためのデータ配列を設定 arr = np.arange(0, 10) # アニメーション用のプロットを作成 # 配列の各値をxとして、それに対して1フレームを作成 for x in arr: d = go.Scatter(x=(x,), y=(x ** 2,), marker_size=20) frames.append(go.Frame(data=[d])) # グラフの作成 fig = go.Figure( data=plot, # データ部分 layout=layout, # レイアウト部分 frames=frames, # アニメーション部分 ) fig.show() # グラフ保存 prefix = 'easing' # 保存ファイル名の接頭辞 save_name = f"{prefix}_frame_duration_go" pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{save_name}.html") pio.write_image(fig, f"{save_name}.png")
px
だとtransition
のduration
も反映されない
最後はtransition
の方のduration
。なんだけどpx
だと反映されない。一応、「Python Figure Reference: layout.sliders」のdurationにはデフォルト値が150との記載があるから、変更後は1000にしたけど反映されない。謎(n回目)。
# pxではtrasitionのdurationも効かない # データの作成 x = np.arange(0, 10) data = {'x': x, 'y': x ** 2} df = pd.DataFrame(data) # グラフ化 fig = px.scatter( df, x='x', y='y', animation_frame='x', range_x=(-1, 10), range_y=(-1, 100), ) # レイアウトの中のスライダーの0番目のステップを調べる print(fig['layout']['sliders'][0]['steps'][0]) # layout.slider.Step({ # 'args': [['0'], {'frame': {'duration': 0, 'redraw': False}, 'mode': # 'immediate', 'fromcurrent': True, 'transition': {'duration': 0, # 'easing': 'linear'}}], # 'label': '0', # 'method': 'animate' # }) # ステップの中のアニメーション動作の部分を調べる print(fig['layout']['sliders'][0]['steps'][0]['args'][1]) # {'frame': {'duration': 0, 'redraw': False}, 'mode': 'immediate', 'fromcurrent': True, 'transition': {'duration': 0, 'easing': 'linear'}} # easingを調べる print( fig['layout']['sliders'][0]['steps'][0]['args'][1]['transition']['duration'] ) # 0 # easingを変更 fig['layout']['sliders'][0]['steps'][0]['args'][1]['transition']['duration'] = 1000 # マーカーのサイズを変更 fig.update_traces(marker_size=20) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'easing' # 保存ファイル名の接頭辞 save_name = f"{prefix}_transsition_duration_px" pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{save_name}.html") pio.write_image(fig, f"{save_name}.png")
go
だとtransition
のduration
の変更は反映されてる?
最後はgo
でtransition
のduration
を変更してみた。んだけど、反映されているのかよくわからん。一応、値は桁違いに大きい値にしたんだけど、特に変わったところはない。
公式「Python Figure Reference: layout.sliders」の説明では以下のように書かれているから変わっても良さそうだが。
Sets the duration of the slider transition
値の変更方法は今まで通り、ボタンを作成する際に設定してしまえばいい。
# goのtransitionのdurationは反映されてる? # 初期プロット plot = [ go.Scatter( x=(0,), y=(0,), # 初期値は原点にしておく marker_size=20, # マーカーサイズを変更できるように ) ] # ボタンを作成 buttons = [dict( label='BUTTON', method='animate', # ボタンラベルとアニメーションの明示 args=[ None, # プロット部分の変更はなし { 'transition': {'easing': easing, 'duration': 50000000}, } ], )] # レイアウトにボタンを配置するための設定 updatemenus = [dict( type='buttons', # ボタンの形式 buttons=buttons, # ボタンの設定 x=0, xanchor='left', y=-0.01, yanchor='top', # ボタンをグラフ下に設置 )] # レイアウトの設定 layout = go.Layout( updatemenus=updatemenus, # 作成したボタン xaxis_range=(-1, 10), # 横軸の表示範囲 yaxis_range=(-1, 100), # 縦軸の表示範囲 font_size=20, hoverlabel_font_size=20 # フォントサイズの変更 ) # アニメーションの各フレームの設定 frames = [] # アニメーションを体験するためのデータ配列を設定 arr = np.arange(0, 10) # アニメーション用のプロットを作成 # 配列の各値をxとして、それに対して1フレームを作成 for x in arr: d = go.Scatter(x=(x,), y=(x ** 2,), marker_size=20) frames.append(go.Frame(data=[d])) # グラフの作成 fig = go.Figure( data=plot, # データ部分 layout=layout, # レイアウト部分 frames=frames, # アニメーション部分 ) fig.show() # グラフ保存 prefix = 'easing' # 保存ファイル名の接頭辞 save_name = f"{prefix}_transision_duration_go" pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{save_name}.html") pio.write_image(fig, f"{save_name}.png")
easing
、duration
で反映されるのは
最後にgo
とpx
でeasing
, duration
のどれが変更されるのかをまとめておく。px
だと全滅なんだけど、使っているバージョンなどで変わってくるかもしれん。
easing | frameのduration | transitionのduration | |
---|---|---|---|
go | ○ | ○ | X |
px | X | X | X |
ちょっとした工夫
今回はPlotly
のアニメーショングラフで使えるeasing
とduration
について解説した。easing
はアニメーションの遷移の仕方、duration
はアニメーションの各フレームの間隔を指定するもの。
本質じゃない部分なので指定したり変更したりしなくてもいいんだけど、ちょっと変更するだけで見栄えが変わるから、覚えておいて損はないだろう。
本記事でeasing
、duration
についての理解を深めていただけると幸いだ。
関連記事
-
-
【plotly&go.Scatter】plotlyの散布図グラフの描き方
続きを見る
-
-
【plotly&マーカー】plotlyのマーカーのシンボル
続きを見る
-
-
【plotly&アニメーション】plotlyで各国の収入と平均寿命の時代変化をバブルチャート&アニメーションで描く
続きを見る
-
-
【plotly&3D】px.scatter_3dとpx.line_3Dで3Dグラフを作成
続きを見る
-
-
【px&バブルチャート】plotly.expressで各国の収入と平均寿命の時代変化をアニメーションで
続きを見る
-
-
【plotly&ガントチャート】px.timelineでGantt Chartsを作成
続きを見る
-
-
【plotly&rangeslider/rangeselector】レンジスライダーとレンジセレクターで時系列を見やすく
続きを見る
-
-
【plotly&pattern】棒グラフとかのパターンまとめ
続きを見る
-
-
【plotly&config】グラフのツールバーを編集する
続きを見る