本篇旨在測試 Altair 套件的長條圖繪製方法 chart.make_bar().
本系列之前一篇文章 :
摘要說明 Altair 繪製圖表的六個步驟 :
- 建立資料 : DataFrame
- 建立圖表物件 : 呼叫建構式 Chart() 並傳入 DataFrame
- 指定圖表類型 : 例如折線圖 mark_line()
- 指定欄位如何呈現 : 呼叫 encode() 方法
- 設定外觀屬性 : 呼叫 properties() 方法
- 設定互動性 : 呼叫 interactive() 方法
從第二步建立 Chart 物件後, 每一個方法都會傳回更新 JSON 設定後的 Chart 物件, 故這些方法可使用鏈式呼叫.
繪製長條圖第三步驟要呼叫 Chart 物件的 mark_bar() 方法, 其常用參數如下表所示 :
| mark_bar() 參數 | 說明 |
|---|---|
| color | 設定長條的顏色,可為 CSS 色碼(如 'red' 或 '#ff0000')或欄位名稱 |
| opacity | 長條透明度,介於 0(完全透明)到 1(不透明)之間 |
| size | 長條寬度(水平長條為高度),預設依欄位自動計算 (也可指定 px) |
| cornerRadius | 設定長條的四角圓弧程度(單一值套用四角,半徑 px) |
| cornerRadiusTopLeft cornerRadiusTopRight cornerRadiusBottomLeft cornerRadiusBottomRight |
分別設定長條四個角的圓弧大小 (半徑 px) |
| stroke | 長條邊框顏色 |
| strokeWidth | 長條邊框寬度(像素) |
| strokeOpacity | 長條邊框透明度 |
例如 :
測試 1 : 預設的長條圖 [看原始碼]
# altair-bar-test-1.py
import altair as alt
import pandas as pd
# 建立資料
df=pd.DataFrame({
'x': ['1月', '2月', '3月', '4月', '5月'],
'y': [120000, 135000, 99000, 150000, 170000]
})
# 建立繪製折線圖的 Chart 物件
chart=alt.Chart(df).mark_bar().encode(
x='x',
y='y',
).properties(
width=400, # 圖片寬 (px)
height=300, # 圖片高 (px)
title='Altair 長條圖' # 圖片標題
)
chart.save('altair-bar-test-1.html')
結果如下 :
但是如果將 X 軸改為全中文, 排序就會因為 Unicode 問題而亂掉, 例如 :
測試 2 : X 軸類別資料的 Unicode 排序問題 [看原始碼]
# altair-bar-test-2.py
import altair as alt
import pandas as pd
# 建立資料
df=pd.DataFrame({
'x': ['一月', '二月', '三月', '四月', '五月'],
'y': [120000, 135000, 99000, 150000, 170000]
})
# 建立繪製折線圖的 Chart 物件
chart=alt.Chart(df).mark_bar().encode(
x='x',
y='y',
).properties(
width=400, # 圖片寬 (px)
height=300, # 圖片高 (px)
title='Altair 長條圖' # 圖片標題
)
chart.save('altair-bar-test-2.html')
此例將 X 軸內容改為全中文, 結果 X 軸順序並非預期的照一月, 二月, ... 排列, 三月在二月前面, 五月在四月前面 :
因為 Altair 是基於 Vega-Lite 的宣告式視覺化系統, 它會將名目 (Nominal) 資料視為無序, 然後根據內部排序 Unicode 字典排序機制來排序, 於是 X 軸順序變成 :
'一' (U+4E00) → '三' (U+4E09) → '二' (U+4E8C) → '五' (U+4E94) → '四' (U+56DB)
解決辦法是在 encode() 中用 X 軸的軸物件 alt.X() 用 sort 參數列舉正確順序, 例如 :
測試 3 : 用軸物件 alt.X() 的 sort 參數列舉名目資料的順序 [看原始碼]
# altair-bar-test-3.py
import altair as alt
import pandas as pd
# 建立資料
df=pd.DataFrame({
'x': ['一月', '二月', '三月', '四月', '五月'],
'y': [120000, 135000, 99000, 150000, 170000]
})
# 建立繪製折線圖的 Chart 物件
chart=alt.Chart(df).mark_bar().encode(
x=alt.X('x', sort=['一月', '二月', '三月', '四月', '五月']), # 列舉 X 軸順序
y='y',
).properties(
width=400, # 圖片寬 (px)
height=300, # 圖片高 (px)
title='Altair 長條圖' # 圖片標題
)
chart.save('altair-bar-test-3.html')
結果如下 :
這樣 X 軸順序就正確了. 另一個方法是用 Pandas 的 pd.Categorical() 函式將 X 軸由名目型 (Nominal) 資料改為類別型資料 (Categorical), 這樣 Altair 就會遵循該類型之順序, 如果 X 軸資料很多不勝列舉, 這方法最方便, 例如 :
測試 4 : 將 X 軸由名目型資料改為類別型資料 [看原始碼]
# altair-bar-test-4.py
import altair as alt
import pandas as pd
# 建立資料
df=pd.DataFrame({
'x': ['一月', '二月', '三月', '四月', '五月'],
'y': [120000, 135000, 99000, 150000, 170000]
})
df['x']=pd.Categorical(df['x'], categories=df['x'].tolist(), ordered=True) # 改為類別型資料
# 建立繪製折線圖的 Chart 物件
chart=alt.Chart(df).mark_bar().encode(
x='x', # 將資料集中的欄位 'x' 對應到 X 軸的視覺通道
y='y',
).properties(
width=400, # 圖片寬 (px)
height=300, # 圖片高 (px)
title='Altair 長條圖' # 圖片標題
)
chart.save('altair-bar-test-4.html')
此例呼叫 pd.Categorical() 先將 X 軸由名目型資料改為類別型資料, 結果與上例一樣 X 軸排序正確.
軸標籤預設是資料欄位名稱 x, y, 下列範例透過直接更改欄位名稱來改變軸標籤, 同時測試直條的 color, size, opacity, 以及邊框的三個參數 :
測試 5 : 修改欄位名稱與測試直條的 color, size, opacity, 與邊框的三個參數 [看原始碼]
# altair-bar-test-5.py
import altair as alt
import pandas as pd
# 建立資料
df=pd.DataFrame({
'月份': ['一月', '二月', '三月', '四月', '五月'],
'營收': [120000, 135000, 99000, 150000, 170000]
})
# 建立繪製折線圖的 Chart 物件
chart=alt.Chart(df).mark_bar(
color='orange', # 設定長條顏色
size=30, # 設定長條寬度 px
opacity=0.5, # 設定長條透明度
stroke='blue', # 設定邊框顏色
strokeWidth=2, # 設定邊框寬度 px
strokeOpacity=0.5 # 設定邊框透明度
).encode(
x=alt.X('月份', sort=['一月', '二月', '三月', '四月', '五月']), # 列舉 X 軸順序
y='營收',
).properties(
width=400, # 圖片寬 (px)
height=300, # 圖片高 (px)
title='Altair 長條圖' # 圖片標題
)
chart.save('altair-bar-test-5.html')
此例修改了 DataFrame 的欄位名稱, 由 'x' 與 'y' 改為 '月份' 與 '營收', 軸標籤預設就是欄位名稱, 另外也設定了長寬寬度為 30 px, 以及邊框的樣式, 結果如下 :
長條的四個邊角可以用 cornerRadius 參數一次設定四個邊角圓弧半徑, 例如 :
測試 6 : 用 cornerRadius 參數一次設定四個邊角圓弧半徑 [看原始碼]
# altair-bar-test-6.py
import altair as alt
import pandas as pd
# 建立資料
df=pd.DataFrame({
'月份': ['一月', '二月', '三月', '四月', '五月'],
'營收': [120000, 135000, 99000, 150000, 170000]
})
# 建立繪製折線圖的 Chart 物件
chart=alt.Chart(df).mark_bar(
color='rgba(0, 255, 255, 0.5)', # 設定長條顏色
size=50, # 設定長條寬度 px
stroke='navy', # 設定邊框顏色
cornerRadius=10 # 設定長條四個邊角圓弧半徑 px
).encode(
x=alt.X('月份', sort=['一月', '二月', '三月', '四月', '五月']), # 列舉 X 軸順序
y='營收',
).properties(
width=400, # 圖片寬 (px)
height=300, # 圖片高 (px)
title='Altair 長條圖' # 圖片標題
)
chart.save('altair-bar-test-6.html')
結果如下 :
也可以用 cornerRadiusTopLeft, cornerRadiusTopRight, cornerRadiusBottomLeft, 與 cornerRadiusBottomRight 這四個參數分別設定四個邊角的圓弧半徑, 例如 :
測試 7 : 分別設定四個邊角圓弧半徑 [看原始碼]
# altair-bar-test-7.py
import altair as alt
import pandas as pd
# 建立資料
df=pd.DataFrame({
'月份': ['一月', '二月', '三月', '四月', '五月'],
'營收': [120000, 135000, 99000, 150000, 170000]
})
# 建立繪製折線圖的 Chart 物件
chart=alt.Chart(df).mark_bar(
color='cyan', # 設定長條顏色
size=50, # 設定長條寬度 px
cornerRadiusTopLeft=10,
cornerRadiusTopRight=10,
cornerRadiusBottomLeft=5,
cornerRadiusBottomRight=5
).encode(
x=alt.X('月份', sort=['一月', '二月', '三月', '四月', '五月']), # 列舉 X 軸順序
y='營收',
).properties(
width=400, # 圖片寬 (px)
height=300, # 圖片高 (px)
title='Altair 長條圖' # 圖片標題
)
chart.save('altair-bar-test-7.html')
結果如下 :
前面的三個範例我們直接修改 DataFrame 欄位名稱來設定軸標籤為 '月份' 與 '營收', 下面範例則是要在呼叫 encode() 時利用 Axis 軸物件的 title 屬性來設定 :
測試 8 : 用軸物件 Axis 設定 X, Y 軸標籤 [看原始碼]
# altair-bar-test-8.py
import altair as alt
import pandas as pd
# 建立資料
df=pd.DataFrame({
'x': ['一月', '二月', '三月', '四月', '五月'],
'y': [120000, 135000, 99000, 150000, 170000]
})
# 建立繪製折線圖的 Chart 物件
chart=alt.Chart(df).mark_bar(
size=50, # 設定直條寬度
color='purple'
).encode(
x=alt.X(
'x',
sort=['一月', '二月', '三月', '四月', '五月'], # 列舉 X 軸順序
axis=alt.Axis(title='月份') # 設定 X 軸標籤
),
y=alt.X(
'y',
axis=alt.Axis(title='營收(元)') # 設定 Y 軸標籤
)
).properties(
width=400, # 圖片寬 (px)
height=300, # 圖片高 (px)
title='Altair 長條圖' # 圖片標題
)
chart.save('altair-bar-test-8.html')
此例 DataFrame 使用 'x' 與 'y' 為欄位名稱, 在 encode() 中用 alt.X() 與 alt.Y() 對應 x, y 軸到視覺通道時傳入 axis 參數, 其值為呼叫 alt.Axis() 建立之軸物件, 只要傳入 title 參數即可設定軸標籤, 結果如下 :
除了 title 外, alt.Axis() 建構式還有其他參數可設定軸標籤樣式, 如下表所示 :
| 參數名稱 | 說明 |
|---|---|
| title | 軸的標籤文字(預設為欄位名) |
| titleFontSize | 標籤文字的字型大小 |
| titleColor | 標籤文字顏色 |
| labelAngle | 軸刻度文字旋轉角度(如 -45) |
| labelFontSize | 軸刻度文字的字型大小 |
| labelColor | 軸刻度文字顏色 |
| grid | 是否顯示格線(布林值) |
| format | 指定數值格式(如 ".2f" 表示小數兩位) |
| orient | 軸的位置,如 "bottom"、"top"、"left"、"right" |
| ticks | 顯示的刻度數量 |
| zindex | 控制圖層順序(數字愈大圖層愈上方) |
例如 :
測試 9 : 用 Axis 軸物件的參數設定軸物件樣式 [看原始碼]
# altair-bar-test-9.py
import altair as alt
import pandas as pd
# 建立資料
df=pd.DataFrame({
'x': ['一月', '二月', '三月', '四月', '五月'],
'y': [120000, 135000, 99000, 150000, 170000]
})
# 建立繪製折線圖的 Chart 物件
chart=alt.Chart(df).mark_bar(
size=50, # 設定直條寬度
color='purple'
).encode(
x=alt.X(
'x',
sort=['一月', '二月', '三月', '四月', '五月'], # 列舉 X 軸順序
axis=alt.Axis(
title='月份',
titleFontSize=16, # 設定軸標籤文字字型大小
titleColor='blue', # 設定軸標籤文字顏色
labelAngle=-45, # 設定軸刻度文字旋轉角度
labelFontSize=12 # 設定軸刻度字型大小
) # 設定 X 軸標籤
),
y=alt.X(
'y',
axis=alt.Axis(
title='營收(元)',
titleFontSize=16, # 設定軸標籤文字字型大小
titleColor='blue', # 設定軸標籤文字顏色
labelAngle=-45, # 設定軸刻度文字旋轉角度
labelFontSize=12 # 設定軸刻度字型大小
) # 設定 Y 軸標籤
)
).properties(
width=400, # 圖片寬 (px)
height=300, # 圖片高 (px)
title='Altair 長條圖' # 圖片標題
)
chart.save('altair-bar-test-9.html')
結果如下 :
最後來測試在 Altair 要如何繪製兩組 Y 軸資料的群組化 (group) 長條圖, 這須滿足兩個條件 :
- DataFrame 資料須從寬格式 (兩組分欄必列) 合併為長格式 (X 軸資料重複兩份, Y 軸兩欄串接合併為一欄), 並且增加一個分群欄位標示 Y 軸資料屬於哪一群.
- 呼叫 encode() 時傳入 xOffset='分群欄位名稱'
以下面的原始資料為例 :
x=['一月', '二月', '三月', '四月', '五月']
y1=[120000, 135000, 99000, 150000, 170000]
y2=[100000, 125000, 87000, 140000, 160000]
其寬格式的 DataFrame 如下 :
df=pd.DataFrame({
'月份': x,
'今年營收': y1,
'去年營收': y2
})
結果如下 :
其長格式的 DataFrame 如下 :
df=pd.DataFrame({
'月份': x * 2,
'營收': y1 + y2,
'類別': ['今年'] * len(x) + ['去年'] * len(x)
})
結果如下 :
可見轉成長格式後月份變成兩倍, 營收今年串接去年, 用類別欄區分今年還是去年.
測試 10 : 繪製兩組 Y 軸資料的群組式長條圖 (垂直) [看原始碼]
# altair-bar-test-10.py
import altair as alt
import pandas as pd
# 原始資料
x=['一月', '二月', '三月', '四月', '五月']
y1=[120000, 135000, 99000, 150000, 170000]
y2=[100000, 125000, 87000, 140000, 160000]
# 將原始資料轉成長格式的 DataFrame
df=pd.DataFrame({
'月份': x * 2,
'營收': y1 + y2,
'類別': ['今年'] * len(x) + ['去年'] * len(x)
})
# 建立群組長條圖
chart=alt.Chart(df).mark_bar().encode(
x=alt.X('月份:N', title='月份', sort=['一月', '二月', '三月', '四月', '五月']),
y=alt.Y('營收:Q', title='營收金額 (元)'),
color=alt.Color('類別:N', title='類別'),
xOffset='類別:N', # 依照指定欄位群組化顯示長條
tooltip=['月份', '類別', '營收']
).properties(
width=400,
height=300,
title='今年與去年每月營收比較'
)
chart.save('altair-bar-test-10.html')
群組化的關鍵就是在 encode() 中用 xOffset 參數指定兩組 Y 軸資料的分群欄位名稱, 結果如下 :
可見兩組以上資料 Altair 會自動出現圖例 (來自 xOffset 指定之分群欄位), 毋須設定.
如果原始資料本來就是寬格式的 DataFrame 例如 :
df=pd.DataFrame({
'月份': ['一月', '二月', '三月', '四月', '五月'],
'今年營收': [120000, 135000, 99000, 150000, 170000],
'去年營收': [100000, 125000, 87000, 140000, 160000]
})
則可用 Pandas 的 melt() 函式將今年與去年營收這兩個欄位融化 (串接) 變成長格式, 例如 :
df_long=df.melt(
id_vars=['月份'], # 保持不變的欄位 (其他欄位要被融化)
value_name='營收', # 融化後新的數值欄位名稱
var_name='類別' # 要新增的分類欄位名稱
)
融化 (串接) 後的值會放進 value_name 指定的新增欄位 '營收' 裡, 而被融化的兩個欄位名稱則會被當作分類名稱填入新增的 '類別' 欄位裡, 結果如下 :
用欄位的 str 屬性值呼叫 replace() 即可去除類別欄中的 '營收' :
df_long['類別']=df_long['類別'].str.replace('營收', '')
結果如下 :
如果要繪製水平的群組式長條圖只要將 X 與 Y 軸對調, 並且在 encode() 中改用 yOffset 參數設定分群欄位名稱即可, 例如 :
測試 11 : 繪製兩組 Y 軸資料的群組式長條圖 (水平) [看原始碼]
# altair-bar-test-11.py
import altair as alt
import pandas as pd
# 原始資料
x=['一月', '二月', '三月', '四月', '五月']
y1=[120000, 135000, 99000, 150000, 170000]
y2=[100000, 125000, 87000, 140000, 160000]
# 將原始資料轉成長格式的 DataFrame
df=pd.DataFrame({
'月份': x * 2,
'營收': y1 + y2,
'類別': ['今年'] * len(x) + ['去年'] * len(x)
})
# 建立群組長條圖
chart=alt.Chart(df).mark_bar().encode(
x=alt.X('營收:Q', title='營收金額 (元)'),
y=alt.Y('月份:N', title='月份', sort=['一月', '二月', '三月', '四月', '五月']),
color=alt.Color('類別:N', title='類別'),
yOffset='類別:N', # 依照指定欄位群組化顯示長條
tooltip=['月份', '類別', '營收']
).properties(
width=400,
height=300,
title='今年與去年每月營收比較'
)
chart.save('altair-bar-test-11.html')
結果如下 :
值得一提的是, 長條圖會以水平或垂直呈現, Altair 預設是以所謂的 Smart defaults 繪圖邏輯規則依據 channel type 與資料型別自動決定的, 此規則摘要如下表 :
| 資料型別組合 | 顯示圖形 |
|---|---|
| x: 數值, y: 類別 | 水平長條圖 ✅ |
| x: 類別, y: 數值 | 垂直長條圖 ✅ |
上面範例的原始資料都是 x: 類別, y: 數值之組合, 因此預設以垂直長條圖呈現, 如果想讓 x, y 不受推論自動作用影響, 就要用 alt.X('欄位:型別') 明確指定型別與軸, 亦即將資料欄位對應到視覺通道.















沒有留言 :
張貼留言