カテゴリー

plotly

【plotly&animation】動き方のeasingと遅延のduration

2021年10月17日

こんな人にオススメ

plotlyのアニメーションにある引数easingdurationって一体何?

durationは数値でeasingPlotly公式の「Intro to Animations in Python」には"easing": "quadratic-in-out"ってあったり"easing": "cubic-in-out"っていあるんだけど。

ということで、今回はplotlyのアニメーションに出てくる引数easingdurationについて解説と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

pxgoでのアニメーショングラフの描き方については以下参照。pxだとサクッとできるが、goだとかなり面倒になる。んだけどカスタム性はgoの方が圧倒的に高い。

【px&animation】plotly.expressでアニメーションのグラフを作成

続きを見る

【Plotly&animation】Plotlyのgoでアニメーションを作成

続きを見る

スポンサーリンク
スポンサーリンク

運営者のメガネです。TwitterInstagramも運営してます。

自己紹介はこちらから、お問い合わせはこちら。

運営者メガネ

作成したコード全文

下準備(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関連。データ作成のためにnumpypandasimport。また、今回はpxgoの両方を使用するのでどちらもimport

pioはグラフ保存用に使用する。pioでもグラフ保存については以下の記事参照。

plotlyでグラフを静止画として保存する際に出るポップアップ
【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でアニメーションのグラフを作成

続きを見る

easingfigの中のlayoutで使われいている。なのでfig.[’layout’](もしくはfig.layout)でレイアウトにアクセス、そこからslider, steps, args, transitionと辿っていくことでeasingにたどり着くことができる。

デフォルトではeasinglinearに設定されている。また、durationframetransitionのどちらにも使われているが、frameの方は各フレーム(グラフ下の各スライダーの点)ごとの間隔のこと。

transitiondurationPlotly公式「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でのアニメーションの作成方法はかなり面倒なので、作成手順をサラッと書いておく。多少、グラフ作成の順番が前後することはあるだろうが大まかには以下。

  1. 初期表示用のプロット作成
  2. アニメーション操作用のボタン作成
  3. ボタンをグラフに配置するための設定
  4. レイアウト設定
  5. アニメーションの各フレームでのプロット作成
  6. fig作成

初期表示のプロットは原点(0, 0)にして、各フレームで2次関数のプロットを行っている。

また、pxとは異なりレイアウトなどを手動で決めているので、フォントサイズやマーカーサイズを引数layoutplotlyの中で設定している。

# 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")

updatemenuseasing一覧をグラフ化


ということで、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だとframedurationが反映されない


easing一覧を作成したが、ここからはdurationについても見ていく。durationframeに関するものとtransitionに関するものの2種類あるが、ここではまずframeに関するdurationを変更してみる。

相変わらずframeについてはデフォルト値がわからないが、そもそもpxだと反映されないから次のgoで確認することにする。謎。

変更方法はeasingと同じ。framedurationを変更できるように中身を見つけにいく。

# 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だとframedurationの変更が反映される


goだとframedurationの変更が反映される。デフォルト値はframeに関しては謎だが、変更後の値を1000にすると大きく遅延したので1000よりも低い値と考えられる。

指定方法はbuttonsargsの2つ目のdictframeで行う。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だとtransitiondurationも反映されない


最後は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だとtransitiondurationの変更は反映されてる?


最後はgotransitiondurationを変更してみた。んだけど、反映されているのかよくわからん。一応、値は桁違いに大きい値にしたんだけど、特に変わったところはない。

公式「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")

easingdurationで反映されるのは

最後にgopxeasing, durationのどれが変更されるのかをまとめておく。pxだと全滅なんだけど、使っているバージョンなどで変わってくるかもしれん。

easingframeのdurationtransitionのduration
goX
pxXXX

ちょっとした工夫

今回はPlotlyのアニメーショングラフで使えるeasingdurationについて解説した。easingはアニメーションの遷移の仕方、durationはアニメーションの各フレームの間隔を指定するもの。

本質じゃない部分なので指定したり変更したりしなくてもいいんだけど、ちょっと変更するだけで見栄えが変わるから、覚えておいて損はないだろう。

本記事でeasingdurationについての理解を深めていただけると幸いだ。

関連記事

【plotly&go.Scatter】plotlyの散布図グラフの描き方

続きを見る

自作したplotlyのマーカーのシンボル一覧
【plotly&マーカー】plotlyのマーカーのシンボル

続きを見る

ubblechart_animation1820_2020_50_latest
【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】グラフのツールバーを編集する

続きを見る

関連コンテンツ

スポンサーリンク

Amazonのお買い物で損したない人へ

1回のチャージ金額通常会員プライム会員
¥90,000〜2.0%2.5%
¥40,000〜1.5%2.0%
¥20,000〜1.0%1.5%
¥5,000〜0.5%1.0%

Amazonギフト券にチャージすることでお得にお買い物できる。通常のAmazon会員なら最大2.0%、プライム会員なら2.5%還元なのでバカにならない。

ゲットしたポイントは通常のAmazonでのお買い物に使えるからお得だ。一度チャージしてしまえば、好きなタイミングでお買いものできる。

なお、有効期限は10年だから安心だ。いつでも気軽にAmazonでお買い物できる。

Amazonチャージはここから出来るで

もっとお得なAmazon Prime会員はこちらから

30日間無料登録

執筆者も便利に使わせてもらってる

スポンサーリンク

  • この記事を書いた人

メガネ

独学でpythonを学び天文学系の大学院を修了。 ガジェット好きでMac×Android使い。色んなスマホやイヤホンを購入したいけどお金がなさすぎて困窮中。 元々、人見知りで根暗だったけど、人生楽しもうと思って良い方向に狂ったために今も人生めちゃくちゃ楽しい。 pythonとガジェットをメインにブログを書いていますので、興味を持たれましたらちょこちょこ訪問してくだされば幸いです🥰。 自己紹介→変わって楽しいの繰り返し

-plotly
-, , , ,