カテゴリー

Plotly全般

【plotly&rangeslider/rangeselector】レンジスライダーとレンジセレクターで時系列を見やすく

2021年11月14日

こんな人にオススメ

plotlyの時系列のグラフで特定の特定の期間をうまく表現できないかね。

全体の中でこの期間はこんな感じっていうグラフが作りたい。

ということで、今回はplotlyrangesliderrangeselectorを使用して横軸の範囲を明示する方法を解説する。現状は横軸にしか対応していないが、期間を見たい時には重宝する。

横軸の移動や拡大などは特に設定しなくても可能だが、rangesliderを使用することで全体の中で拡大した部分はどんな感じなのかが分かりやすい。

また、1年や10年など特定の期間を自動で拡大してくれるrangeselectorも便利。こちらを使用すると丁度10年とかを選択できる。

基本はPlotly公式の「Range Slider and Selector in Python」をベースに、噛み砕きながら解説する。

python環境は以下。

  • Python 3.10.1
  • pandas 1.3.5
  • plotly 5.4.0
  • plotly-orca 3.4.2

運営者のメガネです。YouTubeTwitterInstagramも運営中。自己紹介お問い合わせページあります。

運営者メガネ

作成したコード全文

下準備(import

import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
import plotly.io as pio

まずは下準備としてのimport関連。グラフ化するデータをpandasのデータフレームで扱い、それを[go](<https://megatenpa.com/category/python/plotly/go/>)でグラフ化、pioで保存する。pioでのグラフ保存については以下。

plotlyでグラフを静止画として保存する際に出るポップアップ
【plotly&orca】plotlyで静止画保存(orca)

続きを見る

[px](<https://megatenpa.com/category/python/plotly/px/>)は最後にpxでもレンジスライダー・レンジセレクターを追加できることを示すために使用。

レンジスライダー・セレクターなしのグラフ


まずはレンジスライダーもレンジセレクターもないグラフを作成。データが線のグラフとなっているだけ。

今回はAppleの株価っぽいデータを使用する。データフレームの中身は以下。

import pandas as pd

# データの読み込み
file = '<https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv>'
df = pd.read_csv(file)
print(df)
#            Date   AAPL.Open   AAPL.High  ...        mavg          up   direction
# 0    2015-02-17  127.489998  128.880005  ...  117.927667  129.114281  Increasing
# 1    2015-02-18  127.629997  128.779999  ...  118.940333  130.038244  Increasing
# 2    2015-02-19  128.479996  129.029999  ...  119.889167  130.884089  Decreasing
# 3    2015-02-20  128.619995  129.500000  ...  120.763500  131.741551  Increasing
# 4    2015-02-23  130.020004  133.000000  ...  121.720167  133.067817  Increasing
# ..          ...         ...         ...  ...         ...         ...         ...
# 501  2017-02-10  132.460007  132.940002  ...  124.498666  134.503328  Decreasing
# 502  2017-02-13  133.080002  133.820007  ...  125.205166  135.589534  Increasing
# 503  2017-02-14  133.470001  135.089996  ...  125.953499  136.731280  Increasing
# 504  2017-02-15  135.520004  136.270004  ...  126.723499  137.901963  Decreasing
# 505  2017-02-16  135.669998  135.899994  ...  127.504333  138.805366  Decreasing

# [506 rows x 11 columns]

# 列の一覧
print(df.columns.values)
# ['Date' 'AAPL.Open' 'AAPL.High' 'AAPL.Low' 'AAPL.Close' 'AAPL.Volume'
#  'AAPL.Adjusted' 'dn' 'mavg' 'up' 'direction']

列の一覧の中に「AAPL」という文字が入っているのでこれを削除する。削除にはstr.replace(「対象の文字」, 「変更後の文字」)の置き換えを使う。

# データ列のAAPLという文字を削除
df.columns = [col.replace('AAPL.', '') for col in df.columns]
print(df)

#            Date        Open        High  ...        mavg          up   direction
# 0    2015-02-17  127.489998  128.880005  ...  117.927667  129.114281  Increasing
# 1    2015-02-18  127.629997  128.779999  ...  118.940333  130.038244  Increasing
# 2    2015-02-19  128.479996  129.029999  ...  119.889167  130.884089  Decreasing
# 3    2015-02-20  128.619995  129.500000  ...  120.763500  131.741551  Increasing
# 4    2015-02-23  130.020004  133.000000  ...  121.720167  133.067817  Increasing
# ..          ...         ...         ...  ...         ...         ...         ...
# 501  2017-02-10  132.460007  132.940002  ...  124.498666  134.503328  Decreasing
# 502  2017-02-13  133.080002  133.820007  ...  125.205166  135.589534  Increasing
# 503  2017-02-14  133.470001  135.089996  ...  125.953499  136.731280  Increasing
# 504  2017-02-15  135.520004  136.270004  ...  126.723499  137.901963  Decreasing
# 505  2017-02-16  135.669998  135.899994  ...  127.504333  138.805366  Decreasing

# [506 rows x 11 columns]

あとはこれをグラフ化するだけ。ここではgoの散布図作成であるgo.Scatterを使用。go.Scatterの描き方とfig.update_layoutなどのグラフ更新の方法については以下の記事参照。

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

続きを見る

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

続きを見る

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

# figureの作成
fig = go.Figure()

# プロットを追加
fig.add_trace(go.Scatter(x=list(df.Date), y=list(df.High)))

# グラフタイトルの追加
fig.update_layout(title_text='Time series')

# グラフ全体とホバーのフォントサイズ変更
fig.update_layout(font_size=20, hoverlabel_font_size=20)
fig.show()

# グラフ保存
prefix = 'rangeselector_slider'  # 保存ファイル名の接頭辞
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")

ここでのコード全文を以下に書いておく。一括で見たい方は見ていただきたい。

レンジスライダーを追加


続いてはレンジスライダーを追加。レンジスライダーは上のグラフでわかるように、横軸に範囲を指定できるバーのことを言う。左右の橋の縦の棒を動かすことで、簡単に表示を制限することが可能。

一応、横軸にマウスを持っていってスライドすることで、表示範囲は変更できる。んだけど、レンジスライダーを使うことで全体の中でどこを拡大しているのかがわかりやすい。スライダーの記事は以下。

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

続きを見る

追加は簡単でfig.update_layoutxaxis_rangesliderもしくはxaxis=dict(rangeslider=というように書き、visible=Trueとするだけ。お手軽。

# レンジスライダーを追加

# データの用意と修正
file = '<https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv>'
df = pd.read_csv(file)
df.columns = [col.replace('AAPL.', '') for col in df.columns]

# figureの作成
fig = go.Figure()

# プロットを追加
fig.add_trace(
    go.Scatter(x=list(df.Date), y=list(df.High))
)

# グラフタイトルとレンジスライダーの追加
fig.update_layout(
    # グラフタイトル
    title_text='Time series with range slider',
    # レンジスライダー
    xaxis_rangeslider=dict(
        visible=True,
    ),

# グラフ全体とホバーのフォントサイズ変更
fig.update_layout(font_size=20, hoverlabel_font_size=20)
fig.show()

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

レンジスライダーで縦軸の拡大も可能に


レンジスライダー便利なんだけど、 visible=Trueだけだとconfigのzoom機能では横軸の範囲変更しかできない。不便。

configのズーム機能はグラフ右上の虫眼鏡のマークで、プロット領域をドラッグすることで拡大できるというもの。configについては以下参照。

【plotly&config】グラフのツールバーを編集する

続きを見る

ただ、これはy軸の設定を変更することで解決する。yaxisfixedrange=Falseとすることで縦軸の範囲固定を解除することが可能。

ドラッグでグラフの拡大ができるし、拡大した範囲でレンジスライダーも自動で拡大領域の色分けをしてくれる。ありがたい。

# レンジスライダーで縦軸の拡大も可能にする

# データの用意と修正
file = '<https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv>'
df = pd.read_csv(file)
df.columns = [col.replace('AAPL.', '') for col in df.columns]

# figureの作成
fig = go.Figure()

# プロットを追加
fig.add_trace(
    go.Scatter(x=list(df.Date), y=list(df.High))
)

# グラフタイトルとレンジスライダーの追加
fig.update_layout(
    # グラフタイトル
    title_text='Time series with range slider',
    # レンジスライダー
    xaxis_rangeslider=dict(visible=True,),
    yaxis=dict(
        # y軸のズームを可能にする
        fixedrange=False
    ),
)

# グラフ全体とホバーのフォントサイズ変更
fig.update_layout(font_size=20, hoverlabel_font_size=20)
fig.show()

# グラフ保存
prefix = 'rangeselector_slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_rangeslider_fixedrange"
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")# レンジスライダーを追加

# データの用意と修正
file = '<https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv>'
df = pd.read_csv(file)
df.columns = [col.replace('AAPL.', '') for col in df.columns]

# figureの作成
fig = go.Figure()

# プロットを追加
fig.add_trace(
    go.Scatter(x=list(df.Date), y=list(df.High))
)

# グラフタイトルとレンジスライダーの追加
fig.update_layout(
    # グラフタイトル
    title_text='Time series with range slider',
    # レンジスライダー
    xaxis_rangeslider=dict(
        visible=True,
    ),

レンジスライダーの見た目を変更


レンジスライダーは通常のグラフ同様、色やサイズの変更が可能。上のグラフでは4つの変更を行った。他の引数含め詳しくは公式の「Python Figure Reference: layout.xaxis」を参照。

  • bgcolor='black': 背景を黒に(デフォルトは"#fff”
  • bordercolor='violet': 枠線をヴァイオレットに(デフォルトは"#444”
  • borderwidth=6: 枠線の太さを6に(デフォルトは0=枠線なし)
  • thickness=0.3: スライダーの厚さを0.3に(デフォルトは0.15)

変更はvisibleと同じxaxis_rangesliderで指定するだけ。簡単。

# レンジスライダーの見た目を編集

# データの用意と修正
file = '<https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv>'
df = pd.read_csv(file)
df.columns = [col.replace('AAPL.', '') for col in df.columns]

# figureの作成
fig = go.Figure()

# プロットを追加
fig.add_trace(
    go.Scatter(x=list(df.Date), y=list(df.High))
)

# グラフタイトルとレンジスライダーの追加
fig.update_layout(
    # グラフタイトル
    title_text='Time series with range slider',
    # レンジスライダー
    xaxis_rangeslider=dict(
        visible=True,
        bgcolor='black',  # 背景色
        bordercolor='violet',  # 枠線の色
        borderwidth=6,  # 枠線の太さ
        thickness=0.3,  # レンジスライダーの厚さ
    ),
)

# グラフ全体とホバーのフォントサイズ変更
fig.update_layout(font_size=20, hoverlabel_font_size=20)
fig.show()

# グラフ保存
prefix = 'rangeselector_slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_rangeslider_arrange"
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のupdatemenusにbuttonsを追加

続きを見る

レンジスライダーのように自分で期間を変更するのではなく、予め決められた期間を指定するだけなので正確。しかも上のグラフでもわかるように複数期間をボタンとして登録できる。

使用した期間は以下。

  • 1ヶ月
  • 半年(6ヶ月)
  • 年初来(year-to-date)
  • 1年

年初来ってにが分かりにくいが、要するに表示範囲の最新の日付の年の最初の日=1月1日までを表示すると言うもの。例えば表示されている最新の日付(右端)が2022年2月14日なら一番古い日付(左端)は2022年1月1日になる。

もちろんYTDじゃなくてMTDにすることも可能。1ヶ月のMTDなら最新の月の初め、すなわち○月1日が左端に来る。2ヶ月なら前の月なので○-1月1日が左端に来る。

なお、rangeselectorで使用できる単位は以下。週単位がないので1週間にしたい時は7days換算にする必要がある。また、全期間表示はall一択。

  • 'month': 月単位
  • 'year':年単位
  • 'day': 日単位
  • 'hour': 時間単位
  • 'minute': 分単位
  • 'second': 秒単位
  • 'all': 全期間
# レンジセレクターを追加

# データの用意と修正
file = '<https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv>'
df = pd.read_csv(file)
df.columns = [col.replace('AAPL.', '') for col in df.columns]

# figureの作成
fig = go.Figure()

# プロットを追加
fig.add_trace(
    go.Scatter(x=list(df.Date), y=list(df.High))
)

# グラフタイトルとレンジスライダーの追加
fig.update_layout(
    # グラフタイトル
    title_text='Time series with range selector',
    # レンジセレクターの追加
    xaxis=dict(
        rangeselector=dict(
            buttons=list([
                dict(count=1,
                     label='1m',  # ボタンラベル
                     step='month',  # 1ヶ月
                     stepmode='backward'),
                dict(count=6,
                     label='6m',
                     step='month',  # 半年
                     stepmode='backward'),
                dict(count=1,
                     label='YTD',  # 表示範囲の最新の日付まで、その年の年初から
                     step='year',
                     stepmode='todate'),
                dict(count=1,
                     label='1y',
                     step='year',  # 1年
                     stepmode='backward'),
                dict(step='all')  # 全期間表示
            ])
        ))
)

# グラフ全体とホバーのフォントサイズ変更
fig.update_layout(font_size=20, hoverlabel_font_size=20)
fig.show()

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

なお、レンジセレクターのレイアウトの編集はレンジスライダーやボタンと似ているので割愛する。詳しい引数などは公式の「Python Figure Reference: layout.xaxis」参照。

go.Layoutにレンジスライダー(セレクター)を設定


ここまではfig.update_layoutでレンジスライダーもしくはレンジセレクターを設定したが、もちろんgo.Layoutで設定することも可能。個人的にはupdateするよりもgo.Layoutで書く方好き。

  • プロットとレイアウトを一元管理できる
  • いちいちアップデートしなくても一気に書ける
  • 最後にfigに入れるので頭の中でまとめやすい

要するにプロット・レイアウト・フィギュアを切り分けて考えられる点で好みってこと。

基本的なコードの書き方はfig.updateシリーズと同じ。もちろん出来るグラフもfig.update_layoutなどを使用した時と同じ。

# go.Layoutにレンジスライダー(セレクター)を設置

# データの用意と修正
file = '<https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv>'
df = pd.read_csv(file)
df.columns = [col.replace('AAPL.', '') for col in df.columns]

# プロットの作成
plot = [go.Scatter(x=list(df.Date), y=list(df.High))]
# レイアウトの作成
layout = go.Layout(
    title_text='Time series with range slider',
    # レンジスライダー
    xaxis=dict(
        rangeslider=dict(
            visible=True,
        ),
    )
)

# figureの作成
# ここでプロットとレイアウトを入れる
fig = go.Figure(data=plot, layout=layout)

# グラフ全体とホバーのフォントサイズ変更
fig.update_layout(font_size=20, hoverlabel_font_size=20)
fig.show()

# グラフ保存
prefix = 'rangeselector_slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_rangeslider_ini"
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 Slider and Selector in Python」の「Basic Range Slider and Range Selectors」のグラフ。

レンジセレクターで期間を指定するとレンジスライダーに反映さることがわかる。

# レンジスライダーとレンジセレクターを追加

# データの用意と修正
file = '<https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv>'
df = pd.read_csv(file)
df.columns = [col.replace('AAPL.', '') for col in df.columns]

# figureの作成
fig = go.Figure()

# プロットを追加
fig.add_trace(
    go.Scatter(x=list(df.Date), y=list(df.High))
)

# グラフタイトルとレンジスライダーの追加
fig.update_layout(
    # グラフタイトル
    title_text='Time series with range slider and selectors',
    # レンジスライダー
    xaxis_rangeslider=dict(visible=True,),
    yaxis=dict(
        # y軸のズームを可能にする
        fixedrange=False
    ),
    # レンジセレクターの追加
    xaxis=dict(
        rangeselector=dict(
            buttons=list([
                dict(count=1,
                     label='1m',  # ボタンラベル
                     step='month',  # 1ヶ月
                     stepmode='backward'),
                dict(count=6,
                     label='6m',
                     step='month',  # 半年
                     stepmode='backward'),
                dict(count=1,
                     label='YTD',  # 表示範囲の最新の日付まで、その年の年初から
                     step='year',
                     stepmode='todate'),
                dict(count=1,
                     label='1y',
                     step='year',  # 1年
                     stepmode='backward'),
                dict(step='all')  # 全期間表示
            ])
        ))
)

# グラフ全体とホバーのフォントサイズ変更
fig.update_layout(font_size=20, hoverlabel_font_size=20)
fig.show()

# グラフ保存
prefix = 'rangeselector_slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_rangeslider_rangeselector"
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 Slider with Vertically Stacked Subplots」のグラフ。コードの量が半端ないが、分解してみると同じことの繰り返し。落ち着いて。

分解して解説していく。なお、全文は以下。クソ長いから注意。

figureの作成

# figureの作成
fig = go.Figure()

まずはfigureの作成。これは簡単。

add_traceでプロットの追加

# プロットの追加
fig.add_trace(go.Scatter(
    # xの値
    x=['2013-01-15', '2013-01-29', '2013-02-26', '2013-04-19',
       '2013-07-02', '2013-08-27', '2013-10-22', '2014-01-20',
       '2014-05-05', '2014-07-01', '2015-02-09', '2015-04-13',
       '2015-05-13', '2015-06-08', '2015-08-05', '2016-02-25'],
    # yの値
    y=['8', '3', '2', '10', '5', '5', '6', '8',
       '3', '3', '7', '5', '10', '10', '9', '14'],
    name='var0',  # プロットの名称
    # ホバーに表示する内容
    text=['8', '3', '2', '10', '5', '5', '6', '8',
          '3', '3', '7', '5', '10', '10', '9', '14'],
    # どのy軸にプロットするか
    yaxis='y',
))

次は各データのプロット。いきなりややこしくなっているが、分解してみると以下の構造。シンプル。なお、上のコードはvar0を示している。

  • fig.add_tracefigureにプロットを追加
  • go.Scattrerで散布図を指定
  • xは横軸、yは縦軸
  • nameはプロット名
  • textはホバーに表示する内容
  • yaxisで縦軸をどの軸にするか(2軸グラフ的な)
  • 上記をvar0からvar4まで繰り返す

同じ構造が続くので、xydictでまとめてfor文で回しコードを短くすることも可能。

var0からvar4は以下。

# プロットの追加
fig.add_trace(go.Scatter(
    # xの値
    x=['2013-01-15', '2013-01-29', '2013-02-26', '2013-04-19',
       '2013-07-02', '2013-08-27', '2013-10-22', '2014-01-20',
       '2014-05-05', '2014-07-01', '2015-02-09', '2015-04-13',
       '2015-05-13', '2015-06-08', '2015-08-05', '2016-02-25'],
    # yの値
    y=['8', '3', '2', '10', '5', '5', '6', '8',
       '3', '3', '7', '5', '10', '10', '9', '14'],
    name='var0',  # プロットの名称
    # ホバーに表示する内容
    text=['8', '3', '2', '10', '5', '5', '6', '8',
          '3', '3', '7', '5', '10', '10', '9', '14'],
    # どのy軸にプロットするか
    yaxis='y',
))

fig.add_trace(go.Scatter(
    x=['2015-04-13', '2015-05-13', '2015-06-08', '2015-08-05', '2016-02-25'],
    y=['53.0', '69.0', '89.0', '41.0', '41.0'],
    name='var1',
    text=['53.0', '69.0', '89.0', '41.0', '41.0'],
    yaxis='y2',
))

fig.add_trace(go.Scatter(
    x=['2013-01-29', '2013-02-26', '2013-04-19', '2013-07-02', '2013-08-27',
       '2013-10-22', '2014-01-20', '2014-04-09', '2014-05-05', '2014-07-01',
       '2014-09-30', '2015-02-09', '2015-04-13', '2015-06-08', '2016-02-25'],
    y=['9.6', '4.6', '2.7', '8.3', '18', '7.3', '3', '7.5',
       '1.0', '0.5', '2.8', '9.2', '13', '5.8', '6.9'],
    name='var2',
    text=['9.6', '4.6', '2.7', '8.3', '18', '7.3', '3', '7.5',
          '1.0', '0.5', '2.8', '9.2', '13', '5.8', '6.9'],
    yaxis='y3',
))

fig.add_trace(go.Scatter(
    x=['2013-01-29', '2013-02-26', '2013-04-19', '2013-07-02', '2013-08-27',
       '2013-10-22', '2014-01-20', '2014-04-09', '2014-05-05', '2014-07-01',
        '2014-09-30', '2015-02-09', '2015-04-13', '2015-06-08', '2016-02-25'],
    y=['6.9', '7.5', '7.3', '7.3', '6.9', '7.1', '8', '7.8',
       '7.4', '7.9', '7.9', '7.6', '7.2', '7.2', '8.0'],
    name='var3',
    text=['6.9', '7.5', '7.3', '7.3', '6.9', '7.1', '8', '7.8',
          '7.4', '7.9', '7.9', '7.6', '7.2', '7.2', '8.0'],
    yaxis='y4',
))

fig.add_trace(go.Scatter(
    x=['2013-02-26', '2013-07-02', '2013-09-26', '2013-10-22', '2013-12-04',
       '2014-01-02', '2014-01-20', '2014-05-05', '2014-07-01', '2015-02-09',
       '2015-05-05'],
    y=['290', '1078', '263', '407', '660', '740',
       '33', '374', '95', '734', '3000'],
    name='var4',
    text=['290', '1078', '263', '407', '660', '740',
          '33', '374', '95', '734', '3000'],
    yaxis='y5',
))

update_tracesで全プロット共通の修正

# 全プロットで共通の修正部分
fig.update_traces(
    hoverinfo='name+x+text',  # ホバーに表示する項目
    line={'width': 0.5},  # 線の太さ
    marker={'size': 8},  # マーカーのサイズ
    mode='lines+markers',  # プロットの種類
    showlegend=False  # 凡例を表示しない
)

続いてはfig.update_tracesで作成したgo.Scatterのプロットの内容を修正する。以下の項目を修正している。

  • hoverinfo='name+x+text': マウスオーバー時の内容をプロット名+横軸の値+引数text
  • line={'width': 0.5}: プロット線の太さを0.5に
  • marker={'size': 8}: マーカーのサイズを8に
  • mode='lines+markers': プロットを線+マーカーに
  • showlegend=False: 凡例は非表示

var0からvar4まで毎回、上の設定を書いてもいいんだけど共通事項ならfig.add_traceでプロットを作成してからfig.update_tracesで修正した方が楽。

fig.add_tracefig.update_tracesについては以下の記事で解説している。

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

続きを見る

グラフ中にstate1, state2の文字を追加

# グラフ中に文字(テキスト)を追加
fig.update_layout(
    annotations=[
        dict(
            x='2013-06-01',  # xの位置
            y=0,  # yの位置
            arrowcolor='rgba(63, 81, 181, 0.2)',  # テキストの上の線(矢印)の色
            arrowsize=0.3,  # テキストの上の線(矢印)の色
            ax=0,  # 線(矢印)のx方向の長さ
            ay=30,  # 線(矢印)のy方向の長さ
            text='state1',  # 表示するテキスト
            xref='x',  # xの基準はx軸
            yanchor='bottom',  # yの基準位置
            yref='y'  # yの基準はy軸
        ),
        dict(
            x='2014-09-13',
            y=0,
            arrowcolor='rgba(76, 175, 80, 0.1)',
            arrowsize=0.3,
            ax=0,
            ay=30,
            text='state2',
            xref='x',
            yanchor='bottom',
            yref='y'
        )
    ],
)

続いてはグラフ中にstate1, state2の文字を追加。レイアウトの中の引数annotationを使用することで実現可能。

annotationは矢印を作成するのに使うが、テキストも追加できるので矢印を消してしまえばグラフ中にテキストを追加することが可能。以下の項目を指定。

  • x: xの位置
  • y: yの位置
  • arrowcolor: テキストの上の線(矢印)の色
  • arrowsize: テキストの上の線(矢印)の色
  • ax: 線(矢印)のx方向の長さ
  • ay: 線(矢印)のy方向の長さ
  • text: 表示するテキスト
  • xref: xの基準はx軸
  • yanchor: yの基準位置
  • yref: yの基準はy軸

詳しい解説や引数はPlotly公式の「Text and Annotations in Python」や「Python Figure Reference: layout.annotations」参照。

グラフに塗りつぶしを追加

# グラフ上に四角形の両機を追加
fig.update_layout(
    shapes=[
        dict(
            fillcolor='rgba(63, 81, 181, 0.2)',  # 塗りつぶしの色
            line={'width': 0},  # 枠線の色
            type='rect',  # 図形のタイプ
            x0='2013-01-15',  # xのスタート位置
            x1='2013-10-17',  # xのエンド位置
            xref='x',  # xの基準はx軸
            y0=0,  # yのスタート位置
            y1=0.95,  # yのエンド位置
            yref='paper'  # yの基準はプロット面上
        ),
        dict(
            fillcolor='rgba(76, 175, 80, 0.1)',
            line={'width': 0},
            type='rect',
            x0='2013-10-22',
            x1='2015-08-05',
            xref='x',
            y0=0,
            y1=0.95,
            yref='paper'
        )
    ]
)

続いてはさっきのstate1, 2の領域に塗りつぶしを追加。塗りつぶしについては色んな方法があるが、ここではレイアウトを使ったshapesで使用。他にもgo.Scatterでも可能。詳しくは以下。

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

続きを見る

【plotly&shapes】レイアウトとしてグラフ内に図形を配置する

続きを見る

引数はこれまで同じようなものが並ぶ。line_widthで枠線の太さを指定し、typeで図形の種類を指定。x0, y0x, yのそれぞれの開始位置を指定、x1, y1で終了位置を指定している。

xref, yrefはそれぞれx, yの位置をどこに基準にするか。xref=’x’とすることでx軸の値=x0, x1の値となる。すなわちx0, x1の値はx軸の日付と連動する。

一方でpaperにするとグラフ全体の相対位置となる。yyref='paper’としているが、これはプロット領域の下端を0、上端を1とした相対位置。なのでy1=0.95は上端から0.05下の位置って意味。

fig.update_layoutでレイアウトの編集(xaxis

# x軸
xaxis=dict(
    autorange=True,  # 自動で範囲調節
    # 表示範囲
    range=['2012-10-31 18:36:37.3129', '2016-05-10 05:23:22.6871'],
    # レンジスライダーの追加
    rangeslider=dict(
        autorange=True,  # 自動範囲調節
        # 表示範囲
        range=['2012-10-31 18:36:37.3129', '2016-05-10 05:23:22.6871']
    ),
    type='date'  # x軸の軸タイプ
),

fig.update_layoutxaxisについて。ここでは以下の項目を設定。

  • autorange: 表示範囲の自動調節
  • range: グラフ作成時の表示範囲
  • rangeslider: レンジスライダー
    • autorange: 表示範囲の自動調節
    • range: グラフ作成時の表示範囲
  • type: 軸の型(タイプ、形式)

レンジスライダーは追加だけしたい時はvisible=Trueにする必要があるが、autorangeなど他の引数を設定したいときはvisibleの指定は必要ない。

fig.update_layoutでレイアウトの編集(yaxis

# y軸
yaxis=dict(
    anchor='x',  # x軸に固定
    autorange=True,  # 自動範囲調節
    domain=[0, 0.2],  # プロット位置を相対位置(0-1)のどこに置くか
    linecolor='#673ab7',  # 軸の色
    mirror=True,  # 反対側にも軸の色を反映
    range=[-60.0858369099, 28.4406294707],  # 軸の範囲
    showline=True,  # 軸の線を表示
    side='right',  # 軸を表示するサイド
    tickfont={'color': '#673ab7'},  # 軸のフォントカラー
    tickmode='auto',  # 目盛のモード
    ticks='',  # 目盛を降らない
    titlefont={'color': '#673ab7'},  # 軸ラベルの色
    type='linear',  # 軸タイプ
    zeroline=False  # 0の線は引かない
),

続いてはy軸について。こちらも長いのでy2軸などは省略してy軸だけ表示した。yaxisの場合の設定項目は以下。

  • anchor='x': x軸に固定
  • autorange=True: 自動範囲調節
  • domain=[0, 0.2]: プロット位置を相対位置(0-1)のどこに置くか
  • linecolor='#673ab7': 軸の色
  • mirror=True: 反対側にも軸の色を反映
  • range=[-60.0858369099, 28.4406294707]: 軸の範囲
  • showline=True: 軸の線を表示
  • side='right': 軸を表示するサイド
  • tickfont={'color': '#673ab7'}: 軸のフォントカラー
  • tickmode='auto': 目盛のモード
  • ticks='': 目盛を振らない
  • titlefont={'color': '#673ab7'}: 軸ラベルの色
  • type='linear': 軸タイプ
  • zeroline=False: 0の線は引かない

これらの項目を各軸ごとに設定しているだけ。例えばvar1の場合はgo.Scatterの引数yaxis=’y2’だったのでレイアウトでもyaxis2=で引数を設定すればいい。

update_layoutで各レイアウト全般の設定

# レイアウト全般の設定
fig.update_layout(
    dragmode='zoom',  # ドラッグした時の挙動はズームに
    hovermode='x',  # ホバー時にxの値を表示
    legend=dict(traceorder='reversed'),  # 凡例の向きは逆転(いらん気がする)
    height=600,  # グラフの高さ
    template='plotly_white',  # グラフのテンプレート
    # 余白の設定
    margin=dict(
        t=100,
        b=100
    ),
)

最後の設定はレイアウト全般の設定。以下の項目を設定している。

  • dragmode='zoom': ドラッグした時の挙動はズームに
  • hovermode='x': ホバー時にxの値を表示
  • legend=dict(traceorder='reversed'): 凡例の向きは逆転(いらん気がする)
  • height=600: グラフの高さ
  • template='plotly_white': グラフのテンプレート
  • margin=dict(t=100, b=100): 余白の設定

dragmodeはデフォルトでzoomな気がするが一応設定している。hovermode=’x’でホバー時にその点の情報以外にxの値もx軸に表示。

legendtraceorderはいらん気がするが設定している。heightでグラフの高さを固定、幅widthはレスポンシブルのまま。

templateでグラフのベースを指定。ここでは背景が白くなるなどシンプルな見た目の’plotly_white’を指定。テンプレートについては以下の記事参照。

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

続きを見る

グラフ表示と保存

# グラフ全体とホバーのフォントサイズ変更
fig.update_layout(font_size=20, hoverlabel_font_size=20)
fig.show()

# グラフ保存
prefix = 'rangeselector_slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_stacked"
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 Slider with Vertically Stacked Subplots」のコードを分解してきた。長いんだけ分解したら結構シンプル。

できるグラフは上の通り。積み重ねでもレンジスライダーを追加可能。レンジセレクターももちろん追加可能。

pxでレンジスライダー・レンジセレクター


最後にpxでレンジスライダーとレンジセレクターを追加する方法を解説する。

と言っても特別なことはせず、figを作成した後にfig.update_layoutxaxisrangeslider, rangeselectorを設定するのみ。

今回はpx.data.stocksで株価のデータを取得、Google(GOOG)からMicrosoft(MSFT)のデータをグラフ化。pxについては pxのカテゴリーから色んな記事を閲覧可能。

# pxでレンジスライダーとレンジセレクター

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:6], markers=True)

# レンジセレクターとレンジスライダーを追加
fig.update_layout(
    xaxis=dict(
        rangeselector=dict(
            buttons=list([
                dict(count=1,
                     label='1ヶ月',
                     step='month',
                     stepmode='backward'),
                dict(count=6,
                     label='半年',
                     step='month',
                     stepmode='backward'),
                dict(count=1,
                     label='year-to-date',
                     step='year',
                     stepmode='todate'),
                dict(count=1,
                     label='1年',
                     step='year',
                     stepmode='backward'),
                dict(step='all')
            ])
        ),
        rangeslider=dict(
            visible=True
        ),
        type='date'
    )
)
# グラフ全体とホバーのフォントサイズ変更
fig.update_layout(font_size=20, hoverlabel_font_size=20)
fig.show()

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

レンジスライダー・セレクターで俯瞰と拡大

ということで、今回はplotlyのrangesliderとrangeselectorの解説だった。どちらも自分で範囲を絞れば手動で実現可能だけど、レンジスライダーがあると全体を俯瞰しながら局所的に確認可能。レンジセレクターがあるといちいち自分で範囲を狭める必要がない。

レンジスライダーやレンジセレクターを使いこなせば、横軸の範囲指定に無駄な労力を割かずに済む。なのでグラフの中身に集中できる。

今回の記事をきっかけによりplotlyのレンジスライダー・レンジセレクター、ひいてはレイアウトへの理解を深めていただけると幸いだ。

関連記事

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

続きを見る

【plotly&ボタン】plotlyのupdatemenusに2回押し対応のbuttonsを追加

続きを見る

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

続きを見る

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

続きを見る

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

続きを見る

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

続きを見る

【plotly&add_vrect, hrect】グラフに垂直・水平の塗りつぶし

続きを見る

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

続きを見る

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

続きを見る

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

続きを見る

【plotly&heatmap】go.Heatmapで2次元配列をマップ化

続きを見る

ガジェット

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全般
-, ,