使用 Bokeh 為你的 Python 繪圖新增互動性

2020-06-18 16:47:00

在 Bokeh 中繪圖比其他一些繪相簿要複雜一些,但付出額外的努力是有回報的。

在這一系列文章中,我通過在每個 Python 繪相簿中製作相同的多條形繪圖,來研究不同 Python 繪相簿的特性。這次我重點介紹的是 Bokeh(讀作 “BOE-kay”)。

Bokeh 中的繪圖比要複雜一些,但付出的額外努力是有回報的。Bokeh 的設計既允許你在 Web 上建立自己的互動式繪圖,又能讓你詳細控制互動性如何工作。我將通過給我在這個系列中一直使用的多條形圖新增工具提示來展示這一點。它繪製了 1966 年到 2020 年之間英國選舉結果的資料。

繪圖的放大檢視(©2019 年 Anvil

製作多條形圖

在我們繼續之前,請注意你可能需要調整你的 Python 環境來讓這段程式碼執行,包括以下:

  • 執行最新版本的 Python (在 LinuxMac 和 Windows 上的說明)
  • 確認你執行的 Python 版本能與這些庫一起工作。

資料可線上獲得,可以用 Pandas 匯入。

import pandas as pddf = pd.read_csv('https://anvil.works/blog/img/plotting-in-python/uk-election-results.csv')

現在我們可以繼續進行了。

為了做出多條形圖,你需要對你的資料進行一下調整。

原始資料是這樣的:

>> print(long)        year         party  seats0       1966  Conservative    2531       1970  Conservative    3302   Feb 1974  Conservative    2973   Oct 1974  Conservative    2774       1979  Conservative    339..       ...           ...    ...103     2005        Others     30104     2010        Others     29105     2015        Others     80106     2017        Others     59107     2019        Others     72[60 rows x 3 columns]

你可以把資料看成是每一個可能的 (year, party) 組合的一系列 seats 值。這正是 Bokeh 處理的方式。你需要做一個 (year, party) 元組的列表:

# 得到每種可能的 (year, party) 組合的元組x = [(str(r[1]['year']), r[1]['party']) for r in df.iterrows()]   # This comes out as [('1922', 'Conservative'), ('1923', 'Conservative'), ... ('2019', 'Others')]

這些將是 x 值。y 值就是席位(seats)。

y = df['seats']

現在你的資料看起來應該像這樣:

x                               y('1966', 'Conservative')        253('1970', 'Conservative')        330('Feb 1974', 'Conservative')    297('Oct 1974', 'Conservative')    277('1979', 'Conservative')        339 ...      ...                   ...('2005', 'Others')              30('2010', 'Others')              29('2015', 'Others')              80('2017', 'Others')              59('2019', 'Others')              72

Bokeh 需要你將資料封裝在它提供的一些物件中,這樣它就能給你提供互動功能。將你的 x 和 y 資料結構封裝在一個 ColumnDataSource 物件中。

    from bokeh.models import ColumnDataSource    source = ColumnDataSource(data={'x': x, 'y': y})

然後構造一個 Figure 物件,並傳入你用 FactorRange 物件封裝的 x 資料。

    from bokeh.plotting import figure    from bokeh.models import FactorRange       p = figure(x_range=FactorRange(*x), width=2000, title="Election results")

你需要讓 Bokeh 建立一個顏色表,這是一個特殊的 DataSpec 字典,它根據你給它的顏色對映生成。在這種情況下,顏色表是一個簡單的黨派名稱和一個十六進位制值之間的對映。

    from bokeh.transform import factor_cmap    cmap = {        'Conservative': '#0343df',        'Labour': '#e50000',        'Liberal': '#ffff14',        'Others': '#929591',    }    fill_color = factor_cmap('x', palette=list(cmap.values()), factors=list(cmap.keys()), start=1, end=2)

現在你可以建立條形圖了:

    p.vbar(x='x', top='y', width=0.9, source=source, fill_color=fill_color, line_color=fill_color)

Bokeh 圖表上資料的視覺化形式被稱為“字形glyphs”,因此你已經建立了一組條形字形。

調整圖表的細節,讓它看起來像你想要的樣子。

    p.y_range.start = 0    p.x_range.range_padding = 0.1    p.yaxis.axis_label = 'Seats'    p.xaxis.major_label_orientation = 1    p.xgrid.grid_line_color = None

最後,告訴 Bokeh 你現在想看你的繪圖:

   from bokeh.io import show   show(p)

這將繪圖寫入一個 HTML 檔案,並在預設的 Web 瀏覽器中開啟它。如下結果:

Bokeh 中的多條形繪圖(©2019年Anvil

它已經有了一些互動功能,比如盒子縮放。

Bokeh 內建的盒子縮放(©2019Anvil

但 Bokeh 的厲害之處在於你可以新增自己的互動性。在下一節中,我們通過在條形圖中新增工具提示來探索這個問題。

給條形圖新增工具提示

要在條形圖上新增工具提示,你只需要建立一個 HoverTool 物件並將其新增到你的繪圖中。

    h = HoverTool(tooltips=[        ('Seats', '@y'),        ('(Year, Party)', '(@x)')    ])    p.add_tools(h)

引數定義了哪些資料會顯示在工具提示上。變數 @y 和 @x 是指你傳入 ColumnDataSource 的變數。你還可以使用一些其他的值。例如,游標在圖上的位置由 $x 和 $y 給出(與 @x 和 @y 沒有關係)。

下面是結果:

選舉圖,現在帶有工具提示(© 2019 Anvil

借助 Bokeh 的 HTML 輸出,將繪圖嵌入到 Web 應用中時,你可以獲得完整的互動體驗。你可以在這裡把這個例子複製為 Anvil 應用(註:Anvil 需要註冊才能使用)。

現在,你可以看到付出額外努力在 Bokeh 中將所有資料封裝在 ColumnDataSource 等物件的原因了。作為回報,你可以相對輕鬆地新增互動性。

回歸簡單:Altair

Bokeh 是四大最流行的繪相簿之一,本系列將研究它們各自的特別之處

我也在研究幾個因其有趣的方法而脫穎而出的庫。接下來,我將看看 Altair,它的宣告式 API 意味著它可以做出非常複雜的繪圖,而不會讓你頭疼。