Streamlitで趣味用のWebアプリを作成してデプロイする

# はじめに
そこまで凝らないWebアプリならStreamlitで簡単に作成でき、無料でデプロイできます。
今回自分が作ったもの：https://formula1-web-app.streamlit.app/

# [Streamlit](https://streamlit.io/)とは
Streamlitはpythonだけで簡単にダッシュボードなどのアプリが作れるパッケージ。

`pip install streamlit`したら、下記のようなコードを作り、`streamlit run app.py`をするだけでWebページが立ち上がるという非常にお手軽なもの。

```
# app.py（ファイル名は任意）
import streamlit as st

st.write("簡単")
```
上記は、「簡単」という文字を出力するだけですが、色々な描画を実現するメソッドがあり、それらを組み合わせると簡単にある程度のものを作れます。
[Qiita上にもみなさんが記事を書かれていて](https://qiita.com/tags/streamlit)すぐに参考にできます。

# デプロイまでの手順
無料でWebアプリを公開して使えるようにする（＝urlでどこからでもアクセスして使う）なら、[Streamlit Cloud](https://streamlit.io/cloud)を使う下記の方法が多分一番簡単だと思います（詳細は後述）。
さもなければ、HerokuやAWSなどのクラウドサービスにお金を払ったりレンタルサーバーを借りたり、といったことをすることに。

## 1. Githubのアカウントを作成する
[Githubの公式ページ](https://github.com/)でSign upします。

開発用という意味だけでなく、[Streamlit Cloudでデプロイ](#4-streamlit-cloudでデプロイする)する際に、連携するので必要です。

## 2. Streamlitを使ったアプリを作る
Githubでアプリ用のリポジトリを作って、アプリを作ります。

Streamlit公式が、様々な人々が作ったアプリを[ギャラリー](https://streamlit.io/gallery)として出してくれているので、どのようなことがどのようなコードで実現できるのか参考にできます。

※自分は趣味のF1を題材に、過去の結果を見たり、レース戦略をシミュレーションできるアプリを作ってみました（[Github](https://github.com/YoshimatsuSaito/formula1-web-streamlit)、および、[アプリ](https://formula1-web-app.streamlit.app/)）。

## 3. Streamlitにアカウントを作る
[Streamlitの公式ページ](https://streamlit.io/)でSign upします。

今回は、[Streamlit Cloudでデプロイ](#4-streamlit-cloudでデプロイする)するので、アカウントを持っている必要があります。


## 4. Streamlit Cloudでデプロイする
Streamlit Cloudは、サーバーを自分で調達したりする必要がなく、無料でアプリを公開することができる非常にありがたいクラウドサービスです。

[マシンが強力というわけではない](https://blog.streamlit.io/common-app-problems-resource-limits/)ので、メモリやストレージなどを多く使うアプリはデプロイすることはできませんが、普通のダッシュボードや簡単な機械学習アプリなどなら問題なく使えます。

3で作ったStreamlitのアカウントでログインし、[デプロイ用のページ](https://share.streamlit.io/)で、下記のようにアプリを開発したGithubのリポジトリとブランチを選択し、デプロイします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3554168/57dff992-5fe9-6c35-29f5-5604de707b8d.png)


問題なくデプロイが済めば、そのurlに繋げばいつでもアプリを使えるようになります。
※ずっとアクセスがないとスリープしたり、最終的に削除されます。


# 作ってみたアプリで使った主なコンポーネントの紹介
今回、主に使用したStreamlitのメソッドの概要を紹介します。
詳細は、[Github](https://github.com/YoshimatsuSaito/formula1-web-streamlit)を見てみてください。

## ダッシュボード部分
### 図の描画
Streamlitではplotlyのインタラクティブな図をアプリに埋め込むことができます。
下記のようにplotlyのfigureを定義して、`st.plotly_chart`で描画できます。
```
    fig = px.line(
        df_season_drivers,
        x="round",
        y="points",
        color="driverRef",
        labels={"driverRef": "driver", "name": "Grand Prix"},
        category_orders={"driverRef": list_order_points},
    )
    st.plotly_chart(fig, use_container_width=True)
```

### データの選択
また、データのどの部分を示すか（今回のF1アプリの例では、どのシーズンの、どのドライバーの結果を表示するか）をユーザが決められるように、選択機能を使っています。
`st.selectbox`や`st.multiselect`で入力コンポーネントを作り、値を受け取ることで、先ほどのplotlyで示すデータの範囲をコントロールできます。
```
    selected_season = st.selectbox(
        "Select season", list_season, key="selected_season_season_result"
    )
    df_season = df[df["year"] == selected_season]

    selected_items = st.multiselect(
        "Select drivers (default = top 5 of the season)",
        options=list_drivers,
        default=list_default_drivers,
        key="selected_drivers_season_result",
    )

    df_season_drivers = df_season[df_season["driverRef"].isin(selected_items)]
```

これらを組み合わせることで、このようなダッシュボードが作れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3554168/2920d7a1-3a11-a3e9-9ef6-c9994b00dcc2.png)

## シミュレーション部分
### 入力値の取得
dataframeの単純な描画だけでなく、ユーザからのパラメタ入力値を受けて、何らかの計算をし、その結果を返す、といったような処理もできます。
今回はレース戦略を計算するためのパラメタを`st.number_input`で受け取っています。
`st.columns`など、各入力コンポーネントの位置を調節することができるコンポーネントがあり、UIを向上させるために工夫できます。

```
    col_1, col_2, col_3, col_4 = st.columns(4)
    with col_1:
        total_lap = st.number_input(
            label="Total race laps", value=60, min_value=0, max_value=100, step=1
        )
    with col_2:
        pitloss = st.number_input(
            label="Pitloss(sec)",
            value=25.0,
            min_value=0.0,
            max_value=100.0,
            step=0.1,
            format="%.1f",
        )
～略～
```

### トリガーによって、最適なレース戦略を計算
あるきっかけによって、ある処理を行うといったことを`st.button`で実現できます。
今回は、ボタンが押されたら、[上記`st.number_input`で受け取ったパラメタ](#入力値の取得)たちを自前の関数に与えて、レースの最適戦略を計算し、図として表示します。
```
    if st.button("Calculate optimal strategy!"):
        # get optimal strategy based on given tyre combination
        df_res = _get_optimization_results(
            pitloss=pitloss,
            total_lap=total_lap,
            dict_degradation=dict_degradation,
            dict_pace=dict_pace,
            track_improvement=track_improvement,
            fuel_effect=fuel_effect,
            dict_tyre_combi=dict_tyre_combi,
        )
```

これらの処理により、「Calculate optimal strategy!」ボタンが押されると関数が走り、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3554168/a09d6790-08fc-af68-dff4-8e08ef6f247c.png)
最適戦略の計算結果を表示する、といった処理をStreamlitで実現できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3554168/9fee16f5-15bc-4af3-6c0d-421f7a189efa.png)