cover

老師叫我學 Python ,學完才發現沒辦法做互動視覺化,怎麼辦?不要緊,讓 Bokeh 解救你!Bokeh 為一個 Python 套件,他提供了 Python 與 D3.js 之間的橋梁,卻又不需要親自下海寫 JavaScript,一起來看看 Bokeh 怎麼做到的吧!

網際網路 ( Internet ) 與全球資訊網 ( WWW ) 為我們提供了很棒的互動資訊傳播介面,然而這個介面有他獨特的編碼與規範,例如我們使用 HTML 做文件排版、 JavaScript 做互動設計… 隨著科技發展,網頁技術越來越發達,我們已經可以用 HTML + JavaScript 做到很複雜的互動視覺化了,而這也為其它程式語言設下了一個門檻 — 能跑在網頁裡的程式,目前只有 JavaScript !*

不過,這不代表我們就非得學會 JavaScript 才能做到互動圖表,比方說上禮拜我們介紹過的 Shiny  ( 請參考「R 也能互動!用 Shiny 與 R 語言將你的數據變成互動圖表」一文 ) 便能夠讓我們直接以 R 來開發網頁前端視覺化。

在 Python 中若要做到類似的效果,則有 Bokeh 這個套件可以幫我們完成。 Bokeh 為一個 Python 函式庫,提供了各式各樣的視覺化必須的輔助函式,同時也將網頁前端的技術細節包裝成一個個的 Python 函式與參數供我們呼叫,讓我們不再需要編輯 HTML 與 JavaScript 便能製作網頁前端視覺化。

( * 編按: Java 、 ActionScript 與 Basic / C 可以透過 Applet 、 Flash 與 ActiveX 等方式在瀏覽器中執行程式,但這些方式都已經逐漸被淘汰了。其它還有一些透過即時直譯的方式讓 ES6 、 Coffeescript 與 LiveScript 可以在瀏覽器中執行的方式,但都得要翻譯成 JavaScript 才能執行 )

安裝 Bokeh

( 若你尚未安裝 Python 、甚至正想學但還不會 Python — 我建議你可以先跳過安裝步驟,先將文章讀完,對 Bokeh 有個概略性的了解,再回頭來自我評估是否要投資時間在 Python 上。 )

若要安裝與使用 Bokeh ,我們必須要有 Python 執行環境以及 Python 套件管理程式 pip,並搭配虛擬環境管理工具 virtualenv 。我們會預設讀者們皆已準備好基本的開發環境,若你尚未有一個 python 的開發環境,可以參考下列資源來建立:

  • 安裝 Python — https://www.python.org/downloads/
  • 安裝 PIP — https://pip.pypa.io/en/stable/installing/
  • 安裝與使用 virtualenv — https://virtualenv.pypa.io/en/latest/installation.html

安裝完成並進入 virtualenv 以後,執行下列程式碼來安裝所需套件:

  pip install NumPy Jinja2 Six Requests Tornado PyYaml Pandas bokeh DateUtil

接著讓我們來測試看看 bokeh 是否順利安裝。新增一個 Python 檔案 test.py 並複製貼上下列程式碼:

  from bokeh.plotting import figure, output_file, show
  output_file("out.html")
  p = figure()
  p.line([1,2,3,4,5],[5,4,3,2,1])
  show(p)

執行 test.py 後,瀏覽器會自動開啟並顯示類似下圖的一張折線圖:

test

使用 Bokeh

上例是一個最簡單的 Bokeh 圖表程式。在 Bokeh 中,我們直接利用其提供的各種函式來繪製圖表。繪製完成後, Bokeh 則會依據我們的程式產生一對對應的 HTML 與 JavaScript 碼,並利用 Bokeh 在網頁上的函式庫「BokehJS」來產生圖表。因此以前面的程式碼為例,一個 Bokeh 程式的基本運作如下:

  1. 設定資料與輸出檔案
    • output_file(“out.html")
  2. 利用 Bokeh 繪製圖表
    • p = figure()
    • p.line([1,2,3,4,5], [5,4,3,2,1])
  3. 開啟產生的 HTML 檔 ( HTML + JavaScript ,自動生成 )
    1. show(p)

為了讓我們可以輕鬆又有彈性的製作各式圖表, Bokeh 將函式庫大略分成三組:

  1. bokeh.model — 製作圖表的基本元素,例如軸線、形狀等等。用來打造各種元件。
  2. bokeh.plotting — 為我們處理掉一些基本細節 ( 例如格點與軸線 ) ,但保留客製化的彈性。
  3. bokeh.charts — 直接使用各種完整圖表,例如長條圖、盒鬚圖等等。

Bokeh.charts

如果想快速掌握 bokeh 的運作,我們可以直接從 bokeh.plotting 來操作,不過 bokeh.charts 也提供了一些預先建置好的圖表供我們使用,下列為其中幾個範例:

  • bokeh.charts.Bar — 製作長條圖
  • bokeh.charts.BoxPlot — 製作盒鬚圖
  • bokeh.charts.HeatMap — 製作熱圖
  • bokeh.charts.Donut — 製作甜甜圈圖

使用也相當簡單,以甜甜圈圖為例,下列程式碼即為一個畫出甜甜圈圖的例子 ( 摘自 Bokeh User Guide ):

  from bokeh.charts import Donut, output_file, show
  output_file('donut.html')
  donut = Donut([[2., 5., 3.], [4., 1., 4.], [6., 4., 3.]], ['cpu1', 'cpu2', 'cpu3'])
  show(donut)

可以看到我們同樣要指定輸出檔以及呼叫「show」函式,但甜甜圈的細節則包裹在「Donut」函式之中,我們只要提供資料即可。下圖為上例的執行結果:

donut

Bokeh.charts 提供了十來種的預建圖表,而且大多有一些共通的設定項,如果想要了解更多的話,可以直接參考 Bokeh 「Using High-Level Charts 」的說明,這裡就不再贅述。

Bokeh.plotting

相較於 bokeh.charts 直接產生圖表又快又簡單的作法, bokeh.plotting 則是讓我們可以更自由的繪製圖表,但仍抽象化了一些細節;比方說,前例所見到的「figure」函式讓我們可以直接在圖表上做畫,但同時又幫我們設定好基本的格點與刻度。

「figure」本身已帶有許多輔助性的成員函式,用這些函式我們可以快速的製作一些最簡單的圖表,例如泡泡圖:

  from bokeh.plotting import figure, output_file, show
  p = figure(width=800,height=300)
  p.circle([1,2,3],[2,5,3],size=[10,20,30],color=["pink","olive","gold"])
  show(p)

這個例子使用「p.circle」來繪製圓圈,其中前兩個參數分別為 x 與 y 軸的座標點,後面則另外指定圓的大小及顏色。結果如下:

scatter

除了 circle 以外,figure 還提供各種不同的基本形狀讓我們畫圖,比方說下例使用了甜甜圈、十字、星星等形狀:

shapes

當然,除了簡單的形狀外,我們也可以繪制任意線條、區域等等, plotting 也提供像是圓餅或甜甜圈的快速繪製,所以其實利用 plotting 可以做到的不會只是簡單的散布圖,但若要做出比較進階的圖表,就需要撰寫更複雜的 Python 程式。

Bokeh  也為 plotting 提供各種樣式的設定,比方說線段的寬度、顏色、虛線樣式或端點的表現等等,若需要更進一步的樣式設定,可以參考「Styling Visual Attributes」一節了解更深入的設定方式。

網頁元件與互動圖表

如果只是畫畫基本圖表,那似乎沒有理由要使用 Bokeh。事實上,Bokeh 標榜的重點之一就是製作互動圖表的能力!Bokeh 提供了類似 Shiny 那樣的網頁元件及排版方式讓我們可以很輕鬆的利用 Python 程式碼來做出網頁上的互動元件,不同的是 Bokeh 還提供了 Custom JS 的前端接口,讓我們可以直接在 Python 中撰寫 JavaScript 來操縱前端元件,除了可以讓圖表有更快的反應之外,也節省了主機計算與網路頻寬的資源。下例為一個簡單的 CustomJS 片段:

  from bokeh.models.widgets import Slider
  callback = CustomJS(args=dict(source=source), code="""
    var data = source.get('data');
    var f = cb_obj.get('value');
    var x = data['x'], y = data['y'];
    for (i = 0; i < x.length; i++) y[i] = Math.pow(x[i], f);
    source.trigger('change');
  """)
  slider = Slider(start=0.1, end=4, value=1, step=.1, title="power", callback=callback)

在上例中可以看到,當使用者更動了 Slider 的數值, 以「CustomJS」定義的 callback 函式便會被執行,其中會依據資料的 X 值與 Slider 的當前值 ( cb_obj.get(“value") ) 重新計算 Y 的值,然後觸發資料變更事件 ( source.trigger(‘change’) ) ,讓 BokehJS 重新繪製圖表。

這些函式與 API 屬於 BokehJS 的範疇,雖然 Bokeh 與 BokehJS 是一套的,但其實 BokehJS 也可以用在 R 、 Julia 或 Ruby 等語言上,我們在這裡不深入去討論。

利用表單元件

上例中的 Slider 為我們提供了網頁的 Slider 元件,它長這個樣子:

slider

由於 Python 並沒有在網頁上執行的能力,這個其實是透過 HTML + Bootstrap 兜起來的元件。如同 Shiny , Bokeh 也提供了相當數量的表單元件,並且都可以讓我們用程式控制,下圖為其中幾種元件的截圖:

widget

這些元件都位於 bokeh.models.widgets 下,可以透過 import 語法將其匯入。

頁面排版

為了要排版這些元件與圖表, Bokeh 也提供了排版專用的工具,其中包含了下列幾種:

  • Horiontal Layout / 水平式版面 ( hplot )
  • Vertical Layout / 垂直式版面 ( vplot )
  • Grid Layout / 格式排版 ( gridplot )
  • Form Layout / 表單排版 (formplot )

使用的方式也相當簡單,我們只要把想要繪製的圖表或元件做為排版函式的參數即可,例如下例將 Slider 與線圖做水平排版:

  from bokeh.plotting import figure, output_file, show, hplot
  from bokeh.models.widgets import Slider
  output_file('layout.html')
  p = figure(width=400,height=200) # 建立圖表
  p.line([1,2,3,4,5],[5,4,3,2,1])
  slide = Slider()                 # 建立 Slider
  layout = hplot(p,slide)          # 將圖表與 Slider 利用 hplot 排版
  show(layout)

上述程式碼中的「hplot」即是做水平排版的函式,「 hplot(p, slide) 」幫我們將圖表與 Slider 放置在網頁的左右方,結果如下圖:

hplot

除了 hplot 之外,我們還可以用 gridplot 或 vplot 來做不同方向的排版,不過這幾個函式主要都是用在圖表,若要如上圖般安插表單元件,Bokeh 規定必須要再包一層「vform」這個函式,否則像上面這個例子中的 Slider 就只有裝飾的用途了。

互動圖表

透過表單元件與 CustomJS ,我們已經可以做到不少互動效果了,不過 Bokeh 還提供了一些直接透過設定的方式來實作互動效果。比方說,在設定圖表範圍時,若為多個圖表設定同一個範圍物件,那麼在使用者操作圖表時,所有圖表都會跟著移動;在下列程式碼中,我們利用第一個圖表 ( s1 ) 的 x_range 來設定後面兩張圖表的範圍:

  from bokeh.plotting import figure, gridplot, output_file, show

  s1 = figure(width=250,height=250)
  s1.circle([1,2,3,4], [1,2,3,4], size=10)

  s2 = figure(width=250, height=250, x_range=s1.x_range)
  s2.triangle([1,2,3,4],[4,3,2,1], size=10)

  s3 = figure(width=250, height=250, x_range=s1.x_range)
  s3.square([1,2,3,4],[4,2,1,3], size=10)

  p = gridplot([[s1, s2, s3]], toolbar_location=None)
  output_file("panning.html")
  show(p)

而因為他們共享了同一個範圍,在拉動任何一張圖表時,其它的圖表也會跟著動起來。下圖為上列程式碼產生的圖表,讀者可以自己動手拖動看看:

除了透過物件共享達成的互動以外, Bokeh 也提供了不少預先定義的行為供我們做設定,比方說在利用 plotting 繪圖時,我們可以設定「selection_color」指定資料點被選中時的顏色:

  p.circle([1,2,3,4,5],[5,4,3,2,1],selection_color="red")

這類的設定通常都會搭配一些使用者行為來定義,例如上例便是在使用者「選取」了資料後才會套用的屬性。Bokeh 預設提供了許多不同的「工具 ( tools ) 」讓我們建立起與使用者溝通的橋粱,比方說點選工具、圈選工具、平移縮放等等的工具;以 selection_color 為例,我們可以搭配「tap」工具來讓使用者可以點選,只要在 figure 函式中設定「tools」參數的值即可:

  from bokeh.plotting import figure, output_file, show
  output_file("out.html")
  p = figure(tools="tap")
  p.circle([1,2,3,4,5],[5,4,3,2,1],selection_color="red")
  show(p)

上列程式碼的執行結果如下,我們可以注意到其選單只剩下 tap 一個按鈕,而圖表無法拖動、但點選資料時該點會變成紅色:

Bokeh  提供若干種的互動工具,包含了套索選取工具、縮放平移工具與多邊形選取工具等等,就一般的使用情境下應該足夠使用了,我們在此不一一列舉出來,若你有興趣的話,可以參考 Bokeh 針對 Tools 所提供的說明文件

結語

由於能在前端網頁執行的程式語言種類不多,但網頁又是一個極佳的互動呈現介面,因此像是 Bokeh 、 Shiny 這類的前端互動輔助套件對於各種不同的語言來說幫助可說是很大,而為了包裝前端的細節,最後這些套件的架構都會很類似,至於誰比較優異就端看其設計細節了。

Bokeh 將大部份的細節保留在 bokeh.models 中,然後透過 bokeh.plotting 與 bokeh.charts 抽象化的方式讓我們得以很容易的上手,這個設計算是相當不錯的,不過很多細節變成是我們必須要完整讀過他的說明文件才能知道究竟我們還有什麼東西可以使用或設定,整體來說略嫌繁瑣;完全透過 Python 程式碼製作圖表與排版這點也讓視覺化的製作無法過於複雜。即便如此,若使用上手又沒有太大的客製化需求, Bokeh 仍可以讓我們很快產出一些有趣的圖表,再加上他本身的視覺設計就相當體面、不太需要調整,相信他對使用 Python 做資料分析與呈現的人來說仍是一個視覺化與圖表製作的好幫手。


Written by infographics.tw

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *