[譯] 利用 Python 中的 Bokeh 實現資料視覺化,第一部分:入門

Starrier發表於2019-03-03

提升你的視覺化遊資料

如果沒有有效的方法來傳達結果,那麼再複雜的統計分析也毫無意義。這一點我在最近的研究專案中深有體會,我們使用資料科學來提高建築能效。在過去的幾個月裡,我團隊成員中的一個人一直在致力於研究一種叫做 wavelet transforms,用於分析時間序列頻率成分的技術。該方法取得了積極的效果,但她在解釋過程中遇到了困難,所幸的是,她沒有迷失在技術細節中。

她很憤怒,問我能否用視覺表達來說明這種變換。我使用了叫做 gganimate 的 R 包,在幾分鐘之內製作了一個簡單的動畫,展示了該方法是如何轉換時間序列的。現在,我的團隊成員可以用這個讓人直觀地瞭解技術是如何工作的東西來取代費勁的語言描述。我的結論是,我們可以做最嚴格的分析,但在一天結束時,所有人都想看到的是一個 gif!雖然說這話是開玩笑,但它蘊含著一個道理:不能清楚地表達結果,就會對結果產生影響,而資料視覺化通常是展示分析結果的最佳方法。

可用於資料科學的資源正在迅速增加,在視覺化領域中尤為明顯,似乎每週都有一種新的嘗試。隨著這些技術的進步,它們逐漸出現了一個共同的趨勢:增加互動性。人們喜歡在靜態圖中檢視資料,但他們更喜歡的是使用資料,並利用這些資料來檢視引數的變化對結果的影響。在我的研究中,有一份報告是用來告訴業主通過改變他們的空調使用時間可以節省下多少度電,但如果給他們一個可以互動的表,他們就可以自己選擇不同的時間表,來觀察不用時間是如何影響用電的,這種方式更加有效。最近,受互動式繪圖趨勢的啟發,以及對不斷學習新工具的渴望,我一直在學習使用一個叫做 Bokeh 的 Python 庫。我為我的研究專案構建的儀表盤中顯示了 Bokeh 互動功能的一個示例:

[譯] 利用 Python 中的 Bokeh 實現資料視覺化,第一部分:入門

儘管我無法共享這個專案的整個程式碼,但我可以通過使用公開可用資料構建完全互動的 Bokeh 應用程式的示例。本系列文章將介紹使用 Bokeh 建立應用程式的整個過程。對於第一篇文章,我們將介紹 Bokeh 的基本元素,我們將在以後的文章中對其進行構建,在本系列文章中,我們將使用 nycflights13 資料集,該資料集有 2013 年以來超過 30 萬次航班的記錄。我們首先將重點放在視覺化單個變數上,在這種情況下,航班的延遲到達以分鐘為單位,我們將從構造一個基本的柱狀圖開始,這是顯示一個連續變數的擴充套件和位置的經典方法。完整的程式碼可以在 GitHub 檢視,第一個 Jupyter notebook 可以在這裡看到。這篇文章關注的是視覺效果,所以我鼓勵任何人檢視程式碼,如果他們想看到無聊但又必不可少資料清洗和格式化的步驟!

Bokeh 基礎

Bokeh 的主要概念是一次建立一個圖層。我們首先建立一個圖,然後向圖中新增名為 glyphs 的元素。(對於那些使用 ggplot 的人來說,glyphs 的概念與地理符號的想法本質上是一樣的,他們一次新增到一個“圖層”中。)根據所需的用途,glyphs 可以呈現多種形狀:圓形、線條、補丁、條形、弧形等。讓我們用正方形和圓形制作一個基本的圖來說明 glyphs 的概念。首先,我們使用 figure 方法繪製一個圖,然後通過呼叫適當的方法傳入資料,將我們的 glyphs 新增到繪圖中。最後,我們展示繪圖(我使用的是 Jupyter Notebook,如果你使用時呼叫的是 output_notebook,就會看到對應的繪圖)。

# bokeh 基礎
from bokeh.plotting import figure
from bokeh.io import show, output_notebook

# 建立帶標籤的空白圖
p = figure(plot_width = 600, plot_height = 600, 
           title = 'Example Glyphs',
           x_axis_label = 'X', y_axis_label = 'Y')

# 示例資料
squares_x = [1, 3, 4, 5, 8]
squares_y = [8, 7, 3, 1, 10]
circles_x = [9, 12, 4, 3, 15]
circles_y = [8, 4, 11, 6, 10]

# 新增方形 glyph
p.square(squares_x, squares_y, size = 12, color = 'navy', alpha = 0.6)
# 新增圓形 glyph
p.circle(circles_x, circles_y, size = 12, color = 'red')

# 設定為在筆記本中輸出情節
output_notebook()
# 顯示繪圖
show(p)
複製程式碼

這就形成了下面略顯平淡的繪圖:

[譯] 利用 Python 中的 Bokeh 實現資料視覺化,第一部分:入門

儘管在任何繪製相簿中,我們都可以很容易地製作這個圖表,但我們可以免費獲取一些工具,其中包含位於右側的 Bokeh 繪圖,包括 panning,縮放和繪圖儲存功能。這些工具是可配置的,當我們想研究我們的資料時,這些工具會派上用場。

我們現在開始展示我們的航班延遲資料。在跳轉到圖形之前,我們應該載入資料並對其進行簡短的檢查(粗體 為輸出程式碼):

# 將 CSV 中的資料讀入
flights = pd.read_csv('../data/flights.csv', index_col=0)

# 興趣欄的統計資料彙總
flights['arr_delay'].describe()

count    327346.000000
mean          6.895377
std          44.633292
min         -86.000000
25%         -17.000000
50%          -5.000000
75%          14.000000
max        1272.000000
複製程式碼

摘要統計資料為我們作出決策提供了資訊:我們有 327、346 次航班,最小延遲事件為 -86 分鐘,最大延遲事件為 1272 分鐘,令人震驚的 21 小時!75% 的分位數只有 14 分鐘,所以我們可以假設 1000 分鐘以上的數字可能是異常值(這並不意味著它們是非法的,只是極端的)。我會集中討論 -60 到 120 分鐘的延遲柱狀圖。

柱狀圖是單個變數初始視覺化的常見選擇,因為它顯示了分散式資料。x 位置是將變數分組成成為 bin 的間隔的值,每個條形的高度表示每個間隔資料點的計數(數目)。在我們的例子中,x 位置將代表以分鐘為單位的延遲到達,高度是對應的 bin 中的航班數。Bokeh 沒有內建的柱狀圖,但我們可以使用 quad glyph 來指定每個條形的底部、上、下、和右邊距。

要建立條形圖的資料,我們要使用 numpy[histogram](https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.histogram.html)function,它計算每個指定 bin 資料點的數值。我們使用 5 分鐘的長度作為函式將計算航班數在每五分鐘所花費的時間延誤。在生成資料之後,我們將其放入一個 pandas dataframe 來將所有的資料儲存在一個物件中。這裡的程式碼對於理解 Bokeh 並不是很重要,但鑑於 Numpy 和 pandas 在資料科學中的流行度,所以它還是有些用處的。

"""Bins will be five minutes in width, so the number of bins 
is (length of interval / 5). Limit delays to [-60, +120] minutes using the range."""

arr_hist, edges = np.histogram(flights['arr_delay'], 
                               bins = int(180/5), 
                               range = [-60, 120])

# 將資訊放入 dataframe
delays = pd.DataFrame({'arr_delay': arr_hist, 
                       'left': edges[:-1], 
                       'right': edges[1:]})
複製程式碼

我們的資料看起來像這樣:

[譯] 利用 Python 中的 Bokeh 實現資料視覺化,第一部分:入門

flights 列是從 leftright 的每個延遲間隔內飛行次數的計數。在這裡,我們可以生成一個新的 Bokeh 圖,並新增一個指定適當引數的 quad glpyh:

# 建立空白繪圖
p = figure(plot_height = 600, plot_width = 600, 
           title = 'Histogram of Arrival Delays',
          x_axis_label = 'Delay (min)]', 
           y_axis_label = 'Number of Flights')

# 新增一個 quad glphy
p.quad(bottom=0, top=delays['flights'], 
       left=delays['left'], right=delays['right'], 
       fill_color='red', line_color='black')

# 顯示繪圖
show(p)
複製程式碼

[譯] 利用 Python 中的 Bokeh 實現資料視覺化,第一部分:入門

生成此圖的大部分工作都是在資料格式化過程中進行的,這在資料科學中並不常見!從我們的繪圖中可以看出,延遲到達幾乎是正態分佈的,右側有一個輕微的正斜度或重尾巴

有更簡單的方法可以在 Python 中建立柱狀圖,也可以使用幾行 [matplotlib](https://en.wikipedia.org/wiki/Matplotlib) 來獲取相同的結果。但是,Bokeh 繪圖所帶來的的開發的好處在於,它可以提供將資料互動輕鬆地新增到圖形中的工具和方法。

新增互動性

我們將在本系列中討論的第一類互動是被動互動。這些是擁護可以採取的不改變顯示資料的操作。它們被稱為 inspectors,因為他們允許使用者檢視更詳細的“調查”資料。有用的 inspector 是當使用者滑鼠在資料點上移動並呼叫 Bokeh 中的懸停工具時,會出現工具提示。

[譯] 利用 Python 中的 Bokeh 實現資料視覺化,第一部分:入門

基礎的懸停工具提示

為了新增工具提示,我們需要將資料來源從 dataframe 中更改為來自 ColumnDataSource,Bokeh 中的一個關鍵概念。這是一個專門用於繪圖的物件,它包含資料以及方法和屬性。ColumnDataSource 允許我們在圖中新增註解和互動,也可以從 pandas dataframe 中進行構建。真實資料被儲存在字典中,可以通過 ColumnDataSource 的 data 屬性訪問。這裡,我們從資料來源進行建立源,並檢視資料字典中與 dataframe 列對應的鍵。

# 匯入 ColumnDataSource 類
from bokeh.models import ColumnDataSource

# 將 dataframe 轉換為 列資料來源
src = ColumnDataSource(delays)
src.data.keys()

dict_keys(['flights', 'left', 'right', 'index'])
複製程式碼

我們使用 CloumDataSource 新增 glyphs 時,我們將 CloumnDataSource 作為 source 引數傳入,並使用字串引用列名:

# 這次新增一個帶有源的 quad glyph
p.quad(source = src, bottom=0, top='flights', 
       left='left', right='right', 
       fill_color='red', line_color='black')
複製程式碼

請注意,程式碼如何引用特定的資料列,比如 ‘flights’、‘left’ 和 ‘right’,而不是像以前那樣使用 df['column'] 格式。

Bokeh 中的 HoverTool

一開始,HoverTool 的語法看上去會有些複雜,但經過實踐後,就會發現它們很容易建立。我們將 HoverTool 例項作為 tooltips 作為 Python 元組傳遞給它,其中第一個元素是資料的標籤,第二個元素引出我們要高亮顯示的特定資料。我們可以使用 ‘$’ 引用圖中任何屬性,例如 x 或 y 的位置,也可以使用 ‘@’ 引用源中特定欄位。這聽起來可能有點令人困惑,所以這裡有一個 HoverTool 的例子,我們在這兩方面都可以這麼做:

# 使用 @ 引用我們自己的資料欄位
# 使用 $ 在圖上的位置懸停工具
h = HoverTool(tooltips = [('Delay Interval Left ', '@left'),
                          ('(x,y)', '($x, $y)')])
複製程式碼

這裡,我們使用 ‘@’ 引用 ColumnDataSource(它對應於原始 dataframe 的 ‘left’ 列)中的 left 資料欄位,並使用 ‘$’ 引用游標的 (x,y) 位置。結果如下:

[譯] 利用 Python 中的 Bokeh 實現資料視覺化,第一部分:入門

顯示不同資料引用的懸停工具提示

(x,y) 位置上是滑鼠的位置,對我們的柱狀圖沒有太大的幫助,因為我們要找到給定條形中對應於條形頂部的飛行術。為了修復這個問題,我們將要修改我們的工具提示例項來引用正確的列。格式化工具提示中的資料顯示可能會讓人沮喪,因此我通常在 dataframe 中使用正確的格式建立另一列。例如,如果我希望我的工具提示顯示給定條的整個隔間,我會在資料框中建立一個格式化列:

# 新增一個列,顯示每個間隔的範圍
delays['f_interval'] = ['%d to %d minutes' % (left, right) for left, right in zip(delays['left'], delays['right'])]
複製程式碼

然後,我將 dataframe 轉換為 CloumnDataSource,並在 HoverTool 呼叫中訪問該列。下面的程式碼使用引用兩個格式化列的懸停工具建立繪圖,把那個將該工具新增到繪圖中。

# 建立一個空白繪圖
p = figure(plot_height = 600, plot_width = 600, 
           title = 'Histogram of Arrival Delays',
          x_axis_label = 'Delay (min)]', 
           y_axis_label = 'Number of Flights')

# 這次,新增帶有源的 quad glyph
p.quad(bottom=0, top='flights', left='left', right='right', source=src,
       fill_color='red', line_color='black', fill_alpha = 0.75,
       hover_fill_alpha = 1.0, hover_fill_color = 'navy')

# 新增引用格式化列的懸停工具
hover = HoverTool(tooltips = [('Delay', '@f_interval'),
                             ('Num of Flights', '@f_flights')])

# 繪圖樣式
p = style(p)

# 將懸停工具新增到圖中
p.add_tools(hover)

# 顯示繪圖
show(p)
複製程式碼

在 Bokeh 樣式中,我們以新增元素至原始的圖中來將元素新增到表中。請注意,在 p.quad glyph 呼叫中,有幾個額外的引數 hover_fill_alphahover_fill_color,當我們的滑鼠移動到條圖形時,這些引數會改變 glyph 的樣式。我還新增了 style 函式(可在筆記中檢視相關程式碼)。審美過程很無聊,所以通常我會寫一個應用於任何繪圖的函式。當我使用樣式時,我會保持簡單並專注於標籤的可讀性。繪圖的主要目的是顯示資料,新增不必要的元素只會降低繪圖的可用性!最後的繪圖如下所示:

[譯] 利用 Python 中的 Bokeh 實現資料視覺化,第一部分:入門

當我們的滑鼠滑過不同的詞條時,會得到該詞條精確的統計資料,它表示間隔以及在該間隔內飛行的次數。如果對繪圖比較滿意,可以將其儲存到 html 檔案中進行共享:

# 匯入儲存函式
from bokeh.io import output_file

# 指定輸出檔案並儲存
output_file('hist.html')
show(p)
複製程式碼

展望與總結

為了獲取 Bokeh 的工作流程,我製作了很多次繪圖,所以如果這看起來有很多東西要學的時候,不要擔心。在本系列教程中,我們將得到更多的練習!雖然Bokeh 看起來似乎有很多工作要做,但是當我們想要將我們的視覺效果擴充套件到簡單的靜態影象之外的時候,它的好處就不言而喻了。一旦我們有了基本的圖,我們就可以通過增加更多的元素來提高視覺效果。例如,如果我們想檢視航空公司的延遲到達,我們可以製作一個互動式圖,讓使用者選擇和比較航空公司。我們將把主動互動(那些更改顯示資料的互動)留到下一篇文章中,但下面是我們目前可以做的事情:

[譯] 利用 Python 中的 Bokeh 實現資料視覺化,第一部分:入門

主動互動需要編寫更多的指令碼,但這給了我們可以使用 Python 的機會!(如果有人想在下一篇文章之前看一下繪圖的程式碼可以在這裡進行檢視。)

在本系列文章中,我想強調的是,Boken 或者任何一個庫工具永遠都不會是滿足所有繪圖需求的一站式解決工具。Bokeh 允許使用者研究繪圖,但對於其他應用,像簡單的探索性資料分析matplotlib 這樣的輕量級庫可能會更高效。本系列旨在為你提供繪圖工具的另一種選擇,這需要更加需求來進行抉擇。你知道的庫越多,就越能高效地使用視覺化工具完成任務。

我一直以來都非常歡迎那些具有建設性的批評和反饋。你們可以在 Twitter @koehrsen_will 上聯絡到我。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章