カテゴリー

Plotly全般

【plotly&fig作成と更新】add_traceやupdate_layoutの使い方

2022年1月8日

こんな人にオススメ

plotlyでグラフを描く時にadd_traceとかupdate_layoutとかあるけど、これらは一体なんなの?

ということで、今回はpythonのplotlyでグラフを描くときに出てくるadd_traceupdate_layoutなどについて解説する。

実はplotlyでグラフを描く際には色んな方法があって、初めはどの方法がどんなどんなプロットになるのかがわかりにくい。実際、執筆者もplotlyを使い始めた時はよくわからなかった。

今回はグラフ描画時のこれらのコードで何ができるのか、どのような挙動になるのかを解説するので、plotly学習のきっかけにしていただけると幸いだ。

なお、今回の記事はplotly公式の「Creating and Updating Figures in Python」の内容をベースに作成している。

python環境は以下。

  • Python 3.10.1
  • plotly 5.4.0
  • plotly-orca 3.4.2

運営者のメガネです。YouTubeTwitterInstagram、自己紹介はこちら、お問い合わせはこちらから。

運営者メガネ

作成したコード全文

下準備(import

import plotly.graph_objects as go
import plotly.io as pio

まずは下準備としてのimport関連。今回はplotlygoを使って解説する。ploltyではgo以外にもpxなるライブラリもあり、こちらはよりサクッとグラフを描くことが可能。

ただ、plotlyグラフ描画のコード内容を知るならgoを使ったほうが細かいのでおすすめ。[pxについての記事](https://megatenpa.com/category/python/plotly/px/)は以下。

px

【plolty&棒グラフ】px.barでバーチャートを作成

2022/8/19

こんな人にオススメ plotlyのpxで棒グラ ...

px

【plotly&ガントチャート】px.timelineでGantt Chartsを作成

2022/8/19

こんな人にオススメ plotlyでガントチ& ...

px

【plotly&fill】px.areaで積み上げ塗りつぶし

2023/1/14

こんな人にオススメ pxで塗りつぶ{ ...

px

【plotly&px.scatter】pxでの散布図の描き方

2022/8/19

こんな人にオススメ plotlyの中にpxって ...

go.Figureの引数datalistでプロットデータを格納


まずはこのブログ「M天パ」で使っている方法。予めプロットするデータをlistなどで作成しておき、最後にfigに入れる。

執筆者がplotlyを使い始めた際に一番理解しやすかった方法だ。この方法だとプロットとフィギュアを別々で管理することができる。

今回は上の図のように2種類のプロットを用意し、それぞれを引数plotに格納、最後にfigの引数dataに入れる。行数や手間は多いけど、1ステップがわかりやすいと思う。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

# 全プロットを入れるためのlist
plot = []

# プロットをlistへ格納
d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
plot.append(d1)  # プロットを格納

d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
plot.append(d2)  # プロットを格納

fig = go.Figure(data=plot)  # Figureの作成
fig.show()  # Figureの表示

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_append_plot"
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.Figureの引数dataに直接プロットデータを格納

続いてはgo.Figureの中に直接プロットデータを入れるという方法。さっきは一旦、変数plotに入れたけど、今回は直接入れる。

ここでのプロットのようにシンプルなグラフを描きたいときはこれでもいいだろう。ただし、go.Scatterの引数が多いグラフを描こうした時は、go.Figure内がゴチャゴチャしてしまうので注意が必要。

グラフの作成のためのコードはさっきと同じなので、できるグラフのさっきと同じ。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

# Figureに直接入れる
fig = go.Figure(
    data=[
        go.Scatter(x=x, y=y1, name='y1'),
        go.Scatter(x=x, y=y2, name='y2')
    ]
)
fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_append_plot_direct"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html", config=None,)
pio.write_image(fig, f"{save_name}.png")

add_traceで1プロットずつ格納

今度はちょっとテイストが異なり、一旦go.Figurefigを作成、そこにfig.add_traceでどんどんプロットを追加する方法。ネットでplotlyのプロット方法を調べると、この方法で書かれた記事が多くヒットする。

ここではy1, y2のプロットを変数d1, d2においてからfig.add_traceに入れているけど、直接fig.add_traceの中にプロットデータを入れてもいい。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成

d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
fig.add_trace(d1)  # figにd1を追加

d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
fig.add_trace(d2)  # figにd2を追加

fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_add_trace"
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")

なお、fig.add_から始まるコードは他にもある。一部は後で解説するが以下だけ種類がある。多すぎる。

# fig.add_一覧
fig.add_annotation
fig.add_bar
fig.add_barpolar
fig.add_box
fig.add_candlestick
fig.add_carpet
fig.add_choropleth
fig.add_choroplethmapbox
fig.add_class
fig.add_cone
fig.add_contour
fig.add_contourcarpet
fig.add_densitymapbox
fig.add_funnel
fig.add_funnelarea
fig.add_heatmap
fig.add_heatmapgl
fig.add_histogram
fig.add_histogram2d
fig.add_histogram2dcontour
fig.add_hline
fig.add_hrect
fig.add_icicle
fig.add_image
fig.add_indicator
fig.add_isosurface
fig.add_layout_image
fig.add_mesh3d
fig.add_ohlc
fig.add_parcats
fig.add_parcoords
fig.add_pie
fig.add_pointcloud
fig.add_sankey
fig.add_scatter
fig.add_scatter3d
fig.add_scattercarpet
fig.add_scattergeo
fig.add_scattergl
fig.add_scattermapbox
fig.add_scatterpolar
fig.add_scatterpolargl
fig.add_scattersmith
fig.add_scatterternary
fig.add_shape
fig.add_splom
fig.add_streamtube
fig.add_sunburst
fig.add_surface
fig.add_table
fig.add_trace
fig.add_traces
fig.add_traits
fig.add_treemap
fig.add_violin
fig.add_vline
fig.add_volume
fig.add_vrect
fig.add_waterfall

add_traceは1プロットの追加のみ

# add_traceで複数プロットを入れようとするとエラー
fig.add_trace([d1, d2])
# ValueError:
#     Invalid element(s) received for the 'data' property of
#         Invalid elements include: [[Scatter({
#     'name': 'y1', 'x': [1, 2, 3], 'y': [1, 3, 2]
# }), Scatter({
#     'name': 'y2', 'x': [1, 2, 3], 'y': [4, 2, 3.5]
# })]]

#     The 'data' property is a tuple of trace instances
#     that may be specified as:
#       - A list or tuple of trace instances
#         (e.g. [Scatter(...), Bar(...)])
#       - A single trace instance
#         (e.g. Scatter(...), Bar(...), etc.)
#       - A list or tuple of dicts of string/value properties where:
#         - The 'type' property specifies the trace type
#             One of: ['bar', 'barpolar', 'box', 'candlestick',
#                      'carpet', 'choropleth', 'choroplethmapbox',
#                      'cone', 'contour', 'contourcarpet',
#                      'densitymapbox', 'funnel', 'funnelarea',
#                      'heatmap', 'heatmapgl', 'histogram',
#                      'histogram2d', 'histogram2dcontour', 'icicle',
#                      'image', 'indicator', 'isosurface', 'mesh3d',
#                      'ohlc', 'parcats', 'parcoords', 'pie',
#                      'pointcloud', 'sankey', 'scatter',
#                      'scatter3d', 'scattercarpet', 'scattergeo',
#                      'scattergl', 'scattermapbox', 'scatterpolar',
#                      'scatterpolargl', 'scattersmith',
#                      'scatterternary', 'splom', 'streamtube',
#                      'sunburst', 'surface', 'table', 'treemap',
#                      'violin', 'volume', 'waterfall']

#         - All remaining properties are passed to the constructor of
#           the specified trace type

#         (e.g. [{'type': 'scatter', ...}, {'type': 'bar, ...}])

fig.add_traceは1プロットの追加にのみ対応している。したがって、上のコードのように複数プロットを入れるとエラーとなる。

複数プロットを同時に追加したいときは次に解説するadd_tracesを使用する。こっちはtracesと複数形になっている。

add_tracesで複数プロットを一気に格納

fig.add_tracesと、traceを複数形のtracesにすることで複数プロットを同時に追加可能。この際、プロットはtuplelistなどで一括りにしないとエラーとなる。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成

d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
fig.add_traces((d1, d2))  # 複数形にすると複数プロットを追記可能

fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_add_traces"
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")

add_scatter, add_barなどで直接格納


先程のfig.add_一覧に載っているプロットの種類であれば、直接プロットの種類を選択して追加可能。いちいちfig.add_traceの中でプロットの種類を選択しなくてもよくなる。

ここではfig.add_scatterで散布図を、fig.add_barで棒グラフを作成した。fig.add_一覧を再度載せるので、各シチュエーションで使いたいものをピックアップしていただきたい。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成

fig.add_scatter(x=x, y=y1, name='y1')  # scatterを直接追加
fig.add_bar(x=x, y=y2, name='y2')  # barを直接追加
fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_add_scatter_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")
# fig.add_一覧
fig.add_annotation
fig.add_bar
fig.add_barpolar
fig.add_box
fig.add_candlestick
fig.add_carpet
fig.add_choropleth
fig.add_choroplethmapbox
fig.add_class
fig.add_cone
fig.add_contour
fig.add_contourcarpet
fig.add_densitymapbox
fig.add_funnel
fig.add_funnelarea
fig.add_heatmap
fig.add_heatmapgl
fig.add_histogram
fig.add_histogram2d
fig.add_histogram2dcontour
fig.add_hline
fig.add_hrect
fig.add_icicle
fig.add_image
fig.add_indicator
fig.add_isosurface
fig.add_layout_image
fig.add_mesh3d
fig.add_ohlc
fig.add_parcats
fig.add_parcoords
fig.add_pie
fig.add_pointcloud
fig.add_sankey
fig.add_scatter
fig.add_scatter3d
fig.add_scattercarpet
fig.add_scattergeo
fig.add_scattergl
fig.add_scattermapbox
fig.add_scatterpolar
fig.add_scatterpolargl
fig.add_scattersmith
fig.add_scatterternary
fig.add_shape
fig.add_splom
fig.add_streamtube
fig.add_sunburst
fig.add_surface
fig.add_table
fig.add_trace
fig.add_traces
fig.add_traits
fig.add_treemap
fig.add_violin
fig.add_vline
fig.add_volume
fig.add_vrect
fig.add_waterfall

update_tracesで既存データを更新

続いてはupdate_tracesupdateと名がついているからわかりやすいが、これは既存のプロットデータ(traces)を更新する。

新規で入れても意味がない

まずは先ほどと同じように空のfigに対して適用してみる。しかし、元から何もプロットデータがない状態なので、グラフをはまっさらのまま。

figをプリントしてもデータ部分には何も入っていないので当たり前。なお、layouttemplateはグラフのテーマに相当する。以下で解説している。

【plotly&グラフテーマ】plotly既存のthemasをグラフ化

続きを見る

import plotly.graph_objects as go
import plotly.io as pio

# update_tracesをプロットの格納に使っても何も表示されない
x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成

d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
fig.update_traces(d1)
d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
fig.update_traces(d2)

print(fig)
# Figure({
#     'data': [], 'layout': {'template': '...'}
# })

fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_update_tarces_miss"
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")

既存データのアップデートに使用可能

update_tracesは既存のプロットに対して適用されるので、予めfigにプロットを入れておく必要がある。ここでは先ほどと同じようにy1, y2でグラフを作成、その後にupdate_tracesでプロットを線から点に変更した。

fig.update_tracesの引数modego.Scattrerの引数で、プロットの種類を選択できる。ここではmode=’marker’でプロットの種類を点だけにしている。デフォルトは既に見てきたように’lines+markers’

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成
d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
fig.add_traces((d1, d2))

fig.update_traces(mode='markers')  # プロットのモードをマーカーだけに更新
fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_update_traces"
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")

なお、fig.update_で始まるものは以下。基本的にはプロット関連が多い。

# fig.updateシリーズ
fig.update_annotations
fig.update_coloraxes
fig.update_geos
fig.update_layout
fig.update_layout_images
fig.update_mapboxes
fig.update_polars
fig.update_scenes
fig.update_shapes
fig.update_smiths
fig.update_ternaries
fig.update_traces
fig.update_xaxes
fig.update_yaxes

全プロットに共通した引数じゃないとエラー

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成
d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
d2 = go.Bar(x=x, y=y2, name='y2')  # y2のプロット
fig.add_traces((d1, d2))

# go.Barの棒グラフには引数modeはないからエラー
fig.update_traces(mode='markers')  # プロットのモードをマーカーだけに更新
# ValueError: Invalid property specified for object of type plotly.graph_objs.Bar: 'mode'

# Did you mean "base"?

先程の例だとy1, y2どちらもgo.Scatterで引数modeを編集した。しかし、引数modeがないgo.Barを使用したときはエラーとなる。

上の例だとy2のプロットがgo.Barなので、fig.update_tracesmodeを指定するとエラーとなる。

この状況でy1modeを変更したいなら次で解説する引数selectorを使用しなければいけない。

selectorで更新対象を選択

一部のプロットの引数のみを変更したい場合はupdate_tracesで引数selectorを使用すれば良い。

ここではname=’y1’でプロット名がy1のものを選んでいるが、プロットのtype=’scatter’のようにプロットのタイプで指定することも可能。また、marker_colormodeでも指定可能なので、状況に応じて適切な選択が可能。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成
d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
d2 = go.Bar(x=x, y=y2, name='y2')  # y2のプロット
fig.add_traces((d1, d2))

fig.update_traces(
    mode='markers',
    marker_symbol='star',  # マーカーの形状を四角形に
    marker_size=30,  # マーカーのサイズを30に
    selector=dict(name='y1')  # selectorでどのプロットに対して更新するか選択可能
)
fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_update_traces_selector"
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")

overwriteで引数を上書き

また、update_tracesの引数overwrite=Trueで設定内容を上書きすることが可能。ここでは予めmarker_color=’red’でマーカーの色を赤色にしたfig上書きで不透明度opacity=0.2に変更した。

こうすることで、元々の赤色の設定が上書きによって消え、不透明度だけが反映されることになる。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure(go.Bar(x=x, y=y1, name='y1', marker_color='red'))
print(fig)
# Figure({
#     'data': [{'marker': {'color': 'red'}, 'name': 'y1', 'type': 'bar', 'x': [1, 2, 3], 'y': [1, 3, 2]}],
#     'layout': {'template': '...'}
# })

# 透明度で上書き
fig.update_traces(
    overwrite=True,  # 上書きする
    marker={'opacity': 0.2}  # 透明度を変更
)
print(fig)
# Figure({
#     'data': [{'marker': {'opacity': 0.2}, 'name': 'y1', 'type': 'bar', 'x': [1, 2, 3], 'y': [1, 3, 2]}],
#     'layout': {'template': '...'}
# })

fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_update_traces_overwrite"
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")

アンダースコア_だと追加になる

ただし、overwriteする内容をアンダースコア_で繋いでしまうと、上書きではなく追加になることに注意。公式ではこのアンダースコアについて以下の記述がある。

In the example below, the red color of markers is overwritten when updating marker in update_traces() with overwrite=True. Note that setting instead marker_opacity with the magic underscore would not overwrite marker_color because properties would be overwritten starting only at the level of marker.opacity.

要するに、ここの例だとmarker_opacityで書くとopacityの内容だけが更新され、marker全体には影響がないということ。一方で、marker={'opacity': 0.2}とするとmarker自体をこのように書き換えるので上書きになる。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure(go.Bar(x=x, y=y1, name='y1', marker_color='red'))
print(fig)
# Figure({
#     'data': [{'marker': {'color': 'red'}, 'name': 'y1', 'type': 'bar', 'x': [1, 2, 3], 'y': [1, 3, 2]}],
#     'layout': {'template': '...'}
# })

# アンダースコアを使った省略方法だと上書きじゃなくて追記になる
fig.update_traces(
    overwrite=True,  # 上書きする
    marker_opacity=0.2  # アンダースコアの書き方にすると上書きされない
)
print(fig)
# Figure({
#     'data': [{'marker': {'color': 'red', 'opacity': 0.2}, 'name': 'y1', 'type': 'bar', 'x': [1, 2, 3], 'y': [1, 3, 2]}],
#     'layout': {'template': '...'}
# })

fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_update_traces_overwrite_underscore"
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")

for_each_tracefigを後から追記・修正

ちょっと応用的な内容だけど、一度作成したfigを後から修正する際にはfig.for_each_traceを使用することができる。

これを使うことで、forを使って数行かけて変更しなければいけない内容を1行でサクッと変更することができる。

lambda文で所定の動作を1行で

fig.for_each_tracelambda式とともに使われることが多い、fig内の特定の引数を全て書き換えてくれるというもの。forで全プロットについて書き換えるところを1行で済ませるイメージ。

ここではmarker_symbol=’square’でマーカーシンボルを全て四角形に変更した。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成

d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
fig.add_traces((d1, d2))  # 複数形にすると複数プロットを追記可能

print(fig)
# Figure({
#     'data': [{'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...'}
# })

# マーカーのシンボルをsquareに変更
fig.for_each_trace(lambda t: t.update(marker_symbol='square'))
print(fig)
# Figure({
#     'data': [{'marker': {'symbol': 'square'}, 'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'marker': {'symbol': 'square'}, 'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...'}
# })

fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_for_each_trace"
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")

for文でも追記・修正可能

lambda式とか面倒なものが出てきたけど、結局のところは以下とやっていることは同じ。figの中のdata部分について、全プロットのマーカーシンボルを変更している。

以下ではlambda式を使って全プロットを対象にしていた内容を、forで書いている。パッと見のわかりやすさだforを使った方がいいかもしれない。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成

d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
fig.add_traces((d1, d2))  # 複数形にすると複数プロットを追記可能

# forを使って1プロットずつ中身を見て、マーカーシンボルを変更することも可能
for num, _ in enumerate(fig['data']):
    fig['data'][num]['marker_symbol'] = 'square'

print(fig)
# Figure({
#     'data': [{'marker': {'symbol': 'square'}, 'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'marker': {'symbol': 'square'}, 'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...'}
# })

fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_for_each_trace_for"
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")

ifを使って条件分岐

さっきまでは全プロットに対して、マーカーを四角形に変更したが、ifを使うことで特定のプロットに対してのみの適用も可能。以下ではy1だけシンボルを四角形にした。

なお、ifで当てはまらない条件についてはelseで処理されるが、特に処理内容がない場合は()を記述する必要がある。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成

d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
fig.add_traces((d1, d2))  # 複数形にすると複数プロットを追記可能

print(fig)
# Figure({
#     'data': [{'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...'}
# })

# y1だけシンボルを変更
fig.for_each_trace(lambda trace: trace.update(marker_symbol='square') if trace.name == 'y1' else ())
print(fig)
# Figure({
#     'data': [{'marker': {'symbol': 'square'}, 'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'marker': {'symbol': 'square'}, 'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...'}
# })

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_for_each_traces_if"
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")

ifを使うならelseは必須

import plotly.graph_objects as go
import plotly.io as pio

fig = go.Figure()  # 先のFigureを作成

d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
fig.add_traces((d1, d2))  # 複数形にすると複数プロットを追記可能

# elseで行う内容がなくてもelseは書かないといけない
fig.for_each_trace(lambda trace: trace.update(marker_symbol='square') if trace.name == 'y1')
#     fig.for_each_trace(lambda trace: trace.update(marker_symbol='square') if trace.name == 'y1')
#                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# SyntaxError: expected 'else' after 'if' expression

else以下で何も処理することがないからelseを省略しようとするとエラー。必ずelseとセットにする必要がある。

go.Figureの引数layoutlistでレイアウトを格納

ここまでプロットについて書いてきたが、レイアウトについても同様に色んな書き方がある。まずは執筆者がよく使う、一旦他の変数にレイアウト情報を入れるスタイル。

イメージはプロットの時と同じで、何かしらの変数(ここではlayout)で内容を設定してから、go.Figureの引数layoutに入れる。

ここでは横軸・縦軸の軸ラベルを設定してみた。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

# 全プロットを入れるためのlist
plot = []

# プロットをlistへ格納
d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
plot.append(d1)  # プロットを格納

d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
plot.append(d2)  # プロットを格納

# レイアウトの作成
layout = go.Layout(xaxis_title='x', yaxis_title='Y')
# dataとともにlayoutも入れられる
fig = go.Figure(data=plot, layout=layout)  # Figureの作成
print(fig)
# Figure({
#     'data': [{'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...', 'xaxis': {'title': {'text': 'x'}}, 'yaxis': {'title': {'text': 'Y'}}}
# })

fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_append_layout"
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")

update_layoutでレイアウトの更新

レイアウトの場合もupdateが可能。レイアウトはadd_layoutのように追加するスタイルではなく更新するスタイル。なお、annotationなど一部引数についてはaddで追加可能。

update_layoutもその中で軸ラベルの編集を行なった。やっていることは同じ。書き方が異なるだけだ。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成

d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
fig.add_traces((d1, d2))  # 複数形にすると複数プロットを追記可能
print(fig)
# Figure({
#     'data': [{'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...'}
# })

# 既存のfigに軸ラベルを追加
fig.update_layout(xaxis_title='x', yaxis_title='Y')
print(fig)
# Figure({
#     'data': [{'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...', 'xaxis': {'title': {'text': 'x'}}, 'yaxis': {'title': {'text': 'Y'}}}
# })

fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_update_layout"
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")

update_xaxes, update_yaxesで直接変更可能

なお、横軸xaxes、縦軸yaxesについてはそれぞれupdate_xaxes, update_yaxesで直接変更可能。fig.upddate_一覧は以下だった。

直接変更することができるから、引数は軸ラベルのテキストを決めるtitleだけでよくなる。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成

d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
fig.add_traces((d1, d2))  # 複数形にすると複数プロットを追記可能
print(fig)
# Figure({
#     'data': [{'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...'}
# })

# figに直接追加も可能
fig.update_xaxes(title='x')
fig.update_yaxes(title='y')
print(fig)
# Figure({
#     'data': [{'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...', 'xaxis': {'title': {'text': 'x'}}, 'yaxis': {'title': {'text': 'y'}}}
# })

fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_update_xyaxes"
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")

ピリオド.を使ってプロパティへアクセス

最後にピリオド.を使った変更方法を解説する。figdictのように扱うことができるので、以下のようにピリオドを使用することで深い階層にアクセスすることが可能。

1行目がピリオド使用の方法、2行目がカッコを使ってアクセスする方法。個人的にはカッコを使う方がエディタ上での色が変わるからわかりやすい。ただ、ピリオドの方が短く書くことができる。

fig.layout.title.text = 'text'
fig['layout']['title']['text'] = 'text'

layoutに対してピリオドでアクセス

まずはレイアウトから。figの中にはdatalayoutがあるので(他にもアニメーションで使用するframesがある)、その中のlayoutを.でアクセス。

さらに.でlayoutの中のtitleと、titleの中のtextにもアクセス。最後に= ‘text’とすることで、グラフタイトルを’text’に変更することが可能になる。

ここではグラフタイトルにしたけど、もちろん他の引数にも適用可能。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成

d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
fig.add_traces((d1, d2))  # 複数形にすると複数プロットを追記可能
print(fig)
# Figure({
#     'data': [{'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...'}
# })

fig.layout.title.text = 'text'
# # 意味としては以下と同じ
# fig['layout']['title']['text'] = 'text'
print(fig)
# Figure({
#     'data': [{'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...', 'title': {'text': 'text'}}
# })

fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_change_layout_period"
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")

traceに対してピリオドでアクセス

traceの場合はちょっと面倒。というのも、レイアウトのように1つのグラフに1つの対応ではなく、1つのグラフに対して複数のプロットが想定されるから。

したがって、fig.datadataにアクセスした後にどのプロットを選択するのかを選ばないといけない。これについてはピリオドで対応できないので[0]のようにインデックス指定する必要がある。

ここでは0番目、すなわちy1のマーカーのシンボルをsquareに変更した。

import plotly.graph_objects as go
import plotly.io as pio

x = (1, 2, 3)
y1 = (1, 3, 2)
y2 = (4, 2, 3.5)

fig = go.Figure()  # 先のFigureを作成

d1 = go.Scatter(x=x, y=y1, name='y1')  # y1のプロット
d2 = go.Scatter(x=x, y=y2, name='y2')  # y2のプロット
fig.add_traces((d1, d2))  # 複数形にすると複数プロットを追記可能
print(fig)
# Figure({
#     'data': [{'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...'}
# })

print(fig.data)  # figの中のdata一覧
# (Scatter({
#     'name': 'y1', 'x': [1, 2, 3], 'y': [1, 3, 2]
# }), Scatter({
#     'name': 'y2', 'x': [1, 2, 3], 'y': [4, 2, 3.5]
# }))
print(fig.data[0])  # dataの0番目=y1
# Scatter({
#     'name': 'y1', 'x': [1, 2, 3], 'y': [1, 3, 2]
# })
print(fig.data[0].marker)  # y1のマーカー
# scatter.Marker()
print(fig.data[0].marker.symbol)  # y1のマーカーのシンボル
# None
fig.data[0].marker.symbol = 'square'  # シンボルをsquareに変更
print(fig.data[0].marker.symbol)
# square

print(fig)
# Figure({
#     'data': [{'marker': {'symbol': 'square'}, 'name': 'y1', 'type': 'scatter', 'x': [1, 2, 3], 'y': [1, 3, 2]},
#              {'name': 'y2', 'type': 'scatter', 'x': [1, 2, 3], 'y': [4, 2, 3.5]}],
#     'layout': {'template': '...'}
# })

fig.show()

# グラフ保存
prefix = 'plotly-creating-updating-figures'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_change_plot_period"
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")

自分に合ったものを

今回はplotlyでプロットを作成する方法について色々と紹介した。今回の内容を全て書けるようになるのが一番だけど、まずは自分に合ったものから取り入れ、そこから派生させる方がハードルが低いからおすすめ。

本記事をきっかけにplotlyで色んな使いやすいグラフを描けるようになっていただけると幸いだ。

関連記事

【plotly&legendまとめ】凡例の引数一覧

続きを見る

【plotly&pattern】棒グラフとかのパターンまとめ

続きを見る

【plotly&ボタン】plotlyのupdatemenusにbuttonsを追加

続きを見る

【plotly&棒グラフ】go.Barでバーチャートを作成

続きを見る

【plotly&fill】goで領域を塗りつぶし

続きを見る

【plotly&スライダー】plotlyのslidersにスライダーを追加

続きを見る

【plotly&3D】goで3Dグラフを作成

続きを見る

【plotly&size, width】Scatterのサイズやlineの太さ一覧表を作成

続きを見る

ガジェット

2023/9/18

【デスクツアー2022下半期】モノは少なく、でも効率的に Desk Updating #0

今回はガジェットブロガーなのにデスク環境を構築していない執筆者の ...

ライフハック

2023/9/16

【Audible vs YouTube Premium】耳で聴く音声学習コンテンツを比較

ワイヤレスイヤホンが普及し耳で学習することへのハードルが格段に下 ...

完全ワイヤレスイヤホン(TWS)

2023/9/18

【SENNHEISER MOMENTUM True Wireless 3レビュー】全てが整ったイヤホン

今回は高音質・高機能なSENNHEISERのフラグシップ完全ワイヤレスイヤホン「SENNH ...

ライフハック

2023/3/11

【YouTube Premiumとは】メリットしかないから全員入れ

今回はYouTube Premiumを実際に使ってみてどうなのか、どんなメリット/デメリット ...

マウス

2023/8/17

【Logicool MX ERGOレビュー】疲れない作業効率重視トラックボールマウス

こんな人におすすめ トラックボールマウスの王道Logicool MX ERGOが気になるけどऩ ...

ベストバイ

2023/9/18

【ベストバイ2022】今年買って良かったモノのトップ10

2022年ベストバイ この1年を振り返って執筆者は何を買ったのか。ガジェッ& ...

スマホ

2023/1/15

【楽天モバイル×povo2.0の併用】月1,000円の保険付きデュアルSIM運用

こんな人におすすめ 楽天モバイルとpovo2.0のデュアルSIM運用って実際のとこ ...

マウス

2023/9/16

【Logicool MX ERGO vs MX Master 3】ERGOをメインにした決定的な理由

こんな疑問・お悩みを持っている人におすすめ 執筆者はLogicoolのハイエンӠ ...

macOSアプリケーション

2022/9/30

【Chrome拡張機能】便利で効率的に作業できるおすすめの拡張機能を18個紹介する

こんな人におすすめ Chromeの拡張機能を入れたいけど、調べても同じような ...

macOSアプリケーション

2023/5/3

【Automator活用術】Macで生産性を上げる作業の自動化術

今回はMacに標準でインストールされているアプリ「Automator」を使ってできる ...

Pythonを学びたいけど独学できる時間なんてない人へのすゝめ

執筆者は大学の研究室・大学院にて独学でPythonを習得した。

でも社会人になったら独学で行うには時間も体力もなくて大変だ。

時間がない社会人だからこそプロの教えを乞うのが効率的。

ここでは色んなタイプに合ったプログラミングスクールの紹介をする。

  • この記事を書いた人

メガネ

Webエンジニア駆け出し。独学のPythonで天文学系の大学院を修了。常時金欠のガジェット好きでM2 Pro MacBook Pro(30万円) x Galaxy S22 Ultra(17万円)使いの狂人。自己紹介と半生→変わって楽しいの繰り返しレビュー依頼など→お問い合わせ運営者情報、TwitterX@m_ten_pa、 YouTube@megatenpa、 Threads@megatenpa

-Plotly全般
-, , , ,