NIKKEI TECHNOLOGY AND CAREER

Streamlit と Cloud Run でデータ可視化ダッシュボードを爆速デプロイする

この記事はNikkei Advent Calendar 2022の 15 日目の記事です。

こんにちは、データ報道グループの並木です。業務では主にデータジャーナリズムと呼ばれるデータの分析や可視化に根ざした記事の執筆にあたっています。最近趣味でクラウドサービスの勉強をしており、今回は休日に取り組んだダッシュボード開発の内容を解説します。

ダッシュボードとは、様々なデータソースから集約した情報をグラフや表などを用いて一覧性が高い形式で可視化するツールのことを指します。データジャーナリストやデータサイエンティストの業務では、データの特徴を掴むにあたってデータ可視化におけるダッシュボードの活用は重要です。ビジネスインテリジェンス(BI)ツールでも、しばしば企業の持つデータを可視化して経営上の意思決定に役立てることを狙いとしてダッシュボード機能が提供されています。

いずれの場合も、ダッシュボード基盤の構築自体は本質ではなく、その先にある報道上や経営上や「価値の発見」により多くの工数を割かれるべきです。 BI ツールに注目が集まる所以もそこにありますが、ノーコードであるほどデータ形式や可視化の自由度が低いという難点があります。とはいえ、データの規模が大きくない場合やダッシュボードを継続的に使用しない場合にしっかりとしたフロントエンドのコーディングやデータベースの構築に工数を割くことは最適解とはいえません。

その解決策として、ダッシュボードを工数少なくコーディング可能な Python のライブラリ「Streamlit」と、シンプルなコンテナ型実行環境である Google Cloud Platform(GCP) の「Cloud Run」を組み合わせることで、可視化の自由度を高めつつ圧倒的低工数でダッシュボードサービスをデプロイする手法をご紹介したいと思います。ソースコードはGitHubで公開しています。

Streamlitとは

Streamlit 社が開発する Python のみでコーディング可能なデータ可視化に特化した Web フレームワークです。 2019 年に初公開され、現在まで頻繁に更新が続いています。

A faster way to build and share data apps」とスローガンを掲げている通り、非常に少ない工数でデータ可視化を行い、ウェブアプリとして共有することができます。公式の動画のように、簡単なデータのインタラクティブな可視化であれば 10 行程度で完成してしまいます。大部分のコーディングも基礎的な Python の知識があれば理解可能なので、Web 系以外のエンジニアやプログラミング初心者にもとっつきやすいライブラリです。

ローカル環境でのホスティングも streamlit run MyApp.py とコマンドを 1 行実行するだけです。後述する Cloud Run などのサービスを活用することで、外部公開も非常に手早く行うことができます。

ライブラリの更新が非常にアクティブな点も特徴的です。公式のChangelogを見ると、 2-3 週間で新しいバージョンが公開されることが多く、執筆時点での最新バージョンは 2022 年 12 月 1 日に公開された Version 1.15.2 でした。

出所:Streamlit

Cloud Runとは

Cloud Run はサーバーレスで提供される GCP のサービスで、フルマネージドなコンテナ実行環境です。

Cloud Run はリクエストがないときにインスタンス数が 0 になるまでスケールインできる点が大きな特徴です。リクエストの有無にかかわらずインスタンスの起動時間に応じて従量課金される Compute Engine や App Engine との大きな違いです。今回の記事では Streamlit で構築した可視化ダッシュボードのアクセス頻度があまり高くないケース(利用者が限られた社内ツールなど)を想定しています。Cloud Run を使用すれば、リクエストがない時間の分だけコストパフォーマンスが良くなるでしょう。

また、Cloud Run がサーバーレスなコンテナの個別実行環境である点もシンプルな SPA(Single Page Application) の Streamlit をデプロイするにあたって適しているポイントです。

App EngineCloud RunCompute Engine
概要サーバーレスアプリケーション実行環境サーバーレスコンテナ実行環境仮想マシン環境
課金インスタンスの従量課金制(最後のリクエストの 15 分後まで)利用時間に応じる( 100ms 単位)インスタンスの従量課金制
サービス体系PaaS≒ CaaSIaaS

ダッシュボードを作ってみる

それでは実際にダッシュボードを作ってから一般公開するところまでタイムアタックで挑戦してみようと思います。

データを探す

ダッシュボード構築の第一歩として、まず可視化するデータを探すことから始まります。気候変動が自分の関心のあるテーマの一つなので、ここでは気候変動に関わるデータを調べてみます。サンプルのダッシュボードということで今回はオープンデータを対象にデータを探します。個人的にはデータを探す際に以下の 5 点に気を配ります。

  • データの情報源の信頼性
  • データの最終更新時期
  • データの更新間隔
  • データの機械判読性(PDF など処理がしにくいフォーマットを避ける)
  • データのライセンス(二次利用が可能か、クレジット表記が必要かなど)

今回は、統計データのポータルサイト「Our World in Data(OWID)」を利用してデータを探します。一般に英語圏の方がオープンデータが豊富で、機械判読性が高いデータが見つかりやすい傾向にあります。 OWID は米国のオックスフォード大学の研究者を中心に運営されており、環境問題や貧困、社会的不平等など地球規模の社会問題に関わる統計データが公開されています。

OWID が公開するデータや記事、グラフは全て Creative Commons BY(CC BY) の下で公開されており、著者と出典を明記した上で有償無償問わずに自由にデータを改変・利用・再配布することができます。一方サードパーティのデータは外部サイトのライセンスに準ずる必要があります。

Our World in Dataのトップページ 出所:Our World in Data

さて「greenhouse gas」などのキーワードで検索すると、CO₂ Data Explorerというページが見つかりました。温室効果ガスの排出量の時系列データが国や地域別に公開されています。データの網羅性も高く、良さそうですね。ここまで約 3 分です。

CO₂ Data Explorer 出所:CO₂ Data Explorer

データの詳細や情報源を読み込んでみます。画像左上からバックデータのリポジトリへアクセスできます。データソースの記載もリポジトリのファイルに記載されています。今回使用するデータの外部ソースは以下の 3 つでした。

どのデータも OWID 同様に CC BY ライセンスで公開されています。また十分に信頼に足る公的機関や組織、研究者によって公開されていることも確認できました。ここまで約 20 分です。

定期的にアップデートされるデータということで、今回は GitHub 上のリンクからデータを読み込むことにします。アプリの読み込み速度を優先するためにデータをローカルにダウンロードすることもできます。 Streamlit では pandas などを使いインターネット上のデータを読み込んだり、 BigQuery などからデータを呼び出したりすることもできるので、可視化したいデータは必ずしもローカルに保存されている必要はありません。

Streamlitのコードを書く

CO₂ Data Explorerを見てみると以下がダッシュボードの要件のようです。

  • 全グラフ共通
    • 温室効果ガスの種類、値の算出方法(、二酸化炭素の場合は排出のソース)、単位をそれぞれ選択できる
    • 値が実数のとき、世界全体の値に対する相対値を表示できる
  • 時系列の折れ線グラフ
    • 可視化する国を複数選択できる
    • Y 軸を線形・対数から選択できる
  • 地図上の階級区分図
    • ある年の値を指定して可視化できる
    • 可視化範囲を全世界・各大陸から選択できる
    • 2 つの年の数値を表形式で表示できる
    • 値が存在しない行は最も近い年の値を表示できる
    • 2 つの年の間の絶対変化と相対変化の数値を表示できる
    • 各列の値でソートできる

今回は上のうち太字の要件を実装してみます。実装に使えそうな Streamlit の機能を一部ピックアップしてみます。詳細は公式のドキュメントチートシートを参照ください。

st.tabs

Version 1.11.0 ( 2022 年 7 月 14 日公開)で初実装された機能なので Streamlit ユーザーの方でも知らない人もいるのではないでしょうか。 st.tabs でタブ数だけのオブジェクトを返し、各オブジェクトを with 句で囲むことで、それぞれのチャートや文章のブロックをタブ化することができます。

tab1, tab2, tab3 = st.tabs(["CHART", "MAP", "TABLE"])

# 折れ線グラフ
with tab1:
    """
    グラフ描画のコード
    """

# 階級区分図
with tab2:
    """
    グラフ描画のコード
    """

# テーブル
with tab3:
    """
    グラフ描画のコード
    """
Untitled

st.plotly_chart

Python でコーディングできる代表的なチャート描画ライブラリの一つ「Plotly」のチャートオブジェクトを読み込みます。st.plotly_chart(fig) のように 1 行書くだけで、 Streamlit 上に Plotly のグラフが描画されます。

fig = px.line(
    df[df[column_name].notnull() & df["country"].isin(plot_countries)],
    x="year",
    y=column_name,
    color="country",
    title=title_name,
    template="simple_white",
    log_y=is_logplot,
)
st.plotly_chart(fig, use_container_width=True)
Untitled

st.download_button

ダウンロードボタンを表示する機能です。例で示しているデータフレームの csv 出力のほか、画像やバイナリデータの出力も可能です。

df_export = (
    df_plot[["year", "iso_code", "country", column_name]]
    .to_csv(index=False)
    .encode("utf-8-sig")
)
st.download_button(
    label="Download data as CSV",
    data=df_export,
    file_name=f"{title_name}.csv",
    mime="text/csv",
)

st.selectbox

ユーザーがリストから一つの要素を選択する機能です。 2 つ目の引数に入力されたリストのうち、ユーザーが選択した値が変数として代入されます。可視化する値の選択や離散的なパラメタの選択に使われる事が多いです。

gas_type = st.selectbox("ガスの種類を選択", ["二酸化炭素", "全ての温室効果ガス", "メタン", "亜酸化窒素"])
Untitled

st.multiselect

ユーザーがリストから任意の数の要素を選択する機能です。 2 つ目の引数に入力されたリストのうち、ユーザーが選択した値がリストの変数として代入されます。可視化する値の選択に使われる事が多いです。

plot_countries = st.multiselect(
    "国と地域を選択",
    countries,
    ["World", "Japan", "United States", "China", "Russia", "France"],
)
Untitled

st.cache

インメモリの KVS(key-value store)によるキャッシングで動作速度を改善します。入力された引数のハッシュをキー、関数の実行結果をバリューとするキャッシュが用意され、入力された引数のハッシュがヒットした場合には関数を実行せずにバリューを返すので動作が高速化されます。

@st.cache
def load_file():
    # source repository https://github.com/owid/co2-data
    df = pd.read_csv(
        "https://raw.githubusercontent.com/owid/co2-data/master/owid-co2-data.csv"
    )
    df_column_meta = pd.read_csv("column_metadata.csv")
    return df, df_column_meta


df, df_column_meta = load_file()

これらの機能を使ってダッシュボードの要件を再現しました。また、ローカルでの動作も確認できました。コードはGitHubで公開しています。

前半の列定義の整理に手間取りましたが、コード自体は 100 行程度で済みました。データの列定義の整理や前処理、コーディングにそれぞれ約 30 分、約 45 分要し、ここまで約 1 時間 35 分です。

Untitled

Cloud Runでデプロイ

今回は個人用の Google アカウントに紐付いた GCP 環境でダッシュボードをデプロイしました。 Google Cloud Shell のエディタを開き、ローカルで作成したコードとデータをアップロードします。この後の操作はすべて Cloud Shell 上で行います。

スクリーンショット 2022-08-16 2.34.48.png

次に必要なライブラリを記載した requirements.txt を追加します。

streamlit==1.11.0
pandas==1.2.4
plotly==5.3.1

今回は Buildpacks を使うので Procfile を作成し 1 行だけ追加します。 Cloud Run がコンテナに追加する環境変数の $PORT を Streamlit が起動するポート番号として指定しています。こちらが公式のドキュメントの参考ページです。

web: streamlit run covid.py --server.port $PORT

ここまでの内容をまとめると、ファイルの構成は以下のようになっています。

greenhousegas-dashboard
├—app.py
├—requirements.txt
├—Procfile
├—column-metadata.csv(データ)

ファイルが揃ったところでデプロイします。デプロイも 1 行で完了します。 --source 引数でデプロイ対象のファイルを指定することで、ソースコードの Cloud Storage へのアップロード、 BuildPacks によるランタイム検出とイメージ作成、デプロイを一括して行ってくれます!(非常に手軽!)

gcloud run deploy greenhousegas-dashboard --source . --timeout=3600

デプロイ先のリージョンを指定し 5 分ほど待つとデプロイが完了し、デプロイ先の URL が出力されます。 Untitled 無事にダッシュボードがデプロイされていることが確認できました。 ここまで約 8 分半でした。なんと 10 分もかからずにローカルで作成したダッシュボードのデプロイに成功しました。開発全体も 1 時間半強で完了しました。 Untitled Dockerfile を記述してデプロイすることももちろん可能です。

FROM python:3.9.5

RUN pip install --upgrade pip
COPY requirements.txt .
RUN pip install --no-cache-dir -r  requirements.txt

EXPOSE 8080

WORKDIR /app
COPY app.py .
COPY column_metadata.csv .
CMD streamlit run app.py\
    --browser.serverAddress="0.0.0.0" \
    --server.port="8080"

ファイルの構成は以下のようになっています。 Procfile の代わりに Dockerfile が設定ファイルとなっています。

greenhousegas-dashboard
├—app.py
├—requirements.txt
├—Dockerfile
├—column-metadata.csv(データ)

gcloud builds コマンドでコンテナを作成し Container Registry にデプロイした後、gcloud run でコンテナイメージを Cloud Run にデプロイします。

gcloud builds submit --tag asia.gcr.io/${PROJECT-ID}/greenhousegas-dashboard:v
gcloud run deploy greenhousegas-dashboard --image asia.gcr.io/${PROJECT-ID}/greenhousegas-dashboard:v1

まとめ

今回は Cloud Run を活用し、約 1 時間半で Streamlit で構築したオープンデータの可視化ダッシュボードをデプロイすることができました。ダッシュボードの構築やデプロイといった工程の工数を最小化することが、データジャーナリストやデータサイエンティストの本分たる「データに潜む価値を発見する」ことにしっかり工数を割くことに繋がります。特にクラウドサービスを活用した自動化は一つの鍵になりそうです。自分も 9 月に自己研鑽の一環として GCP の Associate Cloud Engineer 資格を取得しました。

他にも Streamlit ではPyDeckと呼ばれる地理情報の可視化ライブラリをサポートしているため、 3D 表現など地理空間情報のリッチな可視化もわずかな工数で可能です。画像のアップロード機能などを活用し、機械学習モデルの学習や予測プロセスの効率化や実演にも有用となります。また、BigQuery や AWS S3 などのデータソースと接続することで、 BI ツールより自由度が高い組織内のビッグデータの可視化・分析ツールとしての活用も期待できます。

Untitled 出所:Streamlit

このようなアプリの設計やデータパイプラインの構築、可視化などデータにまつわる知見共有・技術交流をニュースルームとデジタル部署の垣根を越えて行っていくことが新しい時代のジャーナリズムにとって重要だと考えます。日本経済新聞社はデータジャーナリズムを強化しており、インターンシップではデータジャーナリズムを体験できるプログラムを定期的に開催しています。

明日のアドベントカレンダーはデータサイエンティストの増田さん、西川さんによる「ベイジアン A/B テストの利点と実験計画策定に関する一検討」です。お楽しみに!

並木亮
JOURNALIST並木亮

Entry

各種エントリーはこちらから

キャリア採用
Entry
新卒採用
Entry
カジュアル面談
Entry