使用Plotly來(lái)簡(jiǎn)化Python中的數(shù)據(jù)可視化
Plotly 是一個(gè)數(shù)據(jù)繪圖庫(kù),具有整潔的接口,它旨在允許你構(gòu)建自己的 API。
Plotly 是一個(gè)繪圖生態(tài)系統(tǒng),可以讓你在 Python 以及 JavaScript 和 R 中進(jìn)行繪圖。在本文中,我將重點(diǎn)介紹使用 Python 庫(kù)進(jìn)行繪圖。
Plotly 有三種不同的 Python API,你可以選擇不同的方法來(lái)使用它:
- 類(lèi)似于 Matplotlib 的面向?qū)ο蟮?API
- 數(shù)據(jù)驅(qū)動(dòng)的 API,通過(guò)構(gòu)造類(lèi)似 JSON 的數(shù)據(jù)結(jié)構(gòu)來(lái)定義繪圖
- 類(lèi)似于 Seaborn 的高級(jí)繪圖接口,稱為 “Plotly Express” API
我將通過(guò)使用每個(gè) API 來(lái)繪制相同的圖來(lái)探索它們:英國(guó)大選結(jié)果的分組柱狀圖。
在我們進(jìn)一步探討之前,請(qǐng)注意,你可能需要調(diào)整你的 Python 環(huán)境來(lái)讓這段代碼運(yùn)行,包括以下內(nèi)容:
數(shù)據(jù)可在線獲得,可以用 Pandas 導(dǎo)入。
import pandas as pddf = pd.read_csv('https://anvil.works/blog/img/plotting-in-python/uk-election-results.csv')
現(xiàn)在我們可以繼續(xù)進(jìn)行了。
使用圖對(duì)象來(lái)繪制圖
Plotly 面向?qū)ο蟮?API 被稱為 graph_objects,它有點(diǎn)類(lèi)似于 Matplotlib 的面向?qū)ο?API。
要?jiǎng)?chuàng)建一個(gè)柱狀圖,你可以構(gòu)造一個(gè)包含四個(gè)柱狀圖的對(duì)象:
# 導(dǎo)入 Plotly 和數(shù)據(jù)import plotly.graph_objects as gofrom votes import wide as df# 得到 x 列表years = df['year']x = list(range(len(years)))# 定義繪圖bar_plots = [go.Bar(x=x, y=df['conservative'], name='Conservative', marker=go.bar.Marker(color='#0343df')),go.Bar(x=x, y=df['labour'], name='Labour', marker=go.bar.Marker(color='#e50000')),go.Bar(x=x, y=df['liberal'], name='Liberal', marker=go.bar.Marker(color='#ffff14')),go.Bar(x=x, y=df['others'], name='Others', marker=go.bar.Marker(color='#929591')),]# 指定樣式layout = go.Layout(title=go.layout.Title(text="Election results", x=0.5),yaxis_title="Seats",xaxis_tickmode="array",xaxis_tickvals=list(range(27)),xaxis_ticktext=tuple(df['year'].values),)# 繪制柱狀圖fig = go.Figure(data=bar_plots, layout=layout)# 告訴 Plotly 去渲染fig.show()
與 Matplotlib 不同的是,你無(wú)需手動(dòng)計(jì)算柱狀圖的 x 軸位置,Plotly 會(huì)幫你適配。
最終結(jié)果圖:
A multi-bar plot made using Graph Objects (© 2019 Anvil)
使用 Python 數(shù)據(jù)結(jié)構(gòu)來(lái)繪圖
你還可以使用 Python 基本數(shù)據(jù)結(jié)構(gòu)來(lái)定義繪圖,它與面對(duì)對(duì)象 API 具有相同的結(jié)構(gòu)。這直接對(duì)應(yīng)于 Plotly 的 JavaScript 實(shí)現(xiàn)的 JSON API。
# 定義繪圖數(shù)據(jù)fig = {'data': [{'type': 'bar', 'x': x, 'y': df['conservative'], 'name': 'Conservative', 'marker': {'color': '#0343df'}},{'type': 'bar', 'x': x, 'y': df['labour'], 'name': 'Labour', 'marker': {'color': '#e50000'}},{'type': 'bar', 'x': x, 'y': df['liberal'], 'name': 'Liberal', 'marker': {'color': '#ffff14'}},{'type': 'bar', 'x': x, 'y': df['others'], 'name': 'Others', 'marker': {'color': '#929591'}},],'layout': {'title': {'text': 'Election results', 'x': 0.5},'yaxis': {'title': 'Seats'},'xaxis': {'tickmode': 'array','tickvals': list(range(27)),'ticktext': tuple(df['year'].values),}}}# 告訴 Plotly 去渲染它pio.show(fig)
最終結(jié)果與上次完全相同:
A multi-bar plot made using JSON-like data structures (© 2019 Anvil)
使用 Plotly Express 進(jìn)行繪圖
Plotly Express 是對(duì)圖對(duì)象進(jìn)行封裝的高級(jí) API。
你可以使用一行代碼來(lái)繪制柱狀圖:
# 導(dǎo)入 Plotly 和數(shù)據(jù)import plotly.express as pxfrom votes import long as df# 定義顏色字典獲得自定義欄顏色cmap = {'Conservative': '#0343df','Labour': '#e50000','Liberal': '#ffff14','Others': '#929591',}# 生成圖fig = px.bar(df, x="year", y="seats", color="party", barmode="group", color_discrete_map=cmap)
這里使用了長(zhǎng)表 數(shù)據(jù),也稱為“整潔數(shù)據(jù)”。這些列代表年份、政黨和席位,而不是按政黨劃分。這與在 Seaborn 中制作柱狀圖非常相似。
>> print(long)year party seats0 1922 Conservative 3441 1923 Conservative 2582 1924 Conservative 4123 1929 Conservative 2604 1931 Conservative 470.. ... ... ...103 2005 Others 30104 2010 Others 29105 2015 Others 80106 2017 Others 59107 2019 Others 72[108 rows x 3 columns]
你可以訪問(wèn)底層的圖對(duì)象 API 進(jìn)行詳細(xì)調(diào)整。如添加標(biāo)題和 y 軸標(biāo)簽:
# 使用圖對(duì)象 API 來(lái)調(diào)整繪圖import plotly.graph_objects as gofig.layout = go.Layout(title=go.layout.Title(text="Election results", x=0.5),yaxis_title="Seats",)
最后,讓 Plotly 渲染:
fig.show()
這將在未使用的端口上運(yùn)行一個(gè)臨時(shí) Web 服務(wù)器,并打開(kāi)默認(rèn)的 Web 瀏覽器來(lái)查看圖像(Web 服務(wù)器將會(huì)馬上被關(guān)閉)。
不幸的是,結(jié)果并不完美。x 軸被視為整數(shù),因此兩組之間的距離很遠(yuǎn)且很小,這使得我們很難看到趨勢(shì)。
A multi-bar plot made using Plotly Express (© 2019 Anvil)
你可能會(huì)嘗試通過(guò)將 x 值轉(zhuǎn)換為字符串來(lái)使 Plotly Express 將其視為字符串,這樣它就會(huì)以均勻的間隔和詞法順序來(lái)繪制。不幸的是,它們的間隔還是很大,像在 graph_objects中那樣設(shè)置 xaxis_tickvals 也不行。
與 Seaborn 中的類(lèi)似示例不同,在這種情況下,抽象似乎沒(méi)有提供足夠的應(yīng)急方案來(lái)提供你想要的東西,但是也許你可以編寫(xiě)自己的 API?
構(gòu)建自己的 Plotly API
對(duì) Plotly 的操作方式不滿意?那就構(gòu)建自己的 Plotly API!
Plotly 的核心是一個(gè) JavaScript 庫(kù),它使用 D3 和 stack.gl 進(jìn)行繪圖。JavaScript 庫(kù)的接口使用指定的 JSON 結(jié)構(gòu)來(lái)繪圖。因此,你只需要輸出 JavaScript 庫(kù)喜歡使用的 JSON 結(jié)構(gòu)就好了。
Anvil 這樣做是為了創(chuàng)建一個(gè)完全在瀏覽器中工作的 Python Plotly API。
Plotly uses a JavaScript library to create plots, driven by libraries in other languages via JSON (© 2019 Anvil)
在 Anvil 版本中,你可以同時(shí)使用圖對(duì)象 API 和上面介紹的 Python 數(shù)據(jù)結(jié)構(gòu)方法。運(yùn)行完全相同的命令,將數(shù)據(jù)和布局分配給 Anvil 應(yīng)用程序中的 Plot 組件。
這是用 Anvil 的客戶端 Python API 繪制的多列柱狀圖:
# 導(dǎo)入 Anvil 庫(kù)from ._anvil_designer import EntrypointTemplatefrom anvil import *import anvil.server# 導(dǎo)入客戶端 Plotlyimport plotly.graph_objs as go# 這是一個(gè) Anvil 表單class Entrypoint(EntrypointTemplate):def __init__(self, **properties):# Set Form properties and Data Bindings.self.init_components(**properties)# 從服務(wù)器獲取數(shù)據(jù)data = anvil.server.call('get_election_data')# 獲取一個(gè)方便的 x 值列表years = data['year']x = list(range(len(years)))# 定義繪圖bar_plots = [go.Bar(x=x, y=data['conservative'], name='Conservative', marker=go.Marker(color='#0343df')),go.Bar(x=x, y=data['labour'], name='Labour', marker=go.Marker(color='#e50000')),go.Bar(x=x, y=data['liberal'], name='Liberal', marker=go.Marker(color='#ffff14')),go.Bar(x=x, y=data['others'], name='Others', marker=go.Marker(color='#929591')),]# 規(guī)定布局layout = {'title': 'Election results','yaxis': {'title': 'Seats'},'xaxis': {'tickmode': 'array','tickvals': list(range(27)),'ticktext': data['year'],},}# 生成多列柱狀圖self.plot_1.data = bar_plotsself.plot_1.layout = layout
繪圖邏輯與上面相同,但是它完全在 Web 瀏覽器中運(yùn)行,繪圖是由用戶計(jì)算機(jī)上的 Plotly JavaScript 庫(kù)完成的!與本系列的所有其它 Python 繪圖庫(kù)相比,這是一個(gè)很大的優(yōu)勢(shì)。因?yàn)槠渌?Python 庫(kù)都需要在服務(wù)器上運(yùn)行。
這是在 Anvil 應(yīng)用中運(yùn)行的交互式 Plotly 圖:
The election plot on the web using Anvil's client-side-Python Plotly library (© 2019 Anvil)
你可以復(fù)制此示例作為一個(gè) Anvil 應(yīng)用程序(注意:Anvil 需要注冊(cè)才能使用)。
在前端運(yùn)行 Plotly 還有另一個(gè)優(yōu)勢(shì):它為自定義交互行為提供了更多選項(xiàng)。
在 Plotly 中自定義交互
Plotly 繪圖不僅是動(dòng)態(tài)的,你可以自定義它們的互動(dòng)行為。例如,你可以在每個(gè)柱狀圖中使用 hovertemplate 自定義工具提示的格式:
go.Bar(x=x,y=df['others'],name='others',marker=go.bar.Marker(color='#929591'),hovertemplate='Seats: <b>%{y}</b>',),
當(dāng)你把這個(gè)應(yīng)用到每個(gè)柱狀圖時(shí),你會(huì)看到以下結(jié)果:
A multi-bar plot with custom tool-tips (© 2019 Anvil)
這很有用,當(dāng)你想要在某些事件發(fā)生時(shí)執(zhí)行任何你想要的代碼就更好了(例如,當(dāng)用戶將鼠標(biāo)懸停在欄上,你想要顯示一個(gè)相關(guān)選舉的信息框)。在 Anvil 的 Plotly 庫(kù)中,你可以將事件處理程序綁定到諸如懸停之類(lèi)的事件,這使得復(fù)雜的交互成為可能。
A multi-bar plot with a hover event handler (© 2019 Anvil)
你可以通過(guò)將方法綁定到繪圖的懸停事件來(lái)實(shí)現(xiàn):
def plot_1_hover(self, points, **event_args):"""This method is called when a data point is hovered."""i = points[0]['point_number']self.label_year.text = self.data['year'][i]self.label_con.text = self.data['conservative'][i]self.label_lab.text = self.data['labour'][i]self.label_lib.text = self.data['liberal'][i]self.label_oth.text = self.data['others'][i]url = f"https://en.wikipedia.org/wiki/{self.data['year'][i]}_United_Kingdom_general_election"self.link_more_info.text = urlself.link_more_info.url = url
這是一種相當(dāng)極端的交互性,從開(kāi)發(fā)人員的角度來(lái)看,也是一種極端的可定制性。這都要?dú)w功于 Plotly 的架構(gòu) —— 它有一個(gè)簡(jiǎn)潔的接口,明確的設(shè)計(jì)是為了讓你建立自己的API。如果到處都能看到這種偉大的設(shè)計(jì),那將會(huì)很有幫助!
使用 Bokeh 進(jìn)行自定義交互
現(xiàn)在你已經(jīng)了解了 Plotly 如何使用 JavaScript 來(lái)創(chuàng)建動(dòng)態(tài)圖,并且可以使用 Anvil 的客戶端編寫(xiě) Python 代碼在瀏覽器中實(shí)時(shí)編輯它們。
Bokeh 是另一個(gè) Python 繪圖庫(kù),它可以輸出可嵌入 Web 應(yīng)用程序的 HTML 文檔,并獲得與 Plotly 提供的功能類(lèi)似的動(dòng)態(tài)功能(如果你想知道如何發(fā)音,那就是 “BOE-kay”)。







































