請啟用 JavaScript 來查看內容

(2023年新版) 使用 Streamlit 簡單又快速建立 Dashboard 網頁

前言

之前寫這篇 Streamlit 的教學文章已經過去兩年多了,陸續官方還增加了許多功能與優化,而且從我部落格後台的數據來看,發現那篇文章還蠻多人觀看。因此決定再寫篇更新的內容,並多補充一些細節。

此篇文章使用的 Streamlit 是 v1.26.0 版本。


Streamlit 幫助我們快速製作 Web 應用程式,而且不需要任何前端技能,全部都採用 Python 語法完成。讓你輕鬆分享網路爬蟲、數據科學和機器學習的資料,是一個非常方便的套件。

* 文章最後會附上完整的範例程式碼,請一定看到最後~


Demo

這是最後將程式碼放到 GitHub,並使用 Streamlit Cloud 部署的成果。

Streamlit Demo 範例
Streamlit Demo 範例

安裝與執行

安裝

1
pip install streamlit

撰寫本篇文章時最新版為:v1.26.0

執行

1
2
3
4
streamlit run <py程式檔案路徑>

# 以我下方文章範例的話,就是:
streamlit run streamlit_app.py

如果想指定 Streamlit 的 port 埠號,只要在後方加上--server.port 參數:

1
streamlit run <py程式檔案路徑> --server.port 8888

需要終止該應用程式只需在終端中按 Ctrl + c


創建 Streamlit 應用程式

除了跟著我以下的文章流程學習,也可以參考 官方文檔 Get started 的流程操作。

新增 py 程式碼

新創建一個 Python 檔案 streamlit_app.py,以下教學都只會編輯這個檔案。

打開它,在一開始導入 Streamlit 與範例需要的其他套件:

1
2
3
4
5
6
import time

import streamlit as st

import numpy as np
import pandas as pd

打開 "命令提示字元" 或 "終端機",開始執行程式:

1
streamlit run streamlit_app.py

* 第一次執行時,它可能會問你 Email,這可以不用理它,直接按 Enter 即可。

執行 Streamlit 程式,第一次會遇到 Email
執行 Streamlit 程式,第一次會遇到 Email

它應該會顯示如下資訊,並自動開啟一個瀏覽器網頁。

執行 Streamlit 程式
執行 Streamlit 程式

然後網頁裡面顯示… 一片空白?!
沒錯!我們又還沒有添加元件進去,怎麼會有東西呢~ 😆


添加文字和數據

先幫我們的應用程式上個標題,加入以下程式碼:

1
st.title('我的第一個應用程式')

存檔,回到瀏覽器的網頁上。

可以重整網頁,或點擊右上角菜單內 "Rerun" 的按鈕,

Rerun 重整網頁
Rerun 重整網頁

網頁上就會顯示一個標題文字了:

試著加入標題吧
試著加入標題吧

如何?簡單吧,那我們再繼續接著下去~


使用 st.write 和 Magic Commands

在 Streamlit 內有個神奇的指令,他們把它叫 st.write 和 Magic Commands,不管給它 "字串"、"Markdown"、"數據資料" 還是 "圖表",Streamlit 都會自動辨別並以合適的方式顯示出來。

在程式碼中只要把想顯示的東西放進 st.write,甚至單一行只有變數或字串,根本不需要使用 st.write,它都會自動套用並顯示。

1
2
3
4
5
6
7
st.write("嘗試創建**表格**:")

df = pd.DataFrame({
    'first column': [1, 2, 3, 4],
    'second column': [10, 20, 30, 40]
})
df
呈現 Markdown 與 表格
呈現 Markdown 與 表格

但是官方有提到有以下三個原因,如果可以的話還是使用更具體的方法比較好:

  1. Magicst.write() 會檢查您傳入的數據類型,然後決定如何最好地呈現,但有時你可能想以另一種方式呈現它。
  2. 其他方法返回一個可以使用和修改的對象,可以通過向其添加數據或替換它。
  3. 如果使用更具體的 Streamlit 方法,您可以傳遞其他參數來自定義其行為。

繪製圖表和地圖 Chart

折線圖 st.line_chart()

使用 Numpy 生成一個隨機樣本,然後將其繪製成折線圖。

1
2
3
4
chart_data = pd.DataFrame(
    np.random.randn(20, 3),
    columns=['a', 'b', 'c'])
st.line_chart(chart_data)

而且它本身有下載、全螢幕觀看等功能。

繪製折線圖
繪製折線圖

地圖 st.map()

使用 Numpy 生成一個隨機樣本,繪製到地圖上。

1
2
3
4
map_data = pd.DataFrame(
    np.random.randn(100, 2) / [50, 50] + [22.6, 120.4],
    columns=['lat', 'lon'])
st.map(map_data)
繪製地圖
繪製地圖

更多圖表元件請參考:官方文件 Chart elements


輸入類元件

除了上述標題、圖表,還有其他小工具,像是按鈕、選擇框、複選框、滑塊等等。


按鈕 st.button()
1
2
if st.button('不要按!'):
    st.text("不是叫你不要按了嗎!")
按鈕元件
按鈕元件

但你可能會注意到,每次點擊按鈕時,上方的折線圖也會產生變化。
這是因為 Streamlit 只要元件有輸入值的改變,整個程式(網頁)就會重新運行,因此折線圖裡的 random 才會出來不同資料。


複選框 st.checkbox()

使用複選框來顯示/隱藏數據,例如將剛剛繪製地圖的程式改成:

1
2
3
4
5
if st.checkbox('顯示地圖圖表'):
    map_data = pd.DataFrame(
        np.random.randn(100, 2) / [50, 50] + [22.6, 120.4],
        columns=['lat', 'lon'])
    st.map(map_data)

選擇框 st.selectbox()

使用選擇框提供使用者選擇:

1
2
3
4
option = st.selectbox(
    '你喜歡哪種動物?',
    ['狗', '貓', '鸚鵡', '天竺鼠'])
st.text(f'你的答案:{option}')
複選框 與 選擇框
複選框 與 選擇框

調整佈局 Layout

除了剛剛示範的一行一個元件,Streamlit 有提供幾種佈局容器元件供使用、排版。


側邊欄 st.sidebar

例如我們能將元件移到側邊欄中,這邊範例將剛剛的選擇框移入側邊欄:

1
2
3
4
option = st.sidebar.selectbox(
    '你喜歡哪種動物?',
    ['狗', '貓', '鸚鵡', '天竺鼠'])
st.sidebar.text(f'你的答案:{option}')
將元件移到側邊欄
將元件移到側邊欄

列容器 st.columns()

也有左右兩邊的方式來排列的容器:

1
2
3
left_column, right_column = st.columns(2)
left_column.write("這是左邊欄位。")
right_column.write("這是右邊欄位。")

展開容器 st.expander()

也可以通過能展開的容器,來隱藏大量內容來節省空間:

1
2
expander = st.expander("點擊來展開...")
expander.write("如果你要顯示很多文字,但又不想佔大半空間,可以使用這種方式。")
將內容摺疊起來,節省空間
將內容摺疊起來,節省空間

分頁容器 st.tabs()

或者切出不同分頁,來放置不同種類的資料:

1
2
3
4
5
6
7
8
9
tab1, tab2 = st.tabs(["Cat 介紹", "Dog 介紹"])

with tab1:
   st.header("A cat")
   st.image("https://static.streamlit.io/examples/cat.jpg", width=200)

with tab2:
   st.header("A dog")
   st.image("https://static.streamlit.io/examples/dog.jpg", width=200)
不同分頁,放置不同種類的資料
不同分頁,放置不同種類的資料

其他更多佈局和容器請參考:官方文件 Layouts and Containers


狀態元件 Status

進度條 st.progress()

使用 time.sleep() 來模擬進度條的等待:

1
2
3
4
5
6
bar = st.progress(0)
for i in range(100):
    bar.progress(i + 1, f'目前進度 {i+1} %')
    time.sleep(0.05)

bar.progress(100, '載入完成!')

而且網頁右上角還會顯示"RUNNING…",並有多個運動圖示跳動,讓使用者知道目前網頁中還有東西正在運行。

進度條載入時,網頁右上角 RUNNING... 字樣
進度條載入時,網頁右上角 RUNNING... 字樣

消息通知 st.toast()

當有時候我們會需要跳出一個消息通知使用者,但並不想太干擾使用者,這時候就可以使用 st.toast()

* 也可以為文字加上顏色,和加上 icon。

1
2
3
4
if st.button('儲存', type="primary"):
    st.toast(':rainbow[你編輯的內容已經保存]', icon='💾')
    # 或是簡單點,只顯示文字
    # st.toast('你編輯的內容已經保存')

box 訊息 st.success()st.info()st.warning()st.error()

或者想要在頁面上顯示較醒目的錯誤訊息:

1
2
3
4
st.success('Success!')
st.info('Info!')
st.warning('Warning!')
st.error('Error!', icon='🚨')
box 訊息 和右下角的 toast 消息
box 訊息 和右下角的 toast 消息

特效 st.balloons()st.snow()

還有看似無用,但真的好像無用(?)的特效,慶祝氣球 st.balloons() 與慶祝降雪 st.snow()

慶祝氣球特效
慶祝氣球特效

聊天元件 Chat

聊天元件 st.chat_message()st.chat_input()

最近因為 ChatGPT 之類的 LLM 熱門而新增的 "聊天元件" st.chat_message()st.chat_input()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
with st.chat_message("user"):  # 或者寫 "human"
    st.write("Hi 👋,請問你是誰?")

# 另一種寫法
message = st.chat_message("assistant")  # 或者寫 "ai"
# message = st.chat_message("assistant", avatar="🦖")  # 自訂頭像
message.write("你好!我是 ChatBot 🤖,可以回答各種問題,提供資訊。")
message.write("有什麼我可以幫助你的嗎?")

st.chat_input("Say something...")
因應最近 LLM 熱門而新增的聊天元件
因應最近 LLM 熱門而新增的聊天元件

官方還特別寫兩篇教你如何搭建一個對話式的應用程式,甚至示範如何串接 OpenAI 的 GPT 模型,或透過 LangChain 串接:


表單元件

表單 st.form()

經過上面的嘗試,我們都發現在 Streamlit 中,操作每個元件都會導致整個應用程式重新運行。

但是,有時我們可能希望像表單那樣,全部填寫好後再一次觸發(提交),這時候就可以使用 st.form() 表單元件:

1
2
3
4
5
6
7
8
with st.form(key='my_form'):
    form_name = st.text_input(label='姓名', placeholder='請輸入姓名')
    form_gender = st.selectbox('性別', ['男', '女', '其他'])
    form_birthday = st.date_input("生日")
    submit_button = st.form_submit_button(label='Submit')

if submit_button:
    st.write(f'hello {form_name}, 性別:{form_gender}, 生日:{form_birthday}')
使用表單元件,一次一起提交
使用表單元件,一次一起提交

使用快取 Caching

如同前面所述,只要元件輸入值有改變,整個程式(網頁)就會重新運行,但假如有較耗費時間、資源的運算,會造成使用者每調一個參數,就浪費時間在等待,或者產生的物件被重置。

因此 Streamlit 內提供兩種快取(Caching)機制,將之前運算過的結果快取起來,如果下次遇到一樣的輸入值,就能直接使用之前的結果,節省時間,也有助於在重新運行時持久保存物件。

這兩種快取機制以裝飾器(Decorator)寫法使用,用途區分如下:

  1. @st.cache_data:計算後的結果、從 CSV 加載 DataFrame、查詢 API…,大多情況都可以使用 @st.cache_data
  2. @st.cache_resource:全域資源,例如 ML 模型或資料庫連接。
Streamlit 提供兩種緩存(Caching)機制
Streamlit 提供兩種緩存(Caching)機制

對於會變動的資料來源,建議可以設定 ttl (time to live),例如設定 ttl=300,則快取會在 3600 秒 (1小時) 後失效,並在重新運行函數時,再次執行、緩存快取。

對於資料量太大造成記憶體可能不足問題,則可以設定 max_entries=1000 來限制最大資料筆數。

* 更多說明請參考官方文件:Caching


要使用 @st.cache_data 裝飾器,就需要將運算部分的程式碼包成函式(def)。

1
2
3
4
5
6
7
@st.cache_data(ttl=3600, show_spinner="正在加載資料...")  # 👈 Add the caching decorator
def load_data(url):
    df = pd.read_csv(url)
    return df

df = load_data("https://raw.githubusercontent.com/plotly/datasets/master/26k-consumer-complaints.csv")
st.dataframe(df)

第一次載入時會跑比較久(上面範例是讓它到網路上下載 csv 資料後顯示),之後再重整網頁時,因為有快取的關係,它應該一瞬間就顯示出來了。


如果想要清除快取再測試,可以從右上角的選單選擇 "Clear caches":

右上角選單 "Clear caches" 可以清除快取
右上角選單 "Clear caches" 可以清除快取

網頁配置設定

此函式有五個參數:

  • page_title:網頁標題,顯示在瀏覽器分頁的標籤上,預設是程式碼的檔名。
  • page_icon:網頁圖標,顯示在網頁標題前,可以使用 st.image 或 Emoji,或者使用"random"讓它隨機產生XD。
  • layout:網頁中佈局寬度,預設是"centered",還可以使用"wide"。
  • initial_sidebar_state:側邊欄顯示狀態,"expanded"打開或"collapsed"隱藏,預設是"auto",代表在手機尺寸的設備上是隱藏,否則是打開顯示。
  • menu_items:右上角的菜單設定,共有以下三項可以設定:
    • “Get help”:設定 URL,如果沒有,則會隱藏此菜單選項。
    • “Report a Bug”:設定 URL,如果沒有,則會隱藏此菜單選項。
    • “About”:顯示在 "關於" 彈跳視窗中的 Markdown 字串,如果沒有,則僅顯示 Streamlit 預設的內容。
    • URL 也可以設定為電子郵件地址,像是 mailto:john@example.com

官方文件:st.set_page_config

自訂網頁標題與 icon
自訂網頁標題與 icon
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
st.set_page_config(
   page_title="自定義網頁標題",
   page_icon="random",
   layout="centered",
   initial_sidebar_state="expanded",
   menu_items={
        'Get Help': 'https://blog.jiatool.com/about/',
        'About': "# 這是什麼網頁? \n**[IT空間](https://blog.jiatool.com/)** 示範 streamlit 之用網頁"
    }
)

在網頁右上方選單,除了之前提過的 Rerun 和清除快取,還有網頁設定與螢幕錄影功能,將你的操作流程直接錄影起來,展示給其他人。

而選單內的"設定"有幾個選項:

  • Run on save:程式存檔後網頁自動重跑,不需要再手動點 Rerun
  • Wide mode:佈局寬度調寬
  • Theme:選擇或自訂佈景主題

* 自定義主題可參考官方介紹說明

右上角選單 "Settings" 網頁設定
右上角選單 "Settings" 網頁設定

更多的元件、工具

Streamlit 還有提供更多的元件,和或者其他用途的工具:


官方有將 streamlit 可以下的指令、各式元件的寫法濃縮整理成一頁,方便快速查閱:https://cheat-sheet.streamlit.app/


部署應用程式

除了將程式在自己的電腦執行,或架在伺服器之外,官方有提供「Streamlit Community Cloud」,可以在上面部署、管理、共享你的 Streamlit 應用程式。你只要將專案上傳自己的 GitHub 存儲庫(公開或私有都可以),再到 Streamlit Community Cloud 設定連接即可,算是蠻方便的。

* 關於 Streamlit Community Cloud 官方說明可在這邊查看:Welcome to Streamlit Community Cloud


完整部署介紹請參考官方說明,只需要四個步驟:

  1. 先確認你的資料夾內有程式執行需要的依賴套件文件
    • Python 依賴文件 [必要]:requirements.txtPipfileenvironment.ymlpyproject.toml 這幾種擇一,指定程式需要哪些 Python 套件。
    • apt-get 依賴 [非必要]:需要哪些 apt-get 的依賴套件。
  2. 將專案推到自己的 GitHub (公開或私有都可以)。
  3. 前往 share.streamlit.io 工作區,連接你剛剛上傳的 GitHub 存儲庫。
  4. 部署完成!!

推上 GitHub 的專案資料夾內大致會長這樣:

your-repository/
├── streamlit_app.py
└── requirements.txt
推上 GitHub Repository
推上 GitHub Repository

前往 share.streamlit.io 工作區,連接你剛剛上傳的 GitHub 存儲庫。

點擊右上角 "New app"

右上角 "New app"
右上角 "New app"

第一次需要給 Streamlit 擁有讀取 GitHub Repository 的權限:

第一次需要給 Streamlit 讀取 GitHub 權限
第一次需要給 Streamlit 讀取 GitHub 權限

下一步,指定 GitHub Repository、分支、程式檔案名稱,以及是否要自訂網址名稱(你可以為你的應用程式自訂一個獨一無二的名稱):

部署設定
部署設定

下方還可以打開 "Advanced settings…",這邊可以更改 Python 版本、設定環境變數。
我們這個範例不需要編輯這邊。

部署進階設定
部署進階設定

點擊 "Deploy!",等個一、兩分鐘安裝。

部署中...
部署中...

部署完成,網址列上顯示的就是你這個應用程式的專屬網址,把它分享給其他人炫耀你努力的成果吧~🎉

部署完成
部署完成


前往 share.streamlit.io 工作區,可以查看你全部的 Streamlit 應用程式,它還提供統計來訪人數的功能。

share.streamlit.io 工作區
share.streamlit.io 工作區

來看看我 2021 年發布的 Demo 範例,共有 1,522 名讀者來訪~ (而且是計算不重複的)
* 這個統計功能是從 2022 年 4 月才開始,所以實際上會再更多。

統計來訪人數
統計來訪人數

完整範例程式碼

附上完整範例程式碼:streamlit_app.py

此範例程式碼部署在 Streamlit Cloud (share.streamlit.io) 的 Demo:


結語

從之前第一次嘗試 Streamlit 到現在過了兩年多,它又陸續新增了不少功能,看起來還有積極再發展。

經過上方教學實際操作後,會發覺這個套件真的可以很簡單、快速的建立一個顯示資料用的儀表板(Dashboard),因此特別寫這篇文章來分享給大家~




參考:
Streamlit 官方網站
Streamlit 官方文檔


每一個你討厭的現在,
都有一個不夠努力的曾經。

—— 幾米 (台灣繪本作家)


🔻 如果覺得喜歡,歡迎在下方獎勵我 5 個讚~
分享

Jia
作者
Jia
軟體工程師 - Software Engineer