2025年6月11日 星期三

Python 學習筆記 : 用 Bokeh 繪製互動式圖表 (一) 折線圖

Bokeh 是一個支援 Python, R, 與 Lua 等程式語言的互動式資料視覺化開源套件, 具有縮放, 平移, 選取, 懸停提示等多種互動操作, 特別適合給資料工程師建立資料儀表板來展示探索性資料分析結果. Bokeh 套件其實就是將 Python 繪圖程式轉換成 HTML 和 Backeh.js 構成的前端網頁結構, 因此可以嵌入到網頁應用程式於瀏覽器中執行. Bokeh 採 BSD 授權開放原始碼, 可免費使用於商業用途. 

參考書籍 :  
  1. Python 網路爬蟲與資料視覺化 (旗標, 2018) 第 15 章
  2. Python 初學特訓班-增訂版 (碁峰, 2017) 第 7 章
  3. Python 數據可視化-基於 Bokeh 的可視化繪圖 (機械工業出版)
  4. Python programming for data analysis (Springer, 2021) 第 6 章
  5. Hands-On Data Visualization with Bokeh (Packt, 2019)
  6. Python Data Visualization Essentials Guide (BPB)
  7. Data Visualization with Python for Beginners (AI Publishing) 第 9 章
  8. Python Data Analysis 3rd (Packt, 2021)
  9. Interactive Data Visualization with Python 2nd (Packt, 2020)
  10. The Data Visualization Workshop (Packt, 2020)

1. 安裝 bokeh 套件 : 

可使用 pip 或 pip3 (Linux) 安裝 Bokeh 

pip install bokeh 

我之前已安裝過 3.2.2 版 :

>>> import bokeh   
>>> bokeh.__version__   
'3.2.2'

所以加 -U 參數升版 :

pip install bokeh -U

D:\python\test>pip install bokeh -U   
Requirement already satisfied: bokeh in c:\users\tony1\appdata\roaming\python\python310\site-packages (3.2.2)
Collecting bokeh
  Downloading bokeh-3.7.3-py3-none-any.whl.metadata (12 kB)
Requirement already satisfied: Jinja2>=2.9 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from bokeh) (3.1.2)
Collecting contourpy>=1.2 (from bokeh)
  Downloading contourpy-1.3.2-cp310-cp310-win_amd64.whl.metadata (5.5 kB)
Collecting narwhals>=1.13 (from bokeh)
  Downloading narwhals-1.42.0-py3-none-any.whl.metadata (11 kB)
Requirement already satisfied: numpy>=1.16 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from bokeh) (1.24.3)
Requirement already satisfied: packaging>=16.8 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from bokeh) (24.2)
Requirement already satisfied: pandas>=1.2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from bokeh) (2.0.3)
Requirement already satisfied: pillow>=7.1.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from bokeh) (9.5.0)
Requirement already satisfied: PyYAML>=3.10 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from bokeh) (6.0.1)
Requirement already satisfied: tornado>=6.2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from bokeh) (6.3.3)
Requirement already satisfied: xyzservices>=2021.09.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from bokeh) (2023.7.0)
Requirement already satisfied: MarkupSafe>=2.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from Jinja2>=2.9->bokeh) (2.1.3)
Requirement already satisfied: python-dateutil>=2.8.2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pandas>=1.2->bokeh) (2.8.2)
Requirement already satisfied: pytz>=2020.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pandas>=1.2->bokeh) (2023.3)
Requirement already satisfied: tzdata>=2022.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pandas>=1.2->bokeh) (2023.3)
Requirement already satisfied: six>=1.5 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from python-dateutil>=2.8.2->pandas>=1.2->bokeh) (1.16.0)
Downloading bokeh-3.7.3-py3-none-any.whl (7.0 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.0/7.0 MB 4.2 MB/s eta 0:00:00
Downloading contourpy-1.3.2-cp310-cp310-win_amd64.whl (221 kB)
Downloading narwhals-1.42.0-py3-none-any.whl (359 kB)
Installing collected packages: narwhals, contourpy, bokeh
  Attempting uninstall: contourpy
    Found existing installation: contourpy 1.1.0
    Uninstalling contourpy-1.1.0:
      Successfully uninstalled contourpy-1.1.0
  Attempting uninstall: bokeh
    Found existing installation: bokeh 3.2.2
    Uninstalling bokeh-3.2.2:
      Successfully uninstalled bokeh-3.2.2
Successfully installed bokeh-3.7.3 contourpy-1.3.2 narwhals-1.42.0

可見 Bokeh 本身很嬌小才 7MB 而已. 檢視版本 : 

>>> import bokeh   
>>> bokeh.__version__   
'3.7.3'

可見已升至最新版. 


2. Bokeh 的模組化結構 : 

Bokeh 套件採模組化設計, 包含 14 個模組, 涵蓋從高階快速繪圖, 低階元件自訂, 排版, 輸出, 伺服器互動, 色彩方案, 範例資料到互動小部件等完整功能, 能滿足各種資料視覺化與互動應用. 這些模組共同構成了 Bokeh 強大且靈活的視覺化生態系 : 


Bokeh 模組名稱 說明
bokeh.plotting 高階繪圖 API, 可快速建立折線圖、長條圖、散點圖等常見圖表。
bokeh.models 低階元件系統, 提供資料來源、控制項與互動事件等物件。
bokeh.layouts 負責安排圖表與控制元件的位置, 支援 row、column 排版。
bokeh.io 圖表輸出與展示函式, 如 show(), output_file(), output_notebook() 等。
bokeh.server 建構 Bokeh Server Web 應用的基礎模組, 支援互動式資料更新。
bokeh.application 封裝多頁或模組化的 Bokeh Server 應用, 支援動態載入與初始化。
bokeh.document 管理 Bokeh Document 結構, 儲存所有圖表與元件的狀態。
bokeh.themes 套用主題樣式, 可自訂字型、顏色、背景等視覺樣式。
bokeh.embed 將 Bokeh 圖表嵌入 HTML、Flask、Jupyter 等應用環境。
bokeh.resources 控制 JS/CSS 資源載入方式, 如 inline 或 CDN 模式。
bokeh.transform 資料欄位轉換工具, 用於視覺映射如大小、顏色等動態設定。
bokeh.palettes 提供內建配色方案, 包含類別色系與連續漸層色系。
bokeh.events 自訂事件處理模組, 支援點擊、拖曳等使用者互動事件綁定。
bokeh.util 工具與內部函式庫, 包含除錯、類型檢查等開發輔助工具。


各模組之使用情境分類如下表 : 


使用情境 建議使用模組
快速畫圖 bokeh.plotting, bokeh.io
建立儀表板 bokeh.models, bokeh.layouts, bokeh.themes
內嵌 HTML/Flask bokeh.embed, bokeh.resources
Web App(伺服器端) bokeh.server, bokeh.application, bokeh.document
進階互動 bokeh.events, bokeh.transform, bokeh.events
開發輔助工具 bokeh.util


3. Bokeh 繪圖核心模組與 figure 物件的方法 : 

對於一般繪圖應用而言, 最常使用到的 Bokeh 模組是下列三個核心模組 :


Bokeh 核心模組 說明
bokeh.plotting 提供高階繪圖介面, 可快速建立常見圖表如折線圖, 長條圖, 散點圖等, 適合快速原型與互動視覺化.
bokeh.models 提供低階物件系統, 控制圖表中的所有元件, 包含資料來源, 滑桿, 按鈕, 圖例, 工具提示等, 適合需要自訂互動邏輯的應用.
bokeh.layouts 用於安排圖表與控制項的位置, 支援橫向 (row), 縱向 (column) 或巢狀排版, 是建立 Web 儀表板的重要工具.


繪圖相關函式都放在 bokeh.plotting 模組裡, 最常用的是下表的四個函式 :


bokeh.plotting 函式 說明
figure() 建立一個 figure 物件,並可設定標題、軸標籤、範圍、工具列等。
output_file() 指定輸出目標 HTML 檔案的檔名與標題,使得圖表可以儲存為網頁。
show() 顯示繪製好的圖形,一般會開啟瀏覽器來呈現 output_file() 所設定的 HTML。
save() 將圖形儲存為 HTML 檔案但不開啟瀏覽器,通常與 output_file() 搭配使用。


其中 figure() 函式是整個 Bokeh 繪圖程序的起點, 此函式之參數結構如下表 : 


 figure() 參數  說明
 width  設定圖形寬度(像素),預設為 600。
 height  設定圖形高度(像素),預設為 600。
 title  圖表標題,可為字串或 Title 物件。
 x_range  指定 X 軸的範圍(如 [0, 10])或使用 Range1d、FactorRange。
 y_range  指定 Y 軸的範圍,使用方式同 x_range。
 tools  指定互動工具(如 "pan,wheel_zoom,reset")。
 x_axis_type  "linear"、"log"、"datetime" 或 "auto"。
 y_axis_type  "linear"、"log"、"datetime" 或 "auto"。
 toolbar_location  工具列位置,"above"、"below"、"left"、"right" 或 None。
 background_fill_color  背景色,可為顏色名稱 "red"、色碼 "#ff00ff" 或 "rgba(255, 0, 0, 0.5)"。
 sizing_mode  響應式大小,常見值有 "fixed"、"stretch_width"、"scale_both" 等。
 match_aspect  設為 True 時保持 x/y 軸比例一致。
 output_backend  "canvas"(預設)、"svg"、"webgl",指定繪圖後端。


除了畫布底色外, 在 Bokeh 中所有其他顏色相關的參數都可以用顏色名稱, 16 進位色碼, 以及 RGB 色碼這三種方法表示, RGB 色碼除了 "rgba(r, g, b, a)" 字串外也可以傳入 [r, g, b] 串列, 會自動將其轉成 "rgba(r, g, b)" 字串. 關於顏色名稱參考 :


figure() 函式沒有必要參數 (每個參數都有預設值), 呼叫 figure() 會傳回一個代表畫布的 figure 物件, 例如 : 

>>> from bokeh.plotting import figure    
>>> fig=figure()   
>>> type(fig)   
<class 'bokeh.plotting._figure.figure'>   

figure 物件提供了許多方法可在畫布上繪製圖像元素 (Bokeh 稱為 glyph), 這些方法可分為主要繪圖之方法與輔助繪圖之方法兩類, 主要繪圖之方法說明如下表 :


 figure 物件繪圖方法  說明
 line() 繪製折線圖, 必要參數 x, y 軸序列資料
 circle() 繪製圓點圖(常用於散佈圖)
 scatter() 繪製散佈圖,可自訂標記樣式
 square() 繪製方形點
 triangle() 繪製三角形點
 diamond() 繪製菱形點
 asterisk() 繪製星號點
 x() 繪製 X 標記
 cross() 繪製十字標記
 circle_cross() 圓形與十字混合標記
 circle_x() 圓形與 X 混合標記
 square_cross() 方形與十字混合標記
 square_x() 方形與 X 混合標記
 rect() 繪製矩形,可用於自訂長條圖
 vbar() 繪製垂直條狀圖
 hbar() 繪製水平條狀圖
 step() 繪製階梯圖
 patch() 繪製單個多邊形區域
 patches() 繪製多個多邊形
 hex() 六邊形點圖,常用於密度圖
 hex_tile() 六邊形平鋪圖
 quad() 四邊形,可用於 histogram 等
 ray() 繪製射線圖
 segment() 繪製線段(無箭頭)
 wedge() 繪製扇形,可用於圓餅圖
 annulus() 繪製圓環圖
 annular_wedge() 繪製環狀楔形圖
 image() 以灰階形式繪製圖像資料
 image_rgba() 以 RGBA 格式顯示影像
 image_url() 嵌入網路圖片
 text() 繪製文字標籤
 bezier() 繪製貝茲曲線
 quadratic() 繪製二次曲線
 arc() 繪製弧形
 multi_line() 繪製多條線
 multi_polygons() 繪製多層多邊形,可包含內洞


除了上表之繪圖相關方法外, 還有一些是用來設定圖表, 佈局, 互動或處理事件的屬性與方法, 如下表所示 :


 figure 物件輔助方法/屬性  說明
 add_layout() 新增圖例、標籤、顏色條、工具列等額外元素
 add_tools() 新增互動工具(如滑鼠放大、選取工具)
 on_event() 綁定事件處理器(例如滑鼠點擊)
 background_fill_color 設定圖表背景顏色
 border_fill_color 設定整個圖區外圍邊界顏色
 title 設定或取得圖表標題
 x_range / y_range 設定 X/Y 軸的範圍
 xaxis / yaxis 取得或設定軸物件
 xgrid / ygrid 取得或設定網格線
 legend 取得或設定圖例
 toolbar_location 設定工具列出現位置 : "above", "below", "left", None, "right" (預設)
 title_location 設定工具列出現位置 : "above"(預設), "below", "left", "right" 


其中 xgrid/ygrid 屬性分別是 X 軸與 Y 軸網格線 Grid 物件之串列, 每一個 Grid 物件都有一個 visible 屬性, 預設值為 True, 所以 Bokeh 繪製的圖預設都有網格線, 只要將其設為 False 即可取消網格線. 另外, figure() 函式與標題有關的參數只有 title, 標題位置必須用 figure 物件的 title_location 屬性來設定, 只有上下左右四個位置, 且上下是靠左, 不能置中. 

其次 figure 物件還有一個常用屬性 legend, 其值是一個 Legend 物件串列, 可以利用 Legend 物件的 location 設定圖例的位置, 可用位置如下表 :


legend.location 屬性值 說明
"top_left" 圖例顯示於圖表左上角
"top_center" 圖例顯示於圖表正上方中央
"top_right" 圖例顯示於圖表右上角 (預設)
"center_left" 圖例顯示於圖表正中央偏左
"center" 圖例顯示於圖表正中央
"center_right" 圖例顯示於圖表正中央偏右
"bottom_left" 圖例顯示於圖表左下角
"bottom_center" 圖例顯示於圖表正下方中央
"bottom_right" 圖例顯示於圖表右下角


例如 : 

fig.legend[0].location='top_left'   # 圖例位置
fig.legend[0].background_fill_color='#fefbd8'    # 圖例背景色
fig.legend.label_text_font_size='14pt'    # 圖例文字大小
fig.legend.label_text_color= 'red'            # 圖例文字顏色

xgrid/ygrid 屬性值為 Axis 軸物件, 可用來設定 X 軸與 Y 軸的外觀與行為, 例如調整字型, 旋轉標籤, 設定顏色, 隱藏顯示等, 常用的 Axis 物件屬性如下表 : 


 xaxis / yaxis Axis 物件屬性  說明
 axis_label  設定軸的標籤文字 (如 "時間", "價格")
 visible  控制軸是否顯示 (True / False)
 major_label_orientation  控制刻度文字方向,值可為 "horizontal"、"vertical" 或角度 (如 0.5)
 axis_line_color  設定軸線顏色,例如 "black"、"red"
 axis_line_width  設定軸線粗細 (單位為 px)
 major_tick_line_color  設定主刻度線顏色,若設為 None 表示不顯示
 minor_tick_line_color  設定次刻度線顏色,預設為灰色,可設為 None
 major_label_text_font_size  刻度文字大小,例如 "12pt"
 major_label_text_color  刻度文字顏色,例如 "gray"、"blue"
 axis_label_text_font_style  標籤文字樣式,可為 "normal"、"italic"、"bold"


例如 :

fig.xaxis.visible=False
fig.yaxis.visible=False

就會將 X 軸與 Y 軸隱藏起來了.


4. 用 Bokeh 繪製基本的互動圖表 : 

用 Bokeh 繪圖很簡單, 只要四個步驟 : 
  1. 匯入 bokeh.plotting 模組的 figure() 與 show() 函式
  2. 呼叫 figure() 函式建立 figure 物件
  3. 呼叫 figure 物件的繪圖方法
  4. 將 figure 物件傳給 show() 繪圖 
例如繪製折線圖是呼叫 line() 方法, 必要參數是 X, Y 軸資料, 參數結構如下 :


 fig.line() 參數  說明
 x, y  x、y 資料(必要),可為 list、NumPy array、Pandas Series 等
 line_color  線條顏色,如 "blue"、"#FF0000",預設為 "black"
 line_width  線條寬度(整數或浮點數),預設為 1
 line_dash  虛線樣式,如 "solid"、"dashed"、'dotted', 'dotdash', 'dashdot' 等
 line_alpha  線條透明度(0.0~1.0),預設為 1.0
 legend_label  圖例標籤文字,設置後圖例會自動顯示此線
 muted  是否初始為靜音(隱藏)狀態,配合 legend 的 mute 功能使用
 name  為這條圖形指定名稱,可在 `select()` 搜尋用
 source  提供一個 ColumnDataSource 作為資料來源


line_dash 參數也可以自訂虛線樣式, 方法是傳入一個整數串列, 其元素代表虛線中實線與空白的長度 (px), 例如 [6, 4] 表示 6px 實線, 4px 空白, 不斷重複. 

例如 : 


測試 1 : 預設無標記的折線圖 [看原始碼]   

# bokeh-line-test-1.py
from bokeh.plotting import figure, show

x=[0, 1, 2, 3, 4, 5]  # x 軸資料
y=[-5, -2, 6, 12, 4, 9]  # y 軸資料
fig=figure()  # 建立 figure 物件
fig.line(x, y)  # 繪製折線圖
show(fig)  # 顯示圖表

此例直接將 x, y 軸資料傳給 figure 物件的 line() 方法, 執行後預設會在目前工作目錄下產生一個與 Python 程式主檔名一樣的 .html 網頁檔, 並自動開啟瀏覽器顯示結果網頁 : 




可見圖片右上角有一排互動按鈕, 其中下載按鈕可將所繪製之圖片下載存成 png 檔. 

開啟工作目錄下的這個網頁檔會發現繪圖是由 bokeh-3.7.3.min.js 與一堆 JSON 資料完成的, 上面的 Python 程式主要就是生成這些 JSON 繪圖資料讓 bokeh-3.7.3.min.js 在畫布上繪圖 : 




下面範例在呼叫 figure() 時傳入 width, height, 與 tiltle 參數設定圖片尺寸與標題, 同時設定折線圖線段的寬度與顏色 :


測試 2 : 設定圖片尺寸與折線圖的線段顏色與粗細 [看原始碼]   

# bokeh-line-test-2.py
from bokeh.plotting import figure, show

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='red')  # 繪製折線圖
show(fig)  # 顯示圖表

結果如下 :




可見圖片尺寸從預設 600x600 變成 400x300 了, 圖表也有了標題 (預設位置為 left, 左上角), 且線段變成 4px 的紅色. 

下面範例測試畫布的 toolbar_location 與 background_fill_color 參數, titil_location 參數, line() 方法的線段類型 (虛線) 與透明度 : 


測試 3 : 設定圖片底色, 互動工具位置與線段類型, 透明度 [看原始碼]   

# bokeh-line-test-3.py
from bokeh.plotting import figure, show

x=[0, 1, 2, 3, 4, 5]  # x 軸資料
y=[-5, -2, 6, 12, 4, 9]  # y 軸資料
fig=figure(
    width=400,
    height=300,
    title='Bokeh 折線圖',
    toolbar_location='left',
    background_fill_color='ivory'
    )  # 建立 figure 物件
fig.title_location='below'  
fig.line(
    x,
    y,
    line_width=4,
    line_dash='dashed',
    line_alpha=0.5,
    legend_label='測試資料'
    )  # 繪製折線圖
show(fig)  # 顯示圖表

結果如下 :




可見互動工具變成放在左邊, 圖表標題改放置下方, 底色變成象牙色, 線段變成有透明度的虛線了.

下面範例測試將 fig.xgrid.visible/ fig.ygrid.visible 設為 False 取消網格線 :


測試 4 : 利用 fig.xgrid.visible/ fig.ygrid.visible 取消網格線 [看原始碼]   

# bokeh-line-test-4.py
from bokeh.plotting import figure, show

x=[0, 1, 2, 3, 4, 5]  # x 軸資料
y=[-5, -2, 6, 12, 4, 9]  # y 軸資料
fig=figure(
    width=400,
    height=300,
    title='Bokeh 折線圖',
    toolbar_location='below',
    background_fill_color='#ffff00'   # 黃色
    )  # 建立 figure 物件
fig.xgrid.visible=False # 取消 x 軸網格線
fig.line(
    x,
    y,
    line_width=4,
    line_dash='dotted',
    line_alpha=0.5,
    legend_label='測試資料'
    )  # 繪製折線圖
show(fig)  # 顯示圖表

此例僅取消 X 軸 (垂直) 網格線, 另外也將互動工具改放在底下, 虛線改為 dotted, 改用 16 進位色碼等, 結果如下 :



沒有留言 :