Python下使用Altair數(shù)據(jù)制圖
Altair 作為一個(gè) Python 數(shù)據(jù)制圖庫(kù),提供了優(yōu)雅的接口及自有的繪圖語(yǔ)言。
Python 中的 繪圖庫(kù) 提供了呈現(xiàn)數(shù)據(jù)的多種方式,可以滿足你不同的偏好,如靈活性、布局、易用性,或者特殊的風(fēng)格。
和其它方式相比,我發(fā)現(xiàn),Altair 提供的是一種不同的解決方案,且總體而言使用起來(lái)更為簡(jiǎn)單。得益于聲明式的繪圖語(yǔ)言 Vega,Altair 擁有一套優(yōu)雅的接口,可以直接定義要繪的圖應(yīng)該是什么樣子,而不是通過(guò)寫一大堆循環(huán)和條件判斷去一步步構(gòu)建。
繪圖流程
我通過(guò)繪制同一個(gè)多柱狀圖比較了多個(gè) Python 繪圖庫(kù)的差異。正式開(kāi)始之前,你需要將你的 Python 環(huán)境調(diào)整到能運(yùn)行下面代碼的狀態(tài)。具體就是:
演示用數(shù)據(jù)可從網(wǎng)絡(luò)下載,并且可以用 pandas 直接導(dǎo)入:
import pandas as pd
df = pd.read_csv('https://anvil.works/blog/img/plotting-in-python/uk-election-results.csv')
準(zhǔn)備開(kāi)始吧。為了做個(gè)比較,先看下面這個(gè)用 Matplotlib 做的圖:
Matplotlib UK election results
使用 Matplotlib 需要 16 行代碼,圖柱的位置需要自己計(jì)算。
使用 Altair 繪制相似的圖,代碼如下:
import altair as alt
chart = alt.Chart(df).mark_bar().encode(
x='party',
y='seats',
column='year',
color='party',
)
chart.save('altair-elections.html')
真是簡(jiǎn)潔多了!與 Seaborn 類似,Altair 所用數(shù)據(jù)的組織形式是每個(gè)變量一列(即 數(shù)據(jù)列 )。這種方式下可以將每個(gè)變量映射到圖的一個(gè)屬性上 —— Altair 稱之為“通道”。在上例中,我們期望每個(gè) “黨派” 在 x
軸上顯示為一組圖柱, 其 “席位” 顯示在 y
軸,且將圖柱按照 “年份” 分開(kāi)為 “列”。我們還想根據(jù) “黨派” 給圖柱使用不同的 “顏色”。用語(yǔ)言表述需求的話就是上面這個(gè)樣子,而這也正是代碼所要表述的!
現(xiàn)在把圖畫出來(lái):
Altair plot with default styling
調(diào)整樣式
這和我們期待的效果有點(diǎn)接近了。與 Matplotlib 方案相比,主要區(qū)別在于 Altair 方案中,每個(gè) year
組顯示的時(shí)候,內(nèi)部之間都有個(gè)小空白 —— 這不是問(wèn)題,這只是 Altair 多柱狀圖顯示的一個(gè)特性。
所以說(shuō)呢,還需要對(duì)顯示樣式再做一些改進(jìn)。
非整形數(shù)據(jù)
兩個(gè)不是整數(shù)的年份名稱(Feb 1974
和 Oct 1974
)顯示為 NaN
了。這可以通過(guò)將年份數(shù)值 year
轉(zhuǎn)換為字符串來(lái)解決:
df['year'] = df['year'].astype(str)
指定數(shù)據(jù)排序方法
還需要讓 Altair 知道如何對(duì)數(shù)據(jù)進(jìn)行排序。Altair 允許通過(guò)傳給它一個(gè) Column
對(duì)象,來(lái)設(shè)定 Column
通道的更多細(xì)節(jié)?,F(xiàn)在讓 Altair 按照數(shù)據(jù)在數(shù)據(jù)集中出現(xiàn)的順序排列:
chart = alt.Chart(df).mark_bar().encode(
# ...
column=alt.Column('year', sort=list(df['year']), title=None),
# ...
)
移除坐標(biāo)軸標(biāo)簽
我們通過(guò)設(shè)置 title=None
移除了圖頂?shù)?"year" 標(biāo)簽。下面再一處每列數(shù)據(jù)的 "party" 標(biāo)簽:
chart = alt.Chart(df).mark_bar().encode(
x=alt.X('party', title=None),
# ...
)
指定顏色圖
最后,我們還想自己指定圖柱的顏色。Altair 允許建立 domain
中數(shù)值與 range
中顏色的映射來(lái)實(shí)現(xiàn)所需功能,太貼心了:
cmap = {
'Conservative': '#0343df',
'Labour': '#e50000',
'Liberal': '#ffff14',
'Others': '#929591',
}
chart = alt.Chart(df).mark_bar().encode(
# ...
color=alt.Color('party', scale=alt.Scale(domain=list(cmap.keys()), range=list(cmap.values())))
)
樣式調(diào)整后的最終代碼
應(yīng)用上述樣式調(diào)整之后,代碼看起來(lái)不那么悅目了,但我們?nèi)匀皇怯寐暶鞯姆绞綄?shí)現(xiàn)的,這正是 Altair 如此有彈性的原因所在。實(shí)現(xiàn)過(guò)程中,仍然是使用的異于顯示數(shù)據(jù)的獨(dú)立變量來(lái)分離圖中不同屬性的,而不是像在 Matplotlib 中那樣直接對(duì)顯示數(shù)據(jù)做復(fù)雜的操作。唯一的不同是,我們的變量名字封裝在類似 alt.X()
的對(duì)象中,從而實(shí)現(xiàn)對(duì)顯示效果的控制:
import altair as alt
from votes import long as df
cmap = {
'Conservative': '#0343df',
'Labour': '#e50000',
'Liberal': '#ffff14',
'Others': '#929591',
}
df['year'] = df['year'].astype(str)
# We're still assigning, e.g. 'party' to x, but now we've wrapped it
# in alt.X in order to specify its styling
chart = alt.Chart(df).mark_bar().encode(
x=alt.X('party', title=None),
y='seats',
column=alt.Column('year', sort=list(df['year']), title=None),
color=alt.Color('party', scale=alt.Scale(domain=list(cmap.keys()), range=list(cmap.values())))
)
chart.save('altair-elections.html')
現(xiàn)在與 Matplotlib 方案扯平了,代碼數(shù)量達(dá)到了 16 行!
下圖是使用我們的樣式調(diào)整方案之后的 Altair 效果圖:
The Altair plot with our custom styling
結(jié)論
盡管在代碼數(shù)量上,使用 Altair 繪圖沒(méi)有表現(xiàn)出優(yōu)勢(shì),但它的聲明式繪圖語(yǔ)言使得對(duì)圖層的操控更為精密,這是我比較欣賞的。Altair 還提供了清晰而獨(dú)立的方式來(lái)調(diào)校顯示樣式,這使得 相關(guān)代碼與繪圖的代碼塊分離開(kāi)來(lái)。Altair 確實(shí)是使用 Python 繪圖時(shí)又一個(gè)很棒的工具庫(kù)。