StreamlitでPyGWalkerを動かす

2連続のPyGWalker関係の話です。そして、相変わらずStreamlit関係の記事です。

発展著しい両ライブラリですが、StreamlitにPyGWalkerを埋め込んで動かすこともできます。そして、使用感としてはJupyterよりStreamlitに埋め込んだ方が使い勝手がいいですね。

使用するメソッドですが、streamlitがPyGWalkerを埋め込むメソッドを持っているわけではなく、PyGWalker側がStreamlit上で動作するメソッドを持っているので注意が必要です。Streamlit側のドキュメントを読み込んでも本機能についての記述は出てきません。(少なくとも今日時点では。)

こちらを読みます。
参考: PyGWalkerとStreamlitを使ったデータの探索と情報共有 – Kanaries

StreamlitRenderer というのを使えば良いのですね。そして、設定を保存するspec引数もあります。

1点、Jupyterで動かす場合との違いなのですがセキュリティ上の理由なのかわかりませんがデフォルトではspecで指定したjsonファイルへの書き込み、要するに保存ができません。すでにどこかで保存されたダッシュボードの読み込みだけが可能という挙動になります。

これは、spec_io_mode 引数がデフォルトで”r” (読み込みモード) になっているためです。Streamlit上で作ったビューをそのまま保存したい場合は、”rw” を指定する必要があります。

注意点はこれだけなので、早速やってみましょう。データは何でもいいのでまたワインです。

from pygwalker.api.streamlit import StreamlitRenderer
import pandas as pd
import streamlit as st
from sklearn.datasets import load_wine

st.set_page_config(layout="wide")

# データ読み込み
wine = load_wine()
# 特徴量名を列名としてDataFrame作成
df = pd.DataFrame(
    wine.data,
    columns=wine.feature_names,
)

# target列も作成する。
df["target"] = wine.target
df["class"] = df["target"].apply(lambda x: wine["target_names"][x])

pyg_app = StreamlitRenderer(
    df,
    spec="./st_config.json",
    spec_io_mode="rw"
)
pyg_app.explorer()

一番最後の、explorer()を忘れないように注意してくださいね。

これでStreamlit上でもTableau風のUIでグラフを描けるようになりました。

PyGWalkerのダッシュボード設定を保存する

Tableau public でローカルファイルセーブが実装されたのでやや存在感が薄れているのですが、TableauライクなダッシュボードをPythonで作れるPyGWalkerの記事2本目です。

前回書いたのがこのライブラリが登場した直後だったので、当時は今と比べるとまだ基本的な機能も揃っていなかったのですが、現時点では待望のダッシュボードの保存機能が実装されているのでその紹介です。

参考: PyGWalkerでデータフレームを可視化してみる

これ、使い方はすごく簡単で、walk メソッドで起動する時に、jsonファイルのパスをspec引数へ渡し、ダッシュボードを作ったら保存ボタンを押すだけです。(自動保存は今日時点ではサポートされていないらしい。

ReadMe にも記載がありますね。

前回の記事と同じようにワインのデータでやってみましょう。

import pandas as pd
from sklearn.datasets import load_wine
import pygwalker as pyg


# データ読み込み
wine = load_wine()
# 特徴量名を列名としてDataFrame作成
df = pd.DataFrame(
    wine.data,
    columns=wine.feature_names,
)

# target列も作成する。
df["target"] = wine.target
df["class"] = df["target"].apply(lambda x: wine["target_names"][x])

walker = pyg.walk(df, spec="./config.json"). # 設定の保存先をspecで指定 

こうすると、spec で指定したファイルが存在しなければ自動的に作成されます。そしてそこに設定が保存されます。起動時点で、specで指定したファイルが存在していたらそれが読み込まれて前回の続きから作業ができます。

繰り返しですが、「保存」そのものは自動ではやってくれないので、Saveのアイコンを確実に押しましょう。かなりわかりにくいですがこいつです。

Saveの文字はマウオーバーして出てきてくだけなので、その下の歯車付きテキストファイルのようなアイコンを探してください。

st.set_page_configでStreamlitのページの基本的な設定を行う

今回紹介するのは、st.set_page_config という設定コマンドです。

白状しておくと、僕は基本的に st.set_page_config(layout = “wide”) しか使っていません。(ほぼ確実に使うのになかなかこの文を覚えなくて何度も調べているので記事にしました。)

ドキュメントはこちらです。
参考: st.set_page_config – Streamlit Docs

Noteとして、 “This must be the first Streamlit command used on an app page, and must only be set once per page.” と書いてある通り、このメソッドは最初に1回だけ呼び出す必要があります。

これを使うと、デフォルトで中央寄席になっているレイアウトを画面全体に広げたり、ページタイトル(デフォルトではファイル名)を設定したり、ページアイコンを設定したりできます。

引数はドキュメントにある通りで以下の通りです。

page_title : ページのタイトルを文字列で指定。
page_icon: 絵文字や絵文字コードを用いてアイコンを指定できる。”random”も可能。
絵文字コードの一覧はこちら
layout: ページのレイアウト。”centered” or “wide” の2種類。 centered がデフォルトなので、使う場合は基本的には “wide”の方を使用することになります。
initial_sidebar_state: サイドバーの初期状態。”auto”/ “expanded”/ “collapsed” から指定できる。基本的に、デバイスサイズで判断して動いてくれるデフォルトの”auto”で良いと思いますが、必ず表示したい/隠したいという場合は残り二つも選択肢になると思います。
menu_items: 右上に表示するメニューの設定です。

menu_itemsは指定する場合は次のように辞書で指定します。
下記のサンプルはドキュメントからそのまま持ってきました。

import streamlit as st

st.set_page_config(
    menu_items={
        'Get Help': 'https://www.extremelycoolapp.com/help',
        'Report a bug': "https://www.extremelycoolapp.com/bug",
        'About': "# This is a header. This is an *extremely* cool app!"
    }
)

“Get Help” / “Report a bug” はそれぞれURLを指定するとリンクになります。Noneの場合はメニューにこれらの項目自体がなくなります。

“About” はマークダウンの文字列で、ここに書いた内容がそのまま表示されるようになります。指定しない場合はStreamlitのデフォルトのテキストです。

Streamlitで複数ページのアプリを作る

今回はStreamlitで複数ページのアプリを作る方法を紹介します。この記事で紹介している方法を使うと、複数のファイルに分けてStreamlitのアプリを作れるのでコードの管理も簡単になります。

一応書いておくと、サイドバーに選択式のウィジェットを配置して、その選択によって表示する項目を切り分ける実装にするとこの記事に書いてる方法を使わなくても複数ページっぽく振る舞うアプリを作ることはできます。ただ、コードが長くなることや名前空間を共有してしまう不便さ等もあるので別ページのものは別ページで作った方が良いことが多いです。(逆に1ファイルにまとめた方が便利なこともありますが。)

複数ページのアプリを作る場合、最初にこちらのページからリンクされている各ページを参照してStreamlitの方針を把握しておくことをお勧めします。
参考: Multipage apps – Streamlit Docs

では具体的な方法を見ていきましょう。といっても、ほとんどのケースで一番最初のpagesディレクトリを使う方法で十分だと思います。

pagesディレクトリを使用する方法

これは Streamlitのメインページとなる.py ファイルと同じディレクトリに pages という名前でディレクトリを作り、その下に .py ファイルを作るというものです。

ドキュメントでは以下の構成が例示されていますね。

your_working_directory/
├── pages/
│   ├── a_page.py
│   └── another_page.py
└── your_homepage.py

この通りに作って動かして見ましょう。ファイルの中身は全部からっぽです。

% mkdir your_working_directory
% cd your_working_directory
% mkdir pages
% touch your_homepage.py
% touch pages/a_page.py
% touch pages/another_page.py
% streamlit run your_homepage.py

するとこのような画面になります。

実際に動かしていただくと確認できるのですが、それぞれURLが、
– http://localhost:8501/
– http://localhost:8501/a_page
– http://localhost:8501/another_page
となります。メインのファイルがルートパスになる感じですね。URL中には/pages/の文字は含まれません。

ファイル名と、上記の画面のサイドバーに表示されるページ名を見比べていただけると分かるのですが、 _ がスペースに変換されています。これは _ や – がセパレーターとして半角スペースに変換されるからです。また、拡張子 .py も取り除かれます。

また、ファイル名の先頭に正の数字をつけていた場合、それも取り除かれます。項目の並び順の指定には使えるのでうまく活用しましょう。

st.Pageとst.navigationを使う方法

もう一つの方法は、専用のメソッド、st.Pageとst.navigationを使って明示的に複数ページを定義することです。

この方法であれば、端末内の別の場所に置いてあるファイルを呼び出すこともできますし、分岐等を用いて、リンクを配置するページを出し分けることもできます。

具体的な方法は次のようになります。

# ファイル構成
your-repository/
├── page_1.py
├── page_2.py
└── streamlit_app.py

# streamlit_app.pyの中身
import streamlit as st

pg = st.navigation([st.Page("page_1.py"), st.Page("page_2.py")])
pg.run()

st.Page(“ファイル名”)でファイルを読み込んでページオブジェクトを作り、それをst.navigation に渡して、pg.run() しています。run()は忘れがちなので注意しましょう。

正直、pagesディレクトリを使う方がずっと単純なのですが、大規模になってくるとst.navigation を使う場面も出てくると思うので頭の片隅にでもおいておいてください。