由於之前沒學過 Bokeh 套件, 所以先花了幾天時間測試了 Bokeh 功能, 掌握了基本用法後才回頭來測試如何在 Streamlit 上輸出 Bokeh 繪製的圖片. 幾天研究下來發現, Bokeh 真是一款簡單易用, 功能強大且完整的視覺化套件, 用法參考下面幾篇測試 :
本系列所有測試文章參考 :
1. 在虛擬環境安裝 Bokeh 2.4.3 :
因為最新版的 Streamlit v1.45.0 目前只支援到 Bokeh v2.4.3 版, 如果系統安裝的 Boke 比這還新 (最新 v3.7.3) 會出現如下錯誤 :
StreamlitAPIException: Streamlit only supports Bokeh version 2.4.3, but you have version
3.7.3 installed. Please run `pip install --force-reinstall --no-deps bokeh==2.4.3` to
install the correct version.
另外 bokeh v2.4.3 和較新版本的 numpy 不相容, 它僅支援到 Numpy v1.23, 如果系統的 Numpy 版本大於 v1.23 會出現如下錯誤 :
AttributeError: module 'numpy' has no attribute 'bool8'
所以如果要在 Streamlit 應用上輸出 Bokeh 繪製的圖片, 可以建立一個虛擬環境來安裝 Bokeh v2.4.3 與 Numpy v1.23.5 來測試 :
D:\python\test>virtualenv venv
created virtual environment CPython3.10.11.final.0-64 in 19312ms
creator CPython3Windows(dest=D:\python\test\venv, clear=False, no_vcs_ignore=False, global=False)
seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=C:\Users\tony1\AppData\Local\pypa\virtualenv)
added seed packages: pip==23.3.1, setuptools==69.0.2, wheel==0.42.0
activators BashActivator,BatchActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator
這樣便建好一個虛擬環境目錄 venv 了, 切換到 venv :
D:\python\test>cd venv
執行 Scripts 底下的 activate 命令稿來啟動虛擬環境 :
D:\python\test\venv>Scripts\activate
前面出現 (venv) 表示已進入虛擬環境, 用 pip 安裝最新版 Streamlit :
(venv) D:\python\test\venv>pip install streamlit
再指定安裝 Bokeh v2.4.3 版 :
(venv) D:\python\test\venv>pip install bokeh==2.4.3
完成後用 pip list 檢視虛擬環境下的 Python 套件列表 :
(venv) D:\python\test\venv>pip list
Package Version
------------------------- -----------
altair 5.5.0
attrs 25.3.0
blinker 1.9.0
bokeh 2.4.3
cachetools 5.5.2
certifi 2025.4.26
charset-normalizer 3.4.2
click 8.2.1
colorama 0.4.6
gitdb 4.0.12
GitPython 3.1.44
idna 3.10
Jinja2 3.1.6
jsonschema 4.24.0
jsonschema-specifications 2025.4.1
MarkupSafe 3.0.2
narwhals 1.42.1
numpy 1.23.5
packaging 24.2
pandas 2.3.0
pillow 11.2.1
pip 23.3.1
protobuf 6.31.1
pyarrow 20.0.0
pydeck 0.9.1
python-dateutil 2.9.0.post0
pytz 2025.2
PyYAML 6.0.2
referencing 0.36.2
requests 2.32.4
rpds-py 0.25.1
setuptools 69.0.2
six 1.17.0
smmap 5.0.2
streamlit 1.45.1
tenacity 9.1.2
toml 0.10.2
tornado 6.5.1
typing_extensions 4.14.0
tzdata 2025.2
urllib3 2.4.0
watchdog 6.0.0
wheel 0.42.0
這樣便完成測試環境配置了.
2. Bokeh 圖片輸出函式 st.bokeh_chart() :
Streamlit 提供 st.bokeh_chart() 函式來輸出 Bokeh 繪製的圖表, 其參數結構如下 :
st.bokeh_chart(figure, use_container_width=False)
必要參數為 Bokeh 的 Figure 畫布物件, 只要將它傳給 st.bokeh_chart() 即可將圖片顯示在 Streamlit 的網頁中. 由於是透過 Streamlit 展示, 所以程式中毋須匯入 bokeh.plotting.show() 函式.
3. 輸出 Bokeh 折線圖 :
Bokeh 的散布圖方法為 fig.line(), 用法參考 :
Bokeh 的資料來源可以是串列表示的 x, y 軸資料, 也可以是支援字典與 DataFrame 的 ColumnDataSource 物件. 首先來測試串列資料來源 :
測試 1 : 資料來源為串列的折線圖 [看原始碼]
# st-bokeh-test-1.py
import streamlit as st
from bokeh.plotting import figure
x=[0, 1, 2, 3, 4, 5] # x 軸資料
y=[-5, -2, 6, 12, 4, 9] # y 軸資料
fig=figure(width=400, height=300, title='Bokeh 折線圖') # 建立 figure 物件
fig.line(x, y, line_width=4, line_color='cyan') # 繪製折線圖
# 在 Streamlit 顯示圖表
st.bokeh_chart(fig, use_container_width=False)
此例 x, y 軸資料來源為串列, 因為 Bokeh 有設定圖片尺寸為 400x300, 所以 st.bokeh_chart() 要傳入 use_container_width=False, 否則圖片會被自動放大到 Streamlit 容器大小, 結果如下 :
資料來源也可以包裝成 Bokeh 的 ColumnDataSource 物件後再餵給繪圖方法的 source 參數, 這種方式能展現 Bokeh 強大的功能, 例如互動姓, 資料綁定, 多圖表資料共享, 以及能使用進階的轉換器等. 原始的串列資料可以打包成字典或 DataFrame 傳給 ColumnDataSource 類別的建構式.
測試 2 : 資料來源為 ColumnDataSource 物件 (傳入字典) 的折線圖 [看原始碼]
# st-bokeh-test-2.py
import streamlit as st
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
x=[0, 1, 2, 3, 4, 5] # x 軸資料
y=[-5, -2, 6, 12, 4, 9] # y 軸資料
# 將字典轉成 ColumnDataSource 物件
source=ColumnDataSource(data=dict(x=x, y=y))
# 建立 figure 物件
fig=figure(width=400, height=300, title='Bokeh 折線圖')
# 繪製折線圖
fig.line(x='x', y='y', line_width=4, source=source)
# 在 Streamlit 顯示圖表
st.bokeh_chart(fig, use_container_width=False)
此例將原始的串列放入以 'x' 與 'y' 為鍵的字典, 然後將此字典傳給 ColumnDataSource() 建構式打包成 ColumnDataSource 物件, 並將其傳給 fig.line() 的 source 參數. 注意, fig.line() 的 x 與 y 參數要設為 source 物件的 'x' 與 'y' 鍵. 結果如下 :
下面是傳入 DataFrame 的範例 :
測試 3 : 資料來源為 ColumnDataSource 物件 (DataFrame) 的折線圖 [看原始碼]
# st-bokeh-test-3.py
import streamlit as st
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
import pandas as pd
df=pd.DataFrame({
'x': [0, 1, 2, 3, 4, 5], # x 軸資料
'y': [-5, -2, 6, 12, 4, 9] # y 軸資料
})
# 將 DataFrame 轉成 ColumnDataSource 物件
source=ColumnDataSource(df)
# 建立 figure 物件
fig=figure(width=400, height=300, title='Bokeh 折線圖')
# 繪製折線圖
fig.line(x='x', y='y', line_width=4, color='red', source=source)
# 在 Streamlit 顯示圖表
st.bokeh_chart(fig, use_container_width=False)
此例直接將 DataFrame 傳給 ColumnDataSource(), 結果如下 :
4. 輸出 Bokeh 散布圖 :
Bokeh 的散布圖方法為 fig.scatter(), 用法參考 :
下面是資料來源為串列的散布圖範例 :
測試 4 : 資料來源為串列的散布圖 [看原始碼]
# st-bokeh-test-4.py
import streamlit as st
from bokeh.plotting import figure
x=[0, 1, 2, 3, 4, 5] # x 軸資料
y=[-5, -2, 6, 12, 4, 9] # y 軸資料
# 建立 figure 物件
fig=figure(width=400, height=300, title='Bokeh 散布圖')
# 繪製圓點散布圖
fig.scatter(x, y, marker='circle', size=10, color='red')
# 在 Streamlit 顯示圖表
st.bokeh_chart(fig, use_container_width=False)
結果如下 :
下面是資料來源為 ColumnDataSource 物件 (字典) 的散布圖範例 :
測試 5 : 資料來源為 ColumnDataSource 物件 (字典) 的散布圖 [看原始碼]
# st-bokeh-test-5.py
import streamlit as st
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
x=[0, 1, 2, 3, 4, 5] # x 軸資料
y=[-5, -2, 6, 12, 4, 9] # y 軸資料
# 將字典轉成 ColumnDataSource 物件
source=ColumnDataSource(data=dict(x=x, y=y))
# 建立 figure 物件
fig=figure(width=400, height=300, title='Bokeh 散布圖')
# 繪製圓點散布圖
fig.scatter(x='x', y='y', marker='square', size=10, color='navy', source=source)
# 在 Streamlit 顯示圖表
st.bokeh_chart(fig, use_container_width=False)
此例改用方點 (marker='square') 為標記符號, 結果如下 :
下面是資料來源為 ColumnDataSource 物件 (DataFrame) 的散布圖範例 :
測試 6 : 資料來源為 ColumnDataSource 物件 (字典) 的散布圖 [看原始碼]
# st-bokeh-test-6.py
import streamlit as st
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
import pandas as pd
df=pd.DataFrame({
'x': [0, 1, 2, 3, 4, 5], # x 軸資料
'y': [-5, -2, 6, 12, 4, 9] # y 軸資料
})
# 將 DataFrame 轉成 ColumnDataSource 物件
source=ColumnDataSource(df)
# 建立 figure 物件
fig=figure(width=400, height=300, title='Bokeh 散布圖')
# 繪製圓點散布圖
fig.scatter(x='x', y='y', marker='star', size=10, color='purple', source=source)
# 在 Streamlit 顯示圖表
st.bokeh_chart(fig, use_container_width=False)
結果如下 :
5. 輸出 Bokeh 直條圖 :
下面是資料來源為串列的散布圖範例 :
測試 7 : 資料來源為串列的長條圖 [看原始碼]
# st-bokeh-test-7.py
import streamlit as st
from bokeh.plotting import figure
x=[1, 2, 3, 4, 5] # x 軸資料
y=[120000, 135000, 99000, 150000, 170000] # y 軸資料
# 建立 figure 物件
fig=figure(width=400, height=300, title='Bokeh 長條圖')
fig.yaxis.formatter.use_scientific=False # 關閉科學記號轉換
# 繪製長條圖
fig.vbar(x, top=y, width=0.5, color='cyan', line_color='blue')
# 在 Streamlit 顯示圖表
st.bokeh_chart(fig, use_container_width=False)
結果如下 :
下面是資料來源為 ColumnDataSource 物件 (字典) 的長條圖範例 :
測試 8 : 資料來源為 ColumnDataSource 物件 (字典) 的長條圖 [看原始碼]
# st-bokeh-test-8.py
import streamlit as st
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
x=[1, 2, 3, 4, 5] # x 軸資料
y=[120000, 135000, 99000, 150000, 170000] # y 軸資料
# 將字典轉成 ColumnDataSource 物件
source=ColumnDataSource(data=dict(x=x, y=y))
# 建立 figure 物件
fig=figure(width=400, height=300, title='Bokeh 長條圖')
fig.yaxis.formatter.use_scientific=False # 關閉科學記號轉換
# 繪製長條圖
fig.vbar(x='x', top='y', width=0.5, color='orange', line_color='black', source=source)
# 在 Streamlit 顯示圖表
st.bokeh_chart(fig, use_container_width=False)
結果如下 :
下面是資料來源為 ColumnDataSource 物件 (DataFrame) 的長條圖範例 :
測試 9 : 資料來源為 ColumnDataSource 物件 (字典) 的長條圖 [看原始碼]
# st-bokeh-test-9.py
import streamlit as st
from bokeh.plotting import figure
from bokeh.transform import dodge
from bokeh.models import ColumnDataSource
x=['一月', '二月', '三月', '四月', '五月'] # x 軸資料
y1=[120000, 135000, 99000, 150000, 170000] # y 軸資料
y2=[100000, 125000, 87000, 140000, 160000]
# 建立 ColumnDataSource
source=ColumnDataSource(data=dict(x=x, y1=y1, y2=y2))
# 建立 figure 物件
fig=figure(x_range=x, width=500, height=400, title='Bokeh 群組長條圖')
fig.yaxis.formatter.use_scientific=False # y 軸為整數
# 繪製第一個長條圖
fig.vbar(
x=dodge('x', -0.2, range=fig.x_range), # 往左偏移 20%
top='y1',
width=0.4,
color='navy',
legend_label='今年營收',
source=source
)
# 繪製第二個長條圖
fig.vbar(
x=dodge('x', 0.2, range=fig.x_range), # 往右偏移 20%
top='y2',
width=0.4,
color='cyan',
legend_label='去年營收',
source=source
)
fig.legend.location='top_left'
# 在 Streamlit 顯示圖表
st.bokeh_chart(fig, use_container_width=False)
結果如下 :









沒有留言 :
張貼留言