streamlit個人メモ

データ分析などを行ってでも簡単に結果共有したい場合、簡単になにか方法がないかと探ったら`streamlit`にたどり着いたのでちょっと勉強がてら書いてみる。

基本的な`streamlit`についてはGoogleさんに聞いていただくとして実践での活用メモを記載していきます。

## その１．まずはテーブル内の情報をWebリンクさせたい
- `st.dataframe`でWebURLを自動認識するわけでなく
- `st.table`でも同様

う〜ん、どうしたものか


## HTMLを使って書けばよいのですね
こちらからヒントをいただきました、ありがとうございます。
https://github.com/shimat/kanto_it_flu
### streamlitビルドページ
https://share.streamlit.io/shimat/kanto_it_flu/main/main.py


`st.write(xxxx, unsafe_allow_html)`なんてあるわけですから。

## コードはちょっとだけ改変する
HTMLリンクさせたいテーブル列の情報をankerタグで置き換えているので`住所`列での検索がうまく行かないようです。考えてみれば`先頭一致`や`正規表現`だとanker混じりのデータだとヒットしないわけで。。。

## 改変のアイデア
選択条件を受けてのdfフィルター実施時は元のデータのままにして、HTMLテーブル書き出しのタイミングでanker変換すればOKとなりました。

## コードはこちら
```python
# library
import requests
import pandas as pd
import streamlit as st

####################################################################
# streamlit 環境設定
####################################################################
# 最初に設定する必要があります、場所はここ
st.set_page_config(layout='wide')

####################################################################
# データ取得関数
####################################################################
@st.cache()
def load_data():
    response = requests.get("https://influenza.toshinkyo.or.jp/influ-data/influ-list.xls")
    df = pd.read_excel(
        response.content,
        sheet_name=0, 
        skiprows=2,
        usecols=[1, 3, 4, 5, 6]) # drop 医療機関コード, 郵便番号
    df.columns = ["医療機関名称", "住所", "電話番号", "料金(税込)", "医療機関通信欄"]
    
    # 欠損値を対応する df.isnull().sum()にてNan存在確認
    ## 医療機関通信欄のNA
    df['医療機関通信欄'].fillna(value='', inplace=True)
    ## 料金(税込)のNA
    # ￥マークは全角にしてますね
    df["料金(税込)"] = df["料金(税込)"].fillna(value=0).astype('int').astype('str').map(lambda s: f"¥{s}").replace("¥0", "<N/A>")

    return df

# データ生成
df = load_data()

####################################################################
# streamlitレイアウト定義
####################################################################
# title
st.title(f"東京都総合組合保健施設振興協会(東振協) インフルエンザ予防接種 会場リスト")

# カラム構成定義
## 3:2比率で2カラムを作成する
col1, col2 = st.columns([3, 2])

# col1配置
with col1:
    query = st.text_input(
        label='住所検索',
        help='入力した文字列と一致、または正規表現にマッチする住所でリストアップします'
    )

# col2配置
with col2:
    search_option = st.selectbox(
        '検索方法',
        ('部分一致', '先頭一致', '正規表現', "")
    )

####################################################################
# カラム配置オブジェクトからの選択パラメータを受けての制御
####################################################################
if query:
    if search_option == '先頭一致':
        df = df[df['住所'].str.startswith(query)]
    elif search_option == '部分一致':
        df = df[df['住所'].str.contains(query, regex=False)]
    elif search_option == '正規表現':
        try:
            df = df[df['住所'].str.match(query)]
        except Exception as e:
            st.write(f'正規表現がただしくありません')
    else:
        df = df

    # この処理は検索条件設定後に実施しないと空振りする
    # df生成処理からは独立させる必要がある
    # Webテーブルで ankerTagを持たせる
    df['医療機関名称'] = df['医療機関名称'].map(lambda s: f"<a target='_blank' href='https://www.google.com/maps/search/?api=1&query={s}'>{s}</a>")
    df['住所'] = df['住所'].map(lambda s: f"<a target='_blank' href='https://www.google.com/maps/search/?api=1&query={s}'>{s}</a>")

####################################################################
# HTMLテーブルで結果を表現する
####################################################################
html = df.to_html(escape=False)
st.write(
    """
    <style type="text/css">
    # 1列目→indexを表現しないといったことも可能
    # table td:nth-child(1) {
    #     display: none
    # }
    # table th:nth-child(1) {
    #     display: none
    # }
    
    # テーブルレイアウト定義
    table.dataframe {
        display: block;
        overflow-x: scroll;
        overflow-y: scroll;
        height: 600px;
    }
    </style>
    """, 
    unsafe_allow_html=True)

# テーブル描画
st.write(
    html, 
    unsafe_allow_html=True)
```