カテゴリー

当サイトはアフィリエイトプログラムによる収益を得ています〈景品表示法に基づく表記です)

go

【plotly&table+scatter】subplotsで表と散布図を同時にグラフ化

2021年9月6日

こんな人にオススメ

plotlyを使用して1つのグラフに表とプロットを表示したい。どうしたらいい?

あと、ボタンも追加してボタンに対応するプロットと表を強調したいんだけど、そんなことできる?

ということで、今回はplotyを使用して表とグラフを同時に描く方法を解説する。イメージは画像上に表を、下にグラフを描く感じ。こうすることで、データを見ながらグラフを確認できる。

今まではExcelとかで確認した生データとブラウザで確認したplotlyのグラフを一括で閲覧できるようになるとかなり便利になると思う。ぜひ習得指定いただきたい。

python環境は以下。

  • Python 3.9.6
  • matplotlib 3.4.2
  • pandas 1.3.1
  • plotly 5.1.0
  • plotly-orca 3.4.2

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

運営者メガネ

作成したコード全文

下準備

import sys
import matplotlib
import pandas as pd
import plotly
import plotly.graph_objects as go
import plotly.io as pio
from plotly.subplots import make_subplots

sys.path.append('../../')
import plotly_layout_template as template

まずは下準備としてのimport関連。plotly_layout_templateは自作のplotlyテンプレート。これを使用することで簡単にキレイなグラフを作成可能。

使用するデータ

df = pd.read_csv('./data.csv', index_col=0)
print(df)
#         red  blue  green  orange  violet
# Number
# 1        10     8      8       0       3
# 2         6     5      3       9       3
# 3         4     6      5       3       4
# 4        10     2      6       2       7
# 5         3     1      5       9       2
# 6         5     1      2       8       0
# 7         9     0      6       3       3
# 8         0     7      0       5       1
# 9         0    10      1       0       9
# 10        3     8      2       5       1
# 11        6     8      7       4       7
# 12       10     5      7       0       1
# 13        0     7      2       0       6
# 14        6     3      4       1       4
# 15        6     6     10       4      10
# 16        2    10      1       0       7
# 17        9     1      4       6      10
# 18        5     0      4       6       0

今回は表を使用するということで、表のデータを作成した。値は乱数で生成した。まあ何だっていい。インデックスはデータ数を、ヘッダはデータ名を表している。

後々に楽をするためにヘッダ名は色の名称にしている。

サブプロットの定義用の関数

# subplot作成用
def set_subplots(rows, cols, specs, shared_xaxes=False, shared_yaxes=False):
    fig = make_subplots(
        rows=rows, cols=cols,  # 行数と列数
        shared_xaxes=shared_xaxes,  # 横軸の値を共有するか否か
        shared_yaxes=shared_yaxes,  # 縦軸の値を共有するか否か
        vertical_spacing=0,  # 表とグラフの間の間隔を0に
        specs=specs,  # subplotするグラフの配置順やグラフの種類
        row_heights=[0.7, 0.45]  # 表とグラフの高さを設定
    )

    return fig

表とプロットを1つのグラフに描くということで サブプロットを使用する。plotlyのサブプロットに関しては以下の記事で解説しているので参照。

【plotly&make_subplots】pythonのplotlyで複数グラフを1つの画像に描く

続きを見る

引数は以下の通り。

  • rows: サブプロットで作成するグラフの行数
  • cols: サブプロットで作成するグラフの列数
  • specs: サブプロットのどこにどんなグラフを配置するのか
  • shared_xaxes, shared_yaxes: サブプロットで横軸・縦軸を共有するか否か

その他、定義しているvertical_spacingrow_heightsについてはそれぞれの値がちょうどいい感じに配置できたから設定している。返り値のfigplotlyのグラフとなる。

表作成用の関数

# 表作成用
def get_table(h_values, c_values, line_color='silver',
              h_fill_color='lightgray', c_fill_color='whitesmoke',):

    # フォントの色はヘッダの色に設定。Numberは黒に設定
    font_color = ['black'] + list(df.columns.values)
    table = go.Table(
        header=dict(
            values=h_values,  # ヘッダの値
            align="left",  # ヘッダの並び方を左寄せに
            line_color=line_color,  # ヘッダの枠線の色
            fill_color=h_fill_color,  # ヘッダの塗りつぶしの色
            font_color=font_color,  # ヘッダのフォントの色
        ),
        cells=dict(
            values=c_values,  # 各セルの値
            align="left",  # 各セルの並び方を左寄せに
            height=30,  # セルの高さ
            line_color=line_color,  # セルの枠線の色
            fill_color=c_fill_color,  # セルの塗りつぶしの色
            font_color=font_color,  # セルのフォントの色
        ),
    )

    return table

plotlyで表を作成するにはgo.Tableを使用すればいい。go.Tableではヘッダであるheader引数と各値であるcells引数という2種類の引数を駆使して設定を行う。引数は以下。

  • h_values: ヘッダの値
  • c_values: 各セルの値
  • line_color: 表の枠線の色
  • h_fill_color: ヘッダの塗りつぶし色
  • c_fil_color; セルの塗りつぶしの色

基本はh_valuesに1次元配列を、c_valuesに2次元配列を入れて値を埋める。なお、自作テンプレートでフォントサイズやフォント自体を変更したらセルに収まらなかったのでheight=30で調節した。

グラフ保存用の関数

# グラフ保存用の関数
def save(fig, config, save_name):
    pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
    pio.write_html(fig, f"{save_name}.html", config=config,)
    pio.write_image(fig, f"{save_name}.png")

グラフ保存用の関数。htmlとpngの形式で出力する。htmlで出力することで保存後も編集やグリグリ動かすことが可能。configはグラフを拡大したり図形を追加したりするためのツールバー的なもの。

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

続きを見る

上に表、下にグラフのsubplots


まずはシンプルに上に表を、下にグラフを描いてみる。なお、表のフォントの色をそれぞれのヘッダの色にしておいた。ヘッダ名が色なのはこのためだと言っても過言ではない。伏線だ。

なお、データ数を示すNumberは黒色で表現しておいた。

2行1列で作成

def table_plot(rows, cols, specs, plot_col, save_name,):

    # subplotの作成
    fig = set_subplots(
        rows=rows, cols=cols, specs=specs,
        shared_xaxes=True,  # 横軸の値は共有
    )

    # 表の作成
    table = get_table(
        h_values=df.reset_index().columns,  # ヘッダの値
        c_values=df.reset_index().values.T,  # 各セルの値
    )
    fig.append_trace(table, 1, 1)  # 1行目、1列目に配置

    # プロットの作成
    for num, color in enumerate(df):
        d = go.Scatter(x=df.index, y=df[color], name=color, line_color=color,)
        fig.append_trace(d, 2, plot_col(num))  # グラフの列番号は関数で指定

    # レイアウトの作成
    fig.update_layout(
        template=template.plotly_layout(),
        xaxis_title='Number',
        legend=dict(y=0, xanchor='left', yanchor='bottom'),
    )

    # グラフの表示と保存
    config = template.plotly_config()
    fig.show(config=config)

    save(fig=fig, config=config, save_name=save_name)

グラフを作成する手順は以下。

  1. サブプロットの枠組みを作成
  2. 表を作成(df.reset_index()でインデックスを一時的に消した)
  3. プロットを作成
  4. レイアウトを作成
  5. グラフの表示と保存

変更することが必要な項目は引数に入れておいた。plotを作成時のサブプロットのどこに入れるかを決めるfig.append_traceで使用する引数のplot_colは、列をどうするかを決める関数とした。

2行1列の本章初めのグラフなら1でいいんだけど、次で示すような2行2列のグラフにしたい時は関数にして、引数で列数をある法則に則って決められる方が自由度が高い。面倒だけど。

実際に使用する際には以下のように、1列であることを示すための関数が必要となるのが面倒なところ。

# シンプルに1を出力する関数
def simple(num):
    return 1

# 2行1列のsubplotで、上に表を、下にグラフを表示
table_plot(
    rows=2, cols=1,  # 2行1列で区分け
    specs=[
        [{"type": "table"}],  # 上の行に表
        [{"type": "scatter"}]  # 下の行にグラフ
    ],
    plot_col=simple,  # ここで列の情報を入れる
    save_name='table_plot11'
)

ここでは2行1列のグラフを作成するので、specも2行1列のlistとした。なお、1行目が表なのでtypetableにし、2行目は散布図なのでtypescatterにする必要がある。

plot_colは常に1を出力するsimple関数を使用。これで常に1列目にプロットデータが割り当てられることになる。これでできるグラフが本章初めのグラフ。

2行2列でグラフを分ける


一方で、2行2列のグラフにしようとするなら工夫が必要。上のグラフの場合はヘッダが奇数番目は1列目、偶数番目は2列目のプロット領域でプロットされるようにした。

そのためにeven_odd関数を定義し、2の余りの数で奇数と偶数を判別した。

# 偶数は1、奇数は2を出力する関数
def even_odd(num):
    return num % 2 + 1

# 2行2列のsubplotで、上に表を、下に左右でグラフを表示
table_plot(
    rows=2, cols=2,  # 2行2列で区分け
    specs=[
        [{"type": "table", "colspan": 2}, None],  # 上の行に表は2列分の表
        [{"type": "scatter"}, {"type": "scatter"}]  # 下の行には2列分でグラフ
    ],
    plot_col=even_odd,  # 偶数と奇数で描画列を変える
    save_name='table_plot12',
)

また、今回は1行目が2列分使用するということなので、spectableには新たにcolspan2に指定。この表で占領される1行目2列目はNoneでなしにした。

ボタンで表とプロットを強調


最後に、ボタンを使用して表とプロットを連携するグラフを作成。上のグラフだと、各ボタンを押すとそのボタンに応じて色が薄くなったり戻ったりする。

こんなグラフを使用することで、データをより強調して見せることが可能となる。

ボタン作成用の関数

# ボタン作成
def get_button(label, color):
    button = dict(
        label=label,
        method='update',
        args=[
            {
                'header.font.color': [color],  # ヘッダのフォントカラー
                'cells.font.color': [color],  # セルのフォントカラー
                'line.color': color,  # プロットのカラー
            },
            {},
        ],
    )
    return button

今回はボタンを作成するということで、予めボタンを設定するための関数を作成。今回は表のヘッダ、セル、そしてプロット点の透明度を変更したいので、argsの部分にはこれらを記述。

header.font.colorがヘッダのフォントの色を、cells.font.colorがセルのフォントの色を、そしてline.colorがプロットの色を司る。ボタンについては以下参照。

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

続きを見る

透明度を変更したいが、それ専用の引数が存在しないので、色を再度設定する際に同時に透明度も変更する。なお、表に関する色ではlistを被せないと全セルで黒文字になる。

2行1列でボタン付きの表とグラフ

# 2行1列のボタン付きグラフの作成
def table_plot_buttons(rows, cols, specs, plot_col, save_name,):

    fig = set_subplots(
        rows=rows, cols=cols, specs=specs,
        shared_xaxes=True,  # 横軸の値は共有
    )

    table = get_table(
        h_values=df.reset_index().columns,  # ヘッダの値
        c_values=df.reset_index().values.T,  # 各セルの値
    )
    fig.append_trace(table, 1, 1)  # 1行目、1列目に配置

    for num, color in enumerate(df):
        d = go.Scatter(x=df.index, y=df[color], name=color, line_color=color,)
        fig.append_trace(d, 2, plot_col(num))  # グラフの列番号は関数で指定

    # ------------------------------------------------------------
    # ボタンの内容を作成
    font_color = ['black'] + list(df.columns.values)

    # ボタン情報を入れる
    buttons = []

    # 全グラフ表示用ボタン
    button = get_button(label='all', color=font_color)
    buttons.append(button)

    #  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # 各プロット表示・非表示用のボタン
    for color in df:  # グラフプロットの色のみ
        rgbas = {}
        for color2 in font_color:  # グラフプロットの色に加えてNumberの黒も
            # 色のstrをrgbに変換
            rgb = matplotlib.colors.ColorConverter.to_rgb(color2)
            rgb = plotly.colors.convert_to_RGB_255(rgb)  # rgbを0-1から0-255へ

            # Numberかボタンに該当する色は透明度を1にする
            if color2 == 'black' or color2 == color:
                opacity = (1,)
            else:  # それ以外のプロットは透明度を0.1にする
                opacity = (0.1,)

            rgba = rgb + opacity
            rgbas[color2] = f"rgba({rgba})"

        colors = list(rgbas.values())
        button = get_button(label=color, color=colors)
        buttons.append(button)

    #  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # 作成したボタンをグラフ上に配置
    updatemenus = [
        dict(
            type='buttons', direction="right", active=0, buttons=buttons,
            x=0.5, y=1.01, xanchor='center', yanchor='bottom',
        )
    ]

    # ------------------------------------------------------------

    fig.update_layout(
        template=template.plotly_layout(),
        xaxis_title='Number',
        legend=dict(y=0, xanchor='left', yanchor='bottom'),
        updatemenus=updatemenus,  # ボタンをレイアウトに配置
    )

    config = template.plotly_config()
    fig.show(config=config)

    save(fig=fig, config=config, save_name=save_name)

ということで、最後にグラフを作成するためのコード。関数化できそうな部分もあるが、もはや直接記述した。初めの方は先ほどと同様、サブプロットと表とプロットを作成。

その後、ボタンを作成するコードに移るが、今回は全体表示用と各色用の2種類のボタンを用意。それぞれに対してボタンを作成した。

各色用のボタンでは、blackなどの色のstrをrgbに変換し、pltplotlyの間の値の変換(0-1から0-255へ)、そしてボタンに該当しないプロットの透明度を0.1にするという操作を実行。

最終的にrgba(255, 0, 0, 0.1)のようにrgbaで記述してボタンの設定とした。結構面倒な作業だったが、やってることは色の値の変換と該当ボタン以外の透明度を0.1にしたということ。

# 2行1列のsubplotで、上に表を、下にグラフを表示
table_plot_buttons(
    rows=2, cols=1,  # 2行1列で区分け
    specs=[
        [{"type": "table"}],  # 上の行に表
        [{"type": "scatter"}]  # 下の行にグラフ
    ],
    plot_col=simple,  # ここで列の情報を入れる
    save_name='table_plot_buttons'
)

上のコードのようにあとはプロットするだけで、本章初めのグラフを作成することができる。結構シンプル。

データを見ながらプロットを見る大切さ

今回はplotlysubplotsを使用して、上に表、下にプロットという複合グラフを作成する方法について解説した。実際にデータを見ながらプロットを確認できるのは結構楽ちん。

今まではアプリ間を行ったり来たりしたり、目線を移動させながらだったのが1グラフで済むから疲れにくいと考えられる。今回のグラフもプロットの非表示など応用が効くからいろいろ試してほしい。

関連記事

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

続きを見る

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

続きを見る

【plotly&バブルチャート】plotlyで各国の収入と平均寿命の時代変化をバブルチャートで描く

続きを見る

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

続きを見る

ガジェット

2023/11/11

【デスクツアー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/11/4

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

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

ベストバイ

2023/10/29

【ベストバイ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(31万円) x Galaxy Z Fold5(25万円)使いの狂人。自己紹介と半生→変わって楽しいの繰り返しレビュー依頼など→お問い合わせ運営者情報、TwitterX@m_ten_pa、 YouTube@megatenpa、 Threads@megatenpa、 Instagram@megatenpa

-go
-, ,