こんな人にオススメ
plotly
でアニメーションができるらしいけどどうやるの?
Plotly公式の「Intro to Animations in Python」を見てもよくわからん。
ということで、今回はplotly.express
、通称px
を使用してアニメーション機能を持つグラフを作成する。アニメーションってのはグラフ上のボタンを押すと自動でプロット内容が変わるグラフのこと。
px
じゃなくてplotly.graph_objects
、通称go
を使用する方法もあるけど、こっちはかなり複雑でコードが長くなってしまう。今回のpx
だとかなり短く作成可能。
go
で描いたアニメーショングラフについては以下参照。
-
-
【Plotly&animation】Plotlyのgoでアニメーションを作成
続きを見る
アニメーション機能付きのグラフは作成が面倒なことが多いけど、px
なら簡単に作成可能。なので、本記事でアニメーションの作成方法を知っていただけると幸いだ。
python環境は以下。
- Python 3.10.1
- pandas 1.3.5
- plotly 5.4.0
- plotly-orca 3.4.2
作成したコード全文
下準備(import
)
import pandas as pd import plotly.express as px import plotly.io as pio
まずは下準備としてのimport
関連。px
はpandas
のデータフレームを使ってグラフを描くことに特化しているのでpandas
をimport
。
あとはメインのpx
とグラフ保存用のpio
。pio
によるPlotly
のグラフ保存については以下参照。
-
-
【plotly&orca】plotlyで静止画保存(orca)
続きを見る
アニメーションを使わずグラフ化
まずはアニメーションを使わずにグラフ化してみる。コードはPlotly
公式の「Animated figures with Plotly Express」のアニメーショングラフから、アニメーション要素を取り除いた版。
この場合はアニメーションで動かすはずだったプロットも含めて全てのデータがプロットされている。これだと何が何やらわからん。
px.scatter
については以下の記事参照。
-
-
【plotly&px.scatter】pxでの散布図の描き方
続きを見る
このグラフをアニメーション化したグラフを次に解説する。
import pandas as pd import plotly.express as px import plotly.io as pio # データ全体をグラフ化 df = px.data.gapminder() print(df) # country continent year ... gdpPercap iso_alpha iso_num # 0 Afghanistan Asia 1952 ... 779.445314 AFG 4 # 1 Afghanistan Asia 1957 ... 820.853030 AFG 4 # 2 Afghanistan Asia 1962 ... 853.100710 AFG 4 # 3 Afghanistan Asia 1967 ... 836.197138 AFG 4 # 4 Afghanistan Asia 1972 ... 739.981106 AFG 4 # ... ... ... ... ... ... ... ... # 1699 Zimbabwe Africa 1987 ... 706.157306 ZWE 716 # 1700 Zimbabwe Africa 1992 ... 693.420786 ZWE 716 # 1701 Zimbabwe Africa 1997 ... 792.449960 ZWE 716 # 1702 Zimbabwe Africa 2002 ... 672.038623 ZWE 716 # 1703 Zimbabwe Africa 2007 ... 469.709298 ZWE 716 # [1704 rows x 8 columns] fig = px.scatter( df, # 使用するデータフレーム x='gdpPercap', y='lifeExp', # 横軸・縦軸とする列名 size='pop', color='continent', # サイズと色分けの基準とする列名 hover_name='country', # ホバーに表示する情報 log_x=True, # 横軸をlogに size_max=55, # sizeの最大値 range_x=[100, 100000], range_y=[25, 90] # 横軸・縦軸の表示範囲 ) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'px-animation' # 保存ファイル名の接頭辞 save_name = f"{prefix}_simple" 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")
animation_frame
でアニメーショングラフ
px
でアニメーションのグラフを作成するには引数animation_frame
, animation_group
を使用するだけ。本当は使用するデータがアニメーションに対応した構造じゃないといけないが。
ここではアニメーションをyear、どのデータでアニメーション各コマをグループするかを国で指定している。
px
のアニメーションの場合は列名で指定する必要がある。また、各列(各データ)は同じ種族で縦に並べないといけない。例えばcountry
で言えば初めにAfghanistan
のデータが並び、その後にAlbania
のデータが続く。
import pandas as pd import plotly.express as px import plotly.io as pio df = px.data.gapminder() print(df.head(15)) # country continent year ... gdpPercap iso_alpha iso_num # 0 Afghanistan Asia 1952 ... 779.445314 AFG 4 # 1 Afghanistan Asia 1957 ... 820.853030 AFG 4 # 2 Afghanistan Asia 1962 ... 853.100710 AFG 4 # 3 Afghanistan Asia 1967 ... 836.197138 AFG 4 # 4 Afghanistan Asia 1972 ... 739.981106 AFG 4 # 5 Afghanistan Asia 1977 ... 786.113360 AFG 4 # 6 Afghanistan Asia 1982 ... 978.011439 AFG 4 # 7 Afghanistan Asia 1987 ... 852.395945 AFG 4 # 8 Afghanistan Asia 1992 ... 649.341395 AFG 4 # 9 Afghanistan Asia 1997 ... 635.341351 AFG 4 # 10 Afghanistan Asia 2002 ... 726.734055 AFG 4 # 11 Afghanistan Asia 2007 ... 974.580338 AFG 4 # 12 Albania Europe 1952 ... 1601.056136 ALB 8 # 13 Albania Europe 1957 ... 1942.284244 ALB 8 # 14 Albania Europe 1962 ... 2312.888958 ALB 8 # [15 rows x 8 columns]
縦に並んでいないデータフレームを使おうとするとアニメーションがうまくいかなかったりする可能性があるので面倒。
結局は上のように各種族ごとにデータを並べてデータフレームを作成するのが安定。
df = px.data.gapminder() print(df) # country continent year ... gdpPercap iso_alpha iso_num # 0 Afghanistan Asia 1952 ... 779.445314 AFG 4 # 1 Afghanistan Asia 1957 ... 820.853030 AFG 4 # 2 Afghanistan Asia 1962 ... 853.100710 AFG 4 # 3 Afghanistan Asia 1967 ... 836.197138 AFG 4 # 4 Afghanistan Asia 1972 ... 739.981106 AFG 4 # ... ... ... ... ... ... ... ... # 1699 Zimbabwe Africa 1987 ... 706.157306 ZWE 716 # 1700 Zimbabwe Africa 1992 ... 693.420786 ZWE 716 # 1701 Zimbabwe Africa 1997 ... 792.449960 ZWE 716 # 1702 Zimbabwe Africa 2002 ... 672.038623 ZWE 716 # 1703 Zimbabwe Africa 2007 ... 469.709298 ZWE 716 # [1704 rows x 8 columns] fig = px.scatter( df, # 使用するデータフレーム x='gdpPercap', y='lifeExp', # 横軸・縦軸とする列名 animation_frame='year', # アニメーションにする列名 animation_group='country', # アニメーション時に同じグループにする列名 size='pop', color='continent', # サイズと色分けの基準とする列名 hover_name='country', # ホバーに表示する情報 log_x=True, # 横軸をlogに size_max=55, # sizeの最大値 range_x=[100, 100000], range_y=[25, 90] # 横軸・縦軸の表示範囲 ) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'px-animation' # 保存ファイル名の接頭辞 save_name = f"{prefix}_animation" 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")
range_x
, range_y
には注意
px.scatterには横軸と縦軸の表示範囲を変更するための引数range_x
, range_y
がある。このrange_x
, range_y
を指定しないと上のグラフのようにアニメーションの途中でプロットがはみ出てしまう。
1つ前のグラフのようにrange_x
, range_y
を調節する必要があることはPloty
公式「Intro to Animations in Python」でも注意喚起がなされている。
Here is an example of an animated scatter plot creating using Plotly Express. Note that you should always fix the
x_range
andy_range
to ensure that your data remains visible throughout the animation.
import pandas as pd import plotly.express as px import plotly.io as pio # range_x, range_yを調整しないとはみ出る df = px.data.gapminder() fig = px.scatter( df, # 使用するデータフレーム x='gdpPercap', y='lifeExp', # 横軸・縦軸とする列名 animation_frame='year', # アニメーションにする列名 animation_group='country', # アニメーション時に同じグループにする列名 size='pop', color='continent', # サイズと色分けの基準とする列名 hover_name='country', # ホバーに表示する情報 log_x=True, # 横軸をlogに size_max=55, # sizeの最大値 # range_x=[100, 100000], range_y=[25, 90] # 横軸・縦軸の表示範囲 ) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'px-animation' # 保存ファイル名の接頭辞 save_name = f"{prefix}_animation_range" 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.bar
で棒グラフのアニメーション
px.scatter
の散布図だけではなく、px.bar
を使うことで棒グラフも作成することが可能。引数は基本的に同じ。range_x
, range_y
の指定を忘れなければいい。
px.bar
、go
での棒グラフgo.Bar
については以下の記事参照。
-
-
【plolty&棒グラフ】px.barでバーチャートを作成
続きを見る
-
-
【plotly&棒グラフ】go.Barでバーチャートを作成
続きを見る
import pandas as pd import plotly.express as px import plotly.io as pio # px.barで棒グラフでもアニメーション df = px.data.gapminder() print(df) # country continent year ... gdpPercap iso_alpha iso_num # 0 Afghanistan Asia 1952 ... 779.445314 AFG 4 # 1 Afghanistan Asia 1957 ... 820.853030 AFG 4 # 2 Afghanistan Asia 1962 ... 853.100710 AFG 4 # 3 Afghanistan Asia 1967 ... 836.197138 AFG 4 # 4 Afghanistan Asia 1972 ... 739.981106 AFG 4 # ... ... ... ... ... ... ... ... # 1699 Zimbabwe Africa 1987 ... 706.157306 ZWE 716 # 1700 Zimbabwe Africa 1992 ... 693.420786 ZWE 716 # 1701 Zimbabwe Africa 1997 ... 792.449960 ZWE 716 # 1702 Zimbabwe Africa 2002 ... 672.038623 ZWE 716 # 1703 Zimbabwe Africa 2007 ... 469.709298 ZWE 716 # [1704 rows x 8 columns] fig = px.bar( df, # 使用するデータフレーム x="continent", y="pop", # 横軸・縦軸とする列名 color="continent", # 色分けの基準とする列名 animation_frame="year", # アニメーションにする列名 animation_group="country", # アニメーション時に同じグループにする列名 # range_yを設定しておかないとグラフがはみ出る range_y=[0, 4000000000], # 縦軸の表示範囲 ) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'px-animation' # 保存ファイル名の接頭辞 save_name = f"{prefix}_animation_bar" 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
にあるデータpx.data.stocks()
を使用。これは各アメリカの大企業の株価の、データ初日を1とした時の推移。
px.line
はデフォルトでは線だけのグラフ。線だけでもいいけど、ここでは引数marker=True
でマーカーをつけておいた。
また、1列目がdate
で日付なので、実際にグラフ化する際にはdf.columns[1:7]
で企業だけに絞っている。
import pandas as pd import plotly.express as px import plotly.io as pio # アニメーションなしのグラフ # 株価のデータ df = px.data.stocks() print(df) # date GOOG AAPL AMZN FB NFLX MSFT # 0 2018-01-01 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 # 1 2018-01-08 1.018172 1.011943 1.061881 0.959968 1.053526 1.015988 # 2 2018-01-15 1.032008 1.019771 1.053240 0.970243 1.049860 1.020524 # 3 2018-01-22 1.066783 0.980057 1.140676 1.016858 1.307681 1.066561 # 4 2018-01-29 1.008773 0.917143 1.163374 1.018357 1.273537 1.040708 # .. ... ... ... ... ... ... ... # 100 2019-12-02 1.216280 1.546914 1.425061 1.075997 1.463641 1.720717 # 101 2019-12-09 1.222821 1.572286 1.432660 1.038855 1.421496 1.752239 # 102 2019-12-16 1.224418 1.596800 1.453455 1.104094 1.604362 1.784896 # 103 2019-12-23 1.226504 1.656000 1.521226 1.113728 1.567170 1.802472 # 104 2019-12-30 1.213014 1.678000 1.503360 1.098475 1.540883 1.788185 # [105 rows x 7 columns] # pxで折れ線グラフを作成 fig = px.line(df, x='date', y=df.columns[1:7], markers=True,) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'px-animation' # 保存ファイル名の接頭辞 save_name = f"{prefix}_line" 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.scatter
のグラフでは推移をザッと見られれば良かったし、そもそも国数が多いから履歴を残してられない。
ただ、今回はデータ数が少ないから履歴を残したいし、株価の推移だから過去分も見ておきたい。ということでこのグラフは失敗作。
引数として単純にanimation_frame='date'
を指定しただけ、animation_group
は列が1つじゃないから指定できない。例えば以下のようにanimation_group=df.columns[1:7]
とするとエラー。
import pandas as pd import plotly.express as px import plotly.io as pio fig = px.line( df, x='date', y=df.columns[1:7], markers=True, animation_frame='date', animation_group=df.columns[1:7], range_x=('2018-01-01', '2019-12-30'), range_y=[0.6, 2.1], ) # ValueError: All arguments should have the same length. The length of argument `animation_group` is 6, whereas the length of previously-processed arguments ['date'] is 105
単純にアニメーションを作成したいなら以下のコードで作成可能。
# シンプルにアニメーションにすると履歴が残らない # 株価のデータ df = px.data.stocks() print(df) # date GOOG AAPL AMZN FB NFLX MSFT # 0 2018-01-01 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 # 1 2018-01-08 1.018172 1.011943 1.061881 0.959968 1.053526 1.015988 # 2 2018-01-15 1.032008 1.019771 1.053240 0.970243 1.049860 1.020524 # 3 2018-01-22 1.066783 0.980057 1.140676 1.016858 1.307681 1.066561 # 4 2018-01-29 1.008773 0.917143 1.163374 1.018357 1.273537 1.040708 # .. ... ... ... ... ... ... ... # 100 2019-12-02 1.216280 1.546914 1.425061 1.075997 1.463641 1.720717 # 101 2019-12-09 1.222821 1.572286 1.432660 1.038855 1.421496 1.752239 # 102 2019-12-16 1.224418 1.596800 1.453455 1.104094 1.604362 1.784896 # 103 2019-12-23 1.226504 1.656000 1.521226 1.113728 1.567170 1.802472 # 104 2019-12-30 1.213014 1.678000 1.503360 1.098475 1.540883 1.788185 # [105 rows x 7 columns] # pxで折れ線グラフを作成 fig = px.line( df, x='date', y=df.columns[1:7], markers=True, animation_frame='date', # animation_group=df.columns[1:7], # ValueErrorになる range_x=('2018-01-01', '2019-12-30'), range_y=[0.6, 2.1], ) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'px-animation' # 保存ファイル名の接頭辞 save_name = f"{prefix}_animation_nohistory" 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")
軌跡(履歴)を残そうとしたアニメーショングラフ
軌跡を残すアニメーショングラフの考え方
続いて、履歴を残そうとしたグラフを作成。実はアニメーションの作成には工夫する点がある。上の図がその概略図。データフレームとして以下の構造を持っている。
- 赤、緑、黄、青の塗りつぶし
- アニメーションのフレームに対応
- スライダーの各点のこと
- 赤、紫、緑、青の文字
- 各アニメーション内でグラフ化する日付
- 各塗りつぶし(フレームに対応)でプロットする
イメージが難しいかもしれないけど、要するに各フレーム(グラフの各スライダー)で各塗りつぶしをグラフ化するだけ。これを最初、2番目、3番目、...のアニメーションフレームと繋げることで、軌跡を残すことができる。
ポイントは各フレームで過去のプロット(軌跡)までグラフ化する点。例えば「2番目のアニメーションフレーム」だと、新規の紫文字だけプロットすると2018/1/8のデータだけプロットされる。しかし赤文字のデータもプロットすることで過去分(2018/1/1)もプロットされる。
まだ、左右の図の違いは以下。
- 左:各企業を1列で表現
- 右:各企業を列ごとに表現
どっちが見やすいか・管理しやすいか・使いやすいかは人や状況それぞれ。本記事ではどちらのパターンも作成する。
上のグラフは赤文字の2018/1/1のデータと紫文字の2018/1/8のデータをプロットしたもの。さっきの表でいえば緑で塗りつぶされた「2番目のアニメーションフレーム」の時点。
もし、赤文字の2018/1/1をプロットしなかったら、紫文字の2018/1/8のプロットだけになる。これまでの履歴もいちいちプロットしないといけない。
軌跡を残そうとして失敗したアニメーショングラフ
このグラフもなぜか動かせるグラフを載せられなかったからgif
で示す。見てもらえると分かるように、履歴が残らず各アニメーションの点だけがプロットされている。
作成したデータフレームは以下。データフレームの行の数だけfor
でループを回し、各行以前のデータを空のデータフレームdf2
に追加していく方法をとった。
なお、ignore_index=True
にすることで、 df2
のインデックスをリセットして0から勘定するようにした。
import pandas as pd import plotly.express as px import plotly.io as pio df = px.data.stocks() # 空のデータフレームを作成 df2 = pd.DataFrame(index=[], columns=df.columns) # データフレームへの追記はパフォーマンスが悪い for num in range(len(df)): df2 = df2.append( df.iloc[:num + 1, :], ignore_index=True, ) print(df2) # date GOOG AAPL AMZN FB NFLX MSFT # 0 2018-01-01 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 # 1 2018-01-01 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 # 2 2018-01-08 1.018172 1.011943 1.061881 0.959968 1.053526 1.015988 # 3 2018-01-01 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 # 4 2018-01-08 1.018172 1.011943 1.061881 0.959968 1.053526 1.015988 # ... ... ... ... ... ... ... ... # 5560 2019-12-02 1.216280 1.546914 1.425061 1.075997 1.463641 1.720717 # 5561 2019-12-09 1.222821 1.572286 1.432660 1.038855 1.421496 1.752239 # 5562 2019-12-16 1.224418 1.596800 1.453455 1.104094 1.604362 1.784896 # 5563 2019-12-23 1.226504 1.656000 1.521226 1.113728 1.567170 1.802472 # 5564 2019-12-30 1.213014 1.678000 1.503360 1.098475 1.540883 1.788185 # [5565 rows x 7 columns]
このデータフレームで言えば0行目が1フレーム目、1, 2行目が2フレーム目3, 4, 5行目が3フレーム目になる。
プロットのためのコードは以下。animation_group
は指定してもしなくても結果は変わらない。履歴は残らない。ただし、animation_group
を指定しない場合は各フレーム間が滑らかに遷移するようになる。
# pxで折れ線グラフを作成 fig = px.line( df2, x='date', y=df.columns[1:7], markers=True, animation_frame='date', animation_group='date', range_x=('2018-01-01', '2019-12-30'), range_y=[0.6, 2.1], ) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'px-animation' # 保存ファイル名の接頭辞 save_name = f"{prefix}_animation_mistake" 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")
ここでの全体コードを以下に記載しておく。一括で見たい方は開いてみてほしい。
フレームの指定方法が間違っている
じゃあなんで履歴が残らないのかっていうと、恐らくフレームの指定方法(animation_frame
)が間違っているから。
上の画像のように、さっきのコード(上の図の右側)だとフレームが各塗りつぶしでバラバラになっている。一方で、後で解説する上手くいく方法では各フレームでanimation_frameが同じ。これが原因。
このフレームの指定ズレを修正したグラフを次に解説する。
軌跡(履歴)を残せたアニメーショングラフ
最後に軌跡を残すことができたアニメーショングラフの作成方法を解説する。さっきの図であったように、データフレームの各列に各企業を配置する方法と、データフレームの1列に全企業を置く方法の2つがある。
ここではそのどちらの方法も解説するから、状況に応じて使い分けてほしい。厄介だけどどちらの考え方も頭に入れておくとアニメーションでつまづくことがグンと減るだろう。
各列に企業を配置した場合のアニメーショングラフ
ということで奇跡が残るように修正したグラフを作成した。全企業を乗せると重すぎて記事に載せられなかったのでgif。px
はブログと相性が悪い。
空のデータフレームdf3
に、各行以前の日付と株価を追加。さらに各行の最新の日付=各フレームの日付を、軌跡の数だけ追加する。例えば2018/1/8は1/1と1/8の2データ分だけ2018/1/8というフレーム用のデータをframe
列に追加する。1/15なら1/1, 1/8. 1/15の3データ。
df = px.data.stocks() # 空のデータフレームを作成 df3 = pd.DataFrame(index=[], columns=df.columns) frame_lst = [] # 各フレームの名称を入れるための配列 # データフレームへの追記はパフォーマンスが悪い for num in range(len(df)): df3 = df3.append( df.iloc[:num + 1, :], ignore_index=True, ) # 各フレームの日付を軌跡の数だけ作成 date = [df['date'][num]] * (num + 1) frame_lst.extend(date) print(df3) # date GOOG AAPL AMZN FB NFLX MSFT # 0 2018-01-01 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 # 1 2018-01-01 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 # 2 2018-01-08 1.018172 1.011943 1.061881 0.959968 1.053526 1.015988 # 3 2018-01-01 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 # 4 2018-01-08 1.018172 1.011943 1.061881 0.959968 1.053526 1.015988 # ... ... ... ... ... ... ... ... # 5560 2019-12-02 1.216280 1.546914 1.425061 1.075997 1.463641 1.720717 # 5561 2019-12-09 1.222821 1.572286 1.432660 1.038855 1.421496 1.752239 # 5562 2019-12-16 1.224418 1.596800 1.453455 1.104094 1.604362 1.784896 # 5563 2019-12-23 1.226504 1.656000 1.521226 1.113728 1.567170 1.802472 # 5564 2019-12-30 1.213014 1.678000 1.503360 1.098475 1.540883 1.788185 # [5565 rows x 7 columns]
あとはdf3
とpandas
のpd.Series
に変換したframe_lst
をpd.concat
で横に結合するとデータフレームの完成。pd.concat
は上書きではなく新たに変数を作成するので、df3 = pd.concat
にしないとdf3に反映されないことに注意。
データフレームができたらpx.line
でグラフ化するのみ。各企業を各列に置いているのでpx.line
のy
はdf.columns[1:7]
で指定する。
# pxで折れ線グラフを作成 fig = px.line( df3, x='date', y=df.columns[1:7], markers=True, animation_frame='frame', animation_group='date', range_x=('2018-01-01', '2019-12-30'), range_y=[0.6, 2.1], ) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'px-animation' # 保存ファイル名の接頭辞 save_name = f"{prefix}_animation_rowcompany" 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")
まとめたコードは以下。
1列に企業を配置した場合のアニメーショングラフ
一方でデータフレームの1列に全ての企業を配置する方法も可能。むしろこっちの方がpx
では使われるような気もする。ただ、この場合はなぜかマーカーが消える。
データフレームのイメージは下の画像の左側。company
列に全ての企業が羅列されており、全企業分のデータが終わるまではdate
列やframe
列は同じ値を取る。
データフレームの構造が元のdf = px.data.stocks()
と異なるから、データフレームは初めから作成する。作成のイメージは以下。
- 日付、株価、企業、フレームそれぞれで配列を作成
- 各行以前の上記データをそれぞれ格納
- それぞれのデータを
pandas
のpd.Series
に変換 Series
をpd.concat
で結合
まずは各データの軌跡を作成。
import pandas as pd import plotly.express as px import plotly.io as pio df = px.data.stocks() date_lst = [] # 日付データ stock_lst = [] # 株価データ company_lst = [] # 企業データ # frame=2の時に初めの人から始まるから、履歴がバグる frame_lst = [] # 各アニメーションのラベル # 各行までの日付でデータを作成 companies = df.columns[1:] # 企業名 for num in range(len(df)): # 各行の日付をアニメーションのラベルにする # 個数は各行までの日付分 x 企業の数 frame_lst.extend([df.iloc[num, 0]] * len(companies) * (num + 1)) # 各企業ごとでデータを作成 for company in companies: # 企業の数だけ、各業の日付までの日付・株価・企業名を取得・追加 date_lst.extend(df.iloc[:num + 1, 0]) stock_lst.extend(df[company][:num + 1]) company_lst.extend([company] * (num + 1))
続いては各データをpandas
のpd.Series
に変換。ここで引数name
を使うことでpd.concat
後のデータフレームに自動で適用される。
# pandasのシリーズに変換 series_date = pd.Series(date_lst, name='date') series_stock = pd.Series(stock_lst, name='stock') series_company = pd.Series(company_lst, name='company') series_frame = pd.Series(frame_lst, name='frame')
最後にpd.concat
で結合。frame
列とdate
列でズレている行がわかるだろう。このズレている行が次のアニメーションフレームに移行するタイミング。
各フレームごとにまとめて同じ値にしておかないと軌跡が残らない。
# シリーズを横に結合 df3 = pd.concat( [series_date, series_stock, series_company, series_frame], axis=1 ) print(df3) # date stock company frame # 0 2018-01-01 1.000000 GOOG 2018-01-01 # 1 2018-01-01 1.000000 AAPL 2018-01-01 # 2 2018-01-01 1.000000 AMZN 2018-01-01 # 3 2018-01-01 1.000000 FB 2018-01-01 # 4 2018-01-01 1.000000 NFLX 2018-01-01 # ... ... ... ... ... # 33385 2019-12-02 1.720717 MSFT 2019-12-30 # 33386 2019-12-09 1.752239 MSFT 2019-12-30 # 33387 2019-12-16 1.784896 MSFT 2019-12-30 # 33388 2019-12-23 1.802472 MSFT 2019-12-30 # 33389 2019-12-30 1.788185 MSFT 2019-12-30 # [33390 rows x 4 columns] print(df3.head(10)) # date stock company frame # 0 2018-01-01 1.000000 GOOG 2018-01-01 # 1 2018-01-01 1.000000 AAPL 2018-01-01 # 2 2018-01-01 1.000000 AMZN 2018-01-01 # 3 2018-01-01 1.000000 FB 2018-01-01 # 4 2018-01-01 1.000000 NFLX 2018-01-01 # 5 2018-01-01 1.000000 MSFT 2018-01-01 # 6 2018-01-01 1.000000 GOOG 2018-01-08 ここが1/1になるからframe列は必要 # 7 2018-01-08 1.018172 GOOG 2018-01-08 # 8 2018-01-01 1.000000 AAPL 2018-01-08 # 9 2018-01-08 1.011943 AAPL 2018-01-08
あとはpx.lineでグラフ化するのみ。今回は1列に全企業を入れたのでy='stock'する。また、animation_frame='frame’、animation_group='company’と指定することが可能。1列にすると使える引数が増える。
# pxで折れ線グラフを作成 fig = px.line( df3, x='date', y='stock', markers=True, animation_frame='frame', animation_group='company', color='company', range_x=('2018-01-01', '2019-12-30'), range_y=[0.6, 2.1], ) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'px-animation' # 保存ファイル名の接頭辞 save_name = f"{prefix}_animation_columncompany" 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
のアニメーションは比較的簡単
ということで、今回はPlotly
のpx
を使ったアニメーションの描き方を解説した。軌跡を残さない方法だとサクッとできるのがpx
の魅力。
go
のアニメーションだともっと複雑になるのでとっつきにくい。go
のアニメーションについては以下参照。
-
-
【Plotly&animation】Plotlyのgoでアニメーションを作成
続きを見る
本記事でpx
のアニメーションの描き方を知り、使いこなせていただけると幸いだ。
関連記事
-
-
【plotly&subplot】goでアニメーションのサブプロット
続きを見る
-
-
【plotly&animation】動き方のeasingと遅延のduration
続きを見る
-
-
【px&バブルチャート】plotly.expressで各国の収入と平均寿命の時代変化をアニメーションで
続きを見る
-
-
【plotly&アニメーション】plotlyで各国の収入と平均寿命の時代変化をバブルチャート&アニメーションで描く
続きを見る
-
-
【plotly&go.Scatter】plotlyの散布図グラフの描き方
続きを見る
-
-
【plotly&config】グラフのツールバーを編集する
続きを見る
-
-
【plotly&ボタン】plotlyのupdatemenusにbuttonsを追加
続きを見る
-
-
【plotly&スライダー】plotlyのslidersにスライダーを追加
続きを見る
-
-
【plotly&fill】goで領域を塗りつぶし
続きを見る
-
-
【plotly&3D】goで3Dグラフを作成
続きを見る