Jupyter 常見可視化框架選擇

對於以Python做爲技術棧的數據科學工做者,Jupyter是不得不提的數據報告工具。可能對於R社區而言,鼎鼎大名的ggplot2是常見的可視化框架,而你們對於Python,以及Jupyter爲核心的交互式報告的可個視化方案就並無那麼熟悉。本文試圖比較幾個經常使用的解決方案,方便你們選擇。javascript

選擇標準

稱述式仍是命令式

數據工做者使用的圖的類別,常見的就三類:GIS可視化、網絡可視化和統計圖。所以,大多數場景下,咱們並不想接觸很是底層的基於點、線、面的命令,因此,選擇一個好的封裝的框架至關重要。html

固然,公認較好的封裝是基於《The Grammar of Graphics (Statistics and Computing)》一書,R中的ggplot2基本上就是一個很好的實現。咱們基本上能夠像用「天然語言」(Natural Language)同樣使用這些繪圖命令。咱們姑且採用計算機科學領域的「陳述式」來表達這種繪圖方式。前端

相反,有時候,如下情形時,咱們可能對於這種繪圖命令可能並不在乎:java

  1. 出圖至關簡單,要求繪製速度,通常大的框架較重(固然只是相對而言);
  2. 想要對細節作很是詳盡的微調,通常大框架在微調方面會相對複雜或者退縮成一句句命令;
  3. 是統計做圖可視化的創新者,想要嘗試作出新的可視化實踐。

這些狀況下,顯然,簡單操做式並提供底層繪製命令的框架更讓人愉快,與上面相似,咱們借用「命令式」描述這類框架。python

是否交互

與傳統的交付靜態圖標不一樣,基於Web端的Jupter的一大好處就是能夠繪製交互的圖標(最近的RNotebook也有實現),所以,是否選擇交互式,也是一個須要權衡的地方。git

交互圖的優點:github

  1. 能夠提供更多的數據維度和信息;
  2. 用戶端能夠作更多諸如放大、選取、轉存的操做;
  3. 能夠交付BI工程師相應的JavaScript代碼用以工程化;
  4. 效果上比較炫酷,考慮到報告接受者的特徵能夠選擇。

非交互圖的優點:json

  1. 報告文件直接導出成靜態文件時相對問題,不會由於轉換而損失信息;
  2. 圖片能夠與報告分離,必要時做爲其餘工做的成果;
  3. 不須要在運行Notebook時花不少世界載入各種前端框架。

是非內核交互

Jupyter上大多數命令經過如下方式獲取數據,而大多數繪圖方式事實上只是經過Notebook內的代碼在Notebook與內核交互後展現出輸出結果。但ipywidgets框架則能夠實現Code Cell中的代碼與Notebook中的前端控件(好比按鈕等)綁定來進行操做內核,提供不一樣的繪圖結果,甚至某些繪圖框架的每一個元素均可以直接和內核進行交互。前端框架

clipboard.png

用這些框架,能夠搭建更復雜的Notebook的可視化應用,但缺點是由於基於內核,因此在呈遞、展現報告時若是使用離線文件時,這些交互就會無效。網絡

框架羅列

matplotlib

最家喻戶曉的繪圖框架是matplotlib,它提供了幾乎全部python內靜態繪圖框架的底層命令。若是按照上面對可視化框架的分法,matplotlib屬於非交互式的的「命令式」做圖框架。

## matplotlib代碼示例
from pylab import *

X = np.linspace(-np.pi, np.pi, 256,endpoint=True)
C,S = np.cos(X), np.sin(X)

plot(X,C)
plot(X,S)

show()

clipboard.png

優勢是相對較快,底層操做較多。缺點是語言繁瑣,內置默認風格不夠美觀。

matplotlib在jupyter中須要一些配置,能夠展示更好的效果,詳情參見這篇文章.

ggplotplotnine

值得一說,對於R遷移過來的人來講,ggplotplotnine簡直是福音,基本克隆了ggplot2全部語法。橫向比較的話,plotnine的效果更好。這兩個繪圖包的底層依舊是matplotlib,所以,在引用時別忘了使用%matplotlib inline語句。值得一說的是plotnine也移植了ggplot2中良好的配置語法和邏輯。

## plotnine示例
(ggplot(mtcars, aes('wt', 'mpg', color='factor(gear)'))
 + geom_point()
 + stat_smooth(method='lm')
 + facet_wrap('~gear'))

clipboard.png

Seaborn

seaborn準確上說屬於matplotlib的擴展包,在其上作了許多很是有用的封裝,基本上能夠知足大部分統計做圖的需求,以matplotlib+seaborn基本能夠知足大部分業務場景,語法也更加「陳述式」。

缺點是封裝較高,基本上API不提供的圖就徹底不可繪製,對於各種圖的拼合也不適合;此外配置語句語法又迴歸「命令式」,相對複雜且不一致。

## seaborn示例
import seaborn as sns; sns.set(color_codes=True)
iris = sns.load_dataset("iris")
species = iris.pop("species")
g = sns.clustermap(iris)

clipboard.png

plotly

plotly是跨平臺JavaScript交互式繪圖包,因爲開發者的核心是javascript,因此整個語法相似於寫json配置,語法特質也介於「陳述式」和「命令式」之間,無服務版本是免費的。

有點是學習成本不高,能夠很快將語句移植到javascript版本;缺點是語言相對繁瑣。

##plotly示例
import plotly.plotly as py
import plotly.graph_objs as go

# Add data
month = ['January', 'February', 'March', 'April', 'May', 'June', 'July',
         'August', 'September', 'October', 'November', 'December']
high_2000 = [32.5, 37.6, 49.9, 53.0, 69.1, 75.4, 76.5, 76.6, 70.7, 60.6, 45.1, 29.3]
low_2000 = [13.8, 22.3, 32.5, 37.2, 49.9, 56.1, 57.7, 58.3, 51.2, 42.8, 31.6, 15.9]
high_2007 = [36.5, 26.6, 43.6, 52.3, 71.5, 81.4, 80.5, 82.2, 76.0, 67.3, 46.1, 35.0]
low_2007 = [23.6, 14.0, 27.0, 36.8, 47.6, 57.7, 58.9, 61.2, 53.3, 48.5, 31.0, 23.6]
high_2014 = [28.8, 28.5, 37.0, 56.8, 69.7, 79.7, 78.5, 77.8, 74.1, 62.6, 45.3, 39.9]
low_2014 = [12.7, 14.3, 18.6, 35.5, 49.9, 58.0, 60.0, 58.6, 51.7, 45.2, 32.2, 29.1]

# Create and style traces
trace0 = go.Scatter(
    x = month,
    y = high_2014,
    name = 'High 2014',
    line = dict(
        color = ('rgb(205, 12, 24)'),
        width = 4)
)
trace1 = go.Scatter(
    x = month,
    y = low_2014,
    name = 'Low 2014',
    line = dict(
        color = ('rgb(22, 96, 167)'),
        width = 4,)
)
trace2 = go.Scatter(
    x = month,
    y = high_2007,
    name = 'High 2007',
    line = dict(
        color = ('rgb(205, 12, 24)'),
        width = 4,
        dash = 'dash') # dash options include 'dash', 'dot', and 'dashdot'
)
trace3 = go.Scatter(
    x = month,
    y = low_2007,
    name = 'Low 2007',
    line = dict(
        color = ('rgb(22, 96, 167)'),
        width = 4,
        dash = 'dash')
)
trace4 = go.Scatter(
    x = month,
    y = high_2000,
    name = 'High 2000',
    line = dict(
        color = ('rgb(205, 12, 24)'),
        width = 4,
        dash = 'dot')
)
trace5 = go.Scatter(
    x = month,
    y = low_2000,
    name = 'Low 2000',
    line = dict(
        color = ('rgb(22, 96, 167)'),
        width = 4,
        dash = 'dot')
)
data = [trace0, trace1, trace2, trace3, trace4, trace5]

# Edit the layout
layout = dict(title = 'Average High and Low Temperatures in New York',
              xaxis = dict(title = 'Month'),
              yaxis = dict(title = 'Temperature (degrees F)'),
              )

fig = dict(data=data, layout=layout)
py.iplot(fig, filename='styled-line')

clipboard.png

注意:此框架在jupyter中使用須要使用init_notebook_mode()加載JavaScript框架。

bokeh

bokehpydata維護的比較具備潛力的開源交互可視化框架。

值得一說的是,該框架同時提供底層語句和「陳述式」繪圖命令。相對來講語法也比較清楚,但其配置語句依舊有不少可視化框架的問題,就是與「陳述式」命令不符,沒有合理的結構。此外,一些常見的交互效果都是以底層命令的方式使用的,所以若是要快速實現Dashboard或者做圖時就顯得較爲不便了。

## Bokeh示例
import numpy as np
import scipy.special

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

p1 = figure(title="Normal Distribution (μ=0, σ=0.5)",tools="save",
            background_fill_color="#E8DDCB")

mu, sigma = 0, 0.5

measured = np.random.normal(mu, sigma, 1000)
hist, edges = np.histogram(measured, density=True, bins=50)

x = np.linspace(-2, 2, 1000)
pdf = 1/(sigma * np.sqrt(2*np.pi)) * np.exp(-(x-mu)**2 / (2*sigma**2))
cdf = (1+scipy.special.erf((x-mu)/np.sqrt(2*sigma**2)))/2

p1.quad(top=hist, bottom=0, left=edges[:-1], right=edges[1:],
        fill_color="#036564", line_color="#033649")
p1.line(x, pdf, line_color="#D95B43", line_width=8, alpha=0.7, legend="PDF")
p1.line(x, cdf, line_color="white", line_width=2, alpha=0.7, legend="CDF")

p1.legend.location = "center_right"
p1.legend.background_fill_color = "darkgrey"
p1.xaxis.axis_label = 'x'
p1.yaxis.axis_label = 'Pr(x)'



p2 = figure(title="Log Normal Distribution (μ=0, σ=0.5)", tools="save",
            background_fill_color="#E8DDCB")

mu, sigma = 0, 0.5

measured = np.random.lognormal(mu, sigma, 1000)
hist, edges = np.histogram(measured, density=True, bins=50)

x = np.linspace(0.0001, 8.0, 1000)
pdf = 1/(x* sigma * np.sqrt(2*np.pi)) * np.exp(-(np.log(x)-mu)**2 / (2*sigma**2))
cdf = (1+scipy.special.erf((np.log(x)-mu)/(np.sqrt(2)*sigma)))/2

p2.quad(top=hist, bottom=0, left=edges[:-1], right=edges[1:],
        fill_color="#036564", line_color="#033649")
p2.line(x, pdf, line_color="#D95B43", line_width=8, alpha=0.7, legend="PDF")
p2.line(x, cdf, line_color="white", line_width=2, alpha=0.7, legend="CDF")

p2.legend.location = "center_right"
p2.legend.background_fill_color = "darkgrey"
p2.xaxis.axis_label = 'x'
p2.yaxis.axis_label = 'Pr(x)'



p3 = figure(title="Gamma Distribution (k=1, θ=2)", tools="save",
            background_fill_color="#E8DDCB")

k, theta = 1.0, 2.0

measured = np.random.gamma(k, theta, 1000)
hist, edges = np.histogram(measured, density=True, bins=50)

x = np.linspace(0.0001, 20.0, 1000)
pdf = x**(k-1) * np.exp(-x/theta) / (theta**k * scipy.special.gamma(k))
cdf = scipy.special.gammainc(k, x/theta) / scipy.special.gamma(k)

p3.quad(top=hist, bottom=0, left=edges[:-1], right=edges[1:],
        fill_color="#036564", line_color="#033649")
p3.line(x, pdf, line_color="#D95B43", line_width=8, alpha=0.7, legend="PDF")
p3.line(x, cdf, line_color="white", line_width=2, alpha=0.7, legend="CDF")

p3.legend.location = "center_right"
p3.legend.background_fill_color = "darkgrey"
p3.xaxis.axis_label = 'x'
p3.yaxis.axis_label = 'Pr(x)'



p4 = figure(title="Weibull Distribution (λ=1, k=1.25)", tools="save",
            background_fill_color="#E8DDCB")

lam, k = 1, 1.25

measured = lam*(-np.log(np.random.uniform(0, 1, 1000)))**(1/k)
hist, edges = np.histogram(measured, density=True, bins=50)

x = np.linspace(0.0001, 8, 1000)
pdf = (k/lam)*(x/lam)**(k-1) * np.exp(-(x/lam)**k)
cdf = 1 - np.exp(-(x/lam)**k)

p4.quad(top=hist, bottom=0, left=edges[:-1], right=edges[1:],
       fill_color="#036564", line_color="#033649")
p4.line(x, pdf, line_color="#D95B43", line_width=8, alpha=0.7, legend="PDF")
p4.line(x, cdf, line_color="white", line_width=2, alpha=0.7, legend="CDF")

p4.legend.location = "center_right"
p4.legend.background_fill_color = "darkgrey"
p4.xaxis.axis_label = 'x'
p4.yaxis.axis_label = 'Pr(x)'



output_file('histogram.html', title="histogram.py example")

show(gridplot(p1,p2,p3,p4, ncols=2, plot_width=400, plot_height=400, toolbar_location=None))

clipboard.png

bqplot

bqplot是基於ipywidgetsd3.js組合發展的內核交互式的可視化框架。語法上採用了和matplotlib大體一致的語法已經相對封裝較高的「陳述式語法」。優勢是直接和內核交互,可使用大量控件來實現更多的圖像處理,缺點也是直接的,離線文檔則不會顯示任何圖案、控件也都失效。

## bqplot示例
import numpy as np
from IPython.display import display
from bqplot import (
    OrdinalScale, LinearScale, Bars, Lines, Axis, Figure
)

size = 20
np.random.seed(0)

x_data = np.arange(size)

x_ord = OrdinalScale()
y_sc = LinearScale()

bar = Bars(x=x_data, y=np.random.randn(2, size), scales={'x': x_ord, 'y':
y_sc}, type='stacked')
line = Lines(x=x_data, y=np.random.randn(size), scales={'x': x_ord, 'y': y_sc},
             stroke_width=3, colors=['red'], display_legend=True, labels=['Line chart'])

ax_x = Axis(scale=x_ord, grid_lines='solid', label='X')
ax_y = Axis(scale=y_sc, orientation='vertical', tick_format='0.2f',
            grid_lines='solid', label='Y')

Figure(marks=[bar, line], axes=[ax_x, ax_y], title='API Example',
       legend_location='bottom-right')

clipboard.png

其餘特殊需求的做圖

除了統計做圖,網絡可視化和GIS可視化也是很經常使用的,在此只作一個簡單的羅列:

GIS類:

  • gmap:交互,使用google maps接口
  • ipyleaflet:交互,使用leaflet接口

網絡類:

  • networkx:底層爲matplotlib
  • plotly

總結

底層實現 交互方式 語法 語言結構 備註 推薦程度
matplotlib - 命令式 底層語言 能夠實現複雜底層操做 ★★★
gglot matplotlib 陳述式 ggplot2 建議選擇plotnine ★★
plotnine matplotlib 陳述式 ggplot2 徹底移植ggplot2 ★★★★★
seaborn matplotlib 陳述式 高級語言 有不少有用的統計圖類的封裝;但不適合作圖拼裝 ★★★★★
plotly plotly.js 前端交互 介於命令式和陳述式之間 相似JavaScript 語法相似於json配置 ★★★★
bokeh - 前端交互 命令、陳述式 同時有底層語言和高級語言 社區具備潛力 ★★★
bqplot d3.js 內核交互 命令、陳述式 有相似matplotlib底層語言,已經封裝好的高級語言 內核交互 ★★★★
相關文章
相關標籤/搜索