2020年5月2日 星期六

Python 學習筆記 : Matplotlib 資料視覺化 (二) 統計圖

在資料科學中常會用到統計圖來呈現資料的統計特徵, Matplotlib 的 pyplot 子套件提供了一些函數來繪製常見的統計圖. 事實上在前一篇 pyplot 的測試中所呼叫的 plot() 函數預設就是折線圖 (line chart), 關於 pyplot 的基本繪圖用法參考前一篇的 Matplotlib 測試 :

Python 學習筆記 : Matplotlib 資料視覺化 (一) 基本篇

更多 Python 筆記參考 :

# Python 學習筆記索引 

pyplot 子套件除 plot() 外之統計圖繪製函數如下表 :


 pyplot 統計圖函數 說明
 bar(x, y, [options]) 依據 x, y 軸資料繪製長條圖或柱狀圖 (bar chart)
 hist(data, [options]) 依據 data 資料繪製直方圖 (histogram)
 pie(data, [options]) 依據 data 資料繪製圓餅圖 (pie chart)
 scatter(x, y, [options] 依據 x, y 軸資料繪製散佈圖 (scatter chart)
 boxplot(data, [options]) 依據 data 資料繪製箱型圖或盒鬚圖 (box chart)


長條圖 (bar chart) 主要用來呈現定性資料 (qualitative) 之分布情形 (亦即其統計變數為類別變數 categorical), 有垂直與水平兩種型態.


1. 垂直長條圖 (bar chart) :

垂直長條圖函數 bar() 介面如下 :

bar(x, height [kwargs]*)   

其中 x 是代表長條圖 X 軸座標位置的序列物件 (例如 tuple, list, 或 ndarray 陣列等),  kwargs 為選擇性參數, 常用的參數如下表 :


 bar() 常用參數 說明
 color 顏色 ('blue', '#0000ff', 或 0~1 數值), 預設藍色 'blue'
 edgecolor 長條邊框顏色
 width 長條寬度 (0~1 數值), 預設 0.8 
 align X 軸對齊方式 ('center', 'edge'), 預設 'center'
 label 圖例文字 (字串), 預設 None
 tick_label 刻度標籤 (序列物件), 預設 None
 bottom Y 軸基底座標, 預設 0
 log Y 軸是否為對數刻度 (True/False), 預設 False
 linewidth 長條邊框寬度 (px), 預設 0 (無邊框)


參考 :

https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.bar.html

下面範例以直條圖顯示 2020 總統大選得票數統計結果, X 軸變數為類別變數而非數值變數 :


範例 1-1 : 2020 總統大選得票數長條圖 (1) [原始碼]

import numpy as np
import matplotlib.pyplot as plt
votes=[608590, 5522119, 8170231]           #2020總統大選得票數
candidates=['James Soong', 'Korea Fish', 'Tsai Ing-Wen']  #X軸刻度
x=np.arange(len(candidates))                     #產生X軸座標序列
plt.bar(x, votes, tick_label=candidates)     #繪製長條圖
plt.title('2020 Presidential Election')          #設定圖形標題
plt.xlabel('Candidates')                               #設定X軸標籤
plt.ylabel('Votes(million)')                          #設定Y軸標籤
plt.show()

此例以 np.arange() 動態產生 [0, 1, 2, 3 ... N-1] 序列傳入 bar() 當作 X 軸座標, 雖然 X 軸是 [0,1, 2]的序列數值, 但那其實是代表候選人的類別變數而已, 此處用 tick_label 參數將 X 軸座標刻度設定為候選人, 否則預設會顯示序列數值 0, 1, 2 ... 等, 結果如下 :




由於數字最大為百萬, 所以 Y 軸刻度自動以百萬 (1e6) 為單位, 但刻度標籤 1, 2, 3, 4, ... 似乎無法直接感受到那是百萬票, 應該改標成 100, 200, 300,.... 較好. 事實上用 pyplot 的 xticks() 與 yticks() 來設定刻度標籤更有彈性, 例如下面的範例 :


範例 1-2 : 2020 總統大選得票數長條圖 (2) [原始碼]

import numpy as np
import matplotlib.pyplot as plt
votes=[608590, 5522119, 8170231]           #2020總統大選得票數
candidates=['James Soong', 'Korean Fish', 'Tsai Ing-Wen']  #X 軸刻度
x=np.arange(len(candidates))                     #產生 X 軸座標序列
y=np.arange(0, 9000000, 1000000)            #產生 Y 軸座標序列
plt.bar(x, votes)                                           #繪製長條圖
plt.xticks(x, candidates)                             #設定 X 軸刻度標籤
y_ticks=np.arange(0,900,100)                    #Y軸刻度陣列
plt.yticks(y, y_ticks)                                   #設定 Y 軸刻度標籤
plt.title('2020 Presidential Election')           #設定圖形標題
plt.xlabel('Candidates')                                #設定 X 軸標籤
plt.ylabel('Votes(million)')                           #設定 Y 軸標籤
plt.show()

此例以 np.arange(0, 9000000, 1000000) 產生間隔為 100 百萬的數字陣列當做 Y 軸座標之刻度序列,  以 np.arange(0, 900, 100) 產生間隔為 100 的數字陣列當作 Y 軸座標之刻度標籤, 呼叫 plt.yticks(y, y_ticks) 就可以將其對應起來, 結果如下 :




Y 軸刻度標籤經過這樣調整後就比較容易了解了. 下面範例測試選擇性參數 :


範例 1-3 : 2020 總統大選得票數長條圖 (3) [原始碼]

import numpy as np
import matplotlib.pyplot as plt
votes=[608590, 5522119, 8170231]           #2020總統大選得票數
candidates=['James Soong', 'Korean Fish', 'Tsai Ing-Wen']  #X 軸刻度
x=np.arange(len(candidates))               #產生 X 軸座標序列
y=np.arange(0, 9000000, 1000000)     #產生 Y 軸座標序列
plt.bar(x, votes,
        color='cyan',
        edgecolor='#0000ff',
        width=0.5,
        linewidth=2
        )                                                     #繪製長條圖
plt.xticks(x, candidates)                        #設定 X 軸刻度標籤
y_ticks=np.arange(0,900,100)              #Y軸刻度陣列
plt.yticks(y, y_ticks)                             #設定 Y 軸刻度標籤
plt.title('2020 Presidential Election')    #設定圖形標題
plt.xlabel('Candidates')                         #設定 X 軸標籤
plt.ylabel('Votes(million)')                    #設定 Y 軸標籤
plt.show()

此例將長條寬度減為 0.5, 長條顏色設為青色, 框邊寬度設為 2px, 框邊顏色設為藍色, 結果如下 :




長條圖也可以顯示多組資料 (group bar chart), 不過需要將每個長條的寬度變小才能容納多組數據, 例如改為 0.25, 還要將第二組以後的 X 軸座標位置以及 X 軸刻度位置往後移位, 確保刻度是標在各組長條的中間位置, 參考 :

Group Bar Plot In MatPlotLib
https://python-graph-gallery.com/11-grouped-barplot/

下面是繪製兩組資料的長條圖範例 :


範例 1-4 : 兩組資料之長條圖 : ETF 殖利率比較 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
width=0.25
x1=[2015, 2016, 2017, 2018, 2019]          #X 軸 (第一組)
y1=(4.33, 5.67, 3.78, 5.62, 6.7)                 #Y 軸1=0056殖利率
x2=[p + width for p in x1]                        #X 軸 (第二組)
y2=(3.01, 1.28, 6.1, 7.1, 7.22)                   #Y 軸2=0050殖利率
plt.bar(x1, y1, label='0056', width=0.25)  #繪製長條圖
plt.bar(x2, y2, label='0050', width=0.25)  #繪製長條圖
plt.xticks([p + width/2 for p in x1], x1)     #設定 X 軸刻度標籤
plt.legend()                                                #顯示圖例
plt.title('ETF Dividend Yield')                  #設定圖形標題
plt.xlabel('Year')                                        #設定 X 軸標籤
plt.ylabel('Dividend yield(NT$)')             #設定 Y 軸標籤
plt.show()

此例將每個常條寬度縮小為 0.25 (預設寬度的 1/4), 第二組資料的 X 軸座標位置 x2 需全部往右移一個長條的寬度, 此處用串列生成式產生. 另外 X 軸刻度也必須往右移, 由於刻度在一組資料時是標在長條的中央, 因此顯示兩組資料的話, 刻度要標在兩個長條中間, 因此刻度應該右移半個長條寬度 (即加上 width/2), 結果如下 :




可見 Matplotlib 會自動幫各組資料指定長條的顏色, 若要自訂可以透過 color 參數去設定.

如果要顯示三組資料, 則需 X 軸座標刻度位置需右移一個長條寬度, 如下範例所示 :


範例 1-5 : 三組資料之長條圖 : ETF 殖利率比較 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
width=0.25
x1=[2015, 2016, 2017, 2018, 2019]          #X 軸 (第一組)
y1=(4.33, 5.67, 3.78, 5.62, 6.7)                 #Y 軸1=0056殖利率
x2=[p + width for p in x1]                        #X 軸 (第二組)
y2=(3.01, 1.28, 6.1, 7.1, 7.22)                   #Y 軸2=0050殖利率
x3=[p + 2*width for p in x1]                     #X 軸 (第三組)
y3=(0, 2.44, 1.41, 1.72, 1.05)                    #Y 軸3=0052殖利率
plt.bar(x1, y1, label='0056', width=0.25)  #繪製長條圖
plt.bar(x2, y2, label='0050', width=0.25)  #繪製長條圖
plt.bar(x3, y3, label='0052', width=0.25)  #繪製長條圖
plt.xticks([p + width for p in x1], x1)        #設定 X 軸刻度標籤
plt.legend()                                                #顯示圖例
plt.title('ETF Dividend Yield')                  #設定圖形標題
plt.xlabel('Year')                                        #設定 X 軸標籤
plt.ylabel('Dividend yield(NT$)')             #設定 Y 軸標籤
plt.show()

此例加入第三組資料 (0052 富邦科技 ETF), x3 座標位置要往右移兩個長條寬度 (width*2), 而 X 軸座標刻度則是右移一個長條寬度 (width), 結果如下 :




可見預設圖形在長條寬度為 0.25 情況下只能同時繪製 4 組資料, 如果要繪製 5 組資料, 長條寬度需設為 1/5=0.2 才裝得下. 不管要顯示幾組資料, X 軸座標刻度可依照下列通式計算要右移多少 :

xn=p + (n-1)*width/2

資料來源 :

https://goodinfo.tw/StockInfo/StockDetail.asp?STOCK_ID=0052


2. 水平長條圖 (horizontal bar chart) :

水平長條圖函數介面如下 :

barh(y, width [kwargs]*)   

水平長條圖與垂直長條圖的 x, y 與 width, height 互相對調, 此外參數用法都相同 :


 barh() 常用參數 說明
 color 顏色 ('blue', '#0000ff', 或 0~1 數值), 預設藍色 'blue'
 edgecolor 長條邊框顏色
 height 長條高度 (0~1 數值), 預設 0.8 
 align X 軸對齊方式 ('center', 'edge'), 預設 'center'
 label 圖例文字 (字串), 預設 None
 tick_label 刻度標籤 (序列物件), 預設 None
 bottom Y 軸基底座標, 預設 0
 log Y 軸是否為對數刻度 (True/False), 預設 False
 linewidth 長條邊框寬度 (px), 預設 0 (無邊框)


範例 2-1 : 一組資料之水平長條圖 : 2020 總統大選得票數 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
votes=[608590, 5522119, 8170231]           #2020總統大選得票數
candidates=['James Soong', 'Korean Fish', 'Tsai Ing-Wen']  #Y 軸刻度
y=np.arange(len(candidates))                     #產生 Y 軸座標序列
x=np.arange(0, 9000000, 1000000)           #產生 X 軸座標序列
plt.barh(y, votes)                                        #繪製長條圖
plt.yticks(y, candidates)                             #設定 Y 軸刻度標籤
x_ticks=np.arange(0,900,100)                   #X 軸刻度陣列
plt.xticks(x, x_ticks)                                  #設定 X 軸刻度標籤
plt.title('2020 Presidential Election')         #設定圖形標題
plt.xlabel('Votes(million)')                        #設定 X 軸標籤
plt.ylabel('Candidates')                              #設定 Y 軸標籤
plt.show()





水平長條圖同樣可繪製多組資料, 但第二組後的 Y 軸座標位置, 以及 Y 軸座標刻度都必須往下移, 其位移通式如下 :

yn=p + (n-1)*height/2


範例 2-2 : 兩組資料之水平長條圖 : ETF 殖利率比較 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
height=0.25
y1=[2015, 2016, 2017, 2018, 2019]            #Y 軸 (第一組)
x1=(4.33, 5.67, 3.78, 5.62, 6.7)                   #X 軸1=0056殖利率
y2=[p + height for p in y1]                          #Y 軸 (第二組)
x2=(3.01, 1.28, 6.1, 7.1, 7.22)                      #X 軸2=0050殖利率
plt.barh(y1, x1, label='0056', height=0.25)  #繪製長條圖
plt.barh(y2, x2, label='0050', height=0.25)  #繪製長條圖
plt.yticks([p + height/2 for p in y1], y1)      #設定 Y 軸刻度標籤
plt.legend()                                                   #顯示圖例
plt.title('ETF Dividend Yield')                     #設定圖形標題
plt.ylabel('Year')                                           #設定 Y 軸標籤
plt.xlabel('Dividend yield(NT$)')                 #設定 X 軸標籤
plt.show()

此例事實上是將上面範例 1-4 中的 X, Y 軸變數對調, 並將 width 改成 height 兒得, 結果如下 :




三組資料的水平長條圖如下例所示 :


範例 2-3 : 兩組資料之水平長條圖 : ETF 殖利率比較 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
height=0.25
y1=[2015, 2016, 2017, 2018, 2019]            #Y 軸 (第一組)
x1=(4.33, 5.67, 3.78, 5.62, 6.7)                   #X 軸1=0056殖利率
y2=[p + height for p in y1]                          #Y 軸 (第二組)
x2=(3.01, 1.28, 6.1, 7.1, 7.22)                     #X 軸2=0050殖利率
y3=[p + 2*height for p in y1]                      #Y 軸 (第三組)
x3=(0, 2.44, 1.41, 1.72, 1.05)                      #X 軸3=0052殖利率
plt.barh(y1, x1, label='0056', height=0.25)  #繪製長條圖
plt.barh(y2, x2, label='0050', height=0.25)  #繪製長條圖
plt.barh(y3, x3, label='0052', height=0.25)  #繪製長條圖
plt.yticks([p + height for p in y1], y1)          #設定 Y 軸刻度標籤
plt.legend()                                                    #顯示圖例
plt.title('ETF Dividend Yield')                      #設定圖形標題
plt.ylabel('Year')                                            #設定 Y 軸標籤
plt.xlabel('Dividend yield(NT$)')                 #設定 X 軸標籤
plt.show()

結果如下 :




可見經過 Y 軸刻度位移後, 刻度線都剛好位於各組資料的中間.

此外 barh() 也常被用來畫專案進度之甘特圖 (Gantt chart), 參考 :

實用程式碼Python(五)用Matplotlib畫甘特圖
matplotlib 之甘特图
Gantt Charts in Matplotlib


3. 直方圖 (histogram chart) :

直方圖會對資料進行次數分配統計, 通常用來呈現定量資料 (quantitative) 的分布情形, 可藉此觀察資料之偏態與峰度, 其函數介面如下 :

n, bins, patches=plt.hist(data [, bins=10] [, kwargs]*)

傳回值為一組 tuple, 第一個元素 n 為資料的次數分配表 (串列), 第二個元素 bins 為分布區間 (串列), 第三個參數 patches 是一個 Patch 物件 (串列), 其元素為 Rectangle 物件, 用來描繪直方圖中的每一個矩形長條. 參數 data 為序列資料 (串列/元組) 或陣列, bins 為資料分布區間數目 (或稱為組距, 預設為 10 個區間),  kwargs 為關鍵字參數, 常用的參數如下表 :


 hist() 常用參數 說明
 bins 分布區間數目 (整數), 預設 10
 range 分布區間數目範圍 (元組) : (min, max)  
 color 直方圖長條顏色, 例如 'blue' 或 '#0000ff' 等
 edgecolor 直方圖長條邊緣顏色, 例如 'blue' 或 '#0000ff' 等
 histtype 直方圖類型 : 'bar' (預設), 'barstacked', 'step', 'stepfilled'
 rwidth 直方圖長條相對於區間之寬度 (0~1)
 linewidth 長條邊框寬度 (px)
 edgecolor 長條邊框顏色, 例如 'blue' 或 '#0000ff' 等
 orientation 方向, 'vertical' (預設) 或 'horizontal'
 align 對齊方式, 'left', 'mid' (預設), 'right'
 label 直方圖之圖例標籤
 stacked 多組資料時後一組是否疊到前一組織上 : True, False (預設)
 cumulative 每個分布區間是否由小到大累計, True, False (預設)


其實, 大部分的 plot() 的選擇性參數都可以用在 hist() 上, 參考 :

https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.hist.html


範例 3-1 : 學生成績分布之直方圖 (1) [原始碼]

import numpy as np
import matplotlib.pyplot as plt
scores=[20, 75, 45, 68, 92, 34, 65, 29, 88, 31,
        66, 94, 72, 55, 49, 59, 11, 85, 79, 84,
        69, 83, 96, 45, 73, 85, 69, 86, 100, 9]                   #成績
bins_list=[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]    #分布區域 (組距)
n, bins, patches=plt.hist(scores, bins=bins_list)           #繪製直方圖
print(n)                                                                          #輸出次數分配
print(bins)                                                                     #輸出分布區間
for p in patches:                                                            #輸出 Patch 物件內容
    print(p)                                   
plt.title('Score Distribution')                                         #設定圖形標題
plt.ylabel('Scores')                                                        #設定 Y 軸標籤
plt.xlabel('Students')                                                     #設定 X 軸標籤
plt.show()

此例之 scores 列舉了學生某科目成績, bins 則以串列列舉了各個組距, 結果如下 :




print() 輸出結果 :

[1. 1. 2. 2. 3. 2. 5. 4. 6. 4.]
[  0  10  20  30  40  50  60  70  80  90 100]
Rectangle(xy=(0, 0), width=10, height=1, angle=0)
Rectangle(xy=(10, 0), width=10, height=1, angle=0)
Rectangle(xy=(20, 0), width=10, height=2, angle=0)
Rectangle(xy=(30, 0), width=10, height=2, angle=0)
Rectangle(xy=(40, 0), width=10, height=3, angle=0)
Rectangle(xy=(50, 0), width=10, height=2, angle=0)
Rectangle(xy=(60, 0), width=10, height=5, angle=0)
Rectangle(xy=(70, 0), width=10, height=4, angle=0)
Rectangle(xy=(80, 0), width=10, height=6, angle=0)
Rectangle(xy=(90, 0), width=10, height=4, angle=0)

可見分數在 80~90 這個組距人數為 6 人最多, 而分數在 0~20 分這組距有 2 人最少, 每個分布區間都是由個別的 Rectangle 物件所描繪, 例如次數分配由 height 屬性控制.

由上面範例可知, 直方圖預設為藍色. 長條無邊框, 這些都可以用選擇性參數設定, 例如 :


範例 3-2 : 學生成績分布之直方圖 (2) : 使用選擇性參數 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
scores=[20, 75, 45, 68, 92, 34, 65, 29, 88, 31,
        66, 94, 72, 55, 49, 59, 11, 85, 79, 84,
        69, 83, 96, 45, 73, 85, 69, 86, 100, 9]                 #成績
bins_list=[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]  #分布區域 (組距)
plt.hist(scores, bins=bins_list,
         color='cyan',
         linewidth=1,
         edgecolor='black',
         label='Students')                                      #繪製直方圖
plt.legend()                                                       #加上圖例
plt.title('Score Distribution')                             #設定圖形標題
plt.xlabel('Scores')                                            #設定 X 軸標籤
plt.ylabel('Students')                                         #設定 Y 軸標籤
plt.xticks(bins_list)                                           #設定 X 軸刻度
plt.show()

此例在呼叫 hist() 時傳入更多參數來調整直方圖, 其中 color 用來改變直條顏色, linewidth 用來設定直條的邊框寬度, 而 edgecolor 則用來設定邊框顏色, 最後用 xticks() 來調整刻度, 參考 :

Python histogram outline

結果如下 :




可見 X 軸刻度顯示所有組距,比預設的每 20 一格更清楚了.

在上面我們已知 hist() 的傳回值是一個三元素的 tuple, 其中第三個傳回值 patches 是由代表直方圖中每個長條的 Rectangle 物件組成之串列, 這些 Rectangle 物件可搭配 plot.setp() 函數來設定特定直條的樣式, 例如顏色或邊框等以凸顯該組距, 參考 :

What are n bins and patches in matplotlib?

pyplot.setp() 函數介面如下 :

pyplot.setp(obj, args [, kwargs]*)
其中 obj 為要設定屬性之物件, args 為參數, kwargs 為關鍵字參數, 參考 :

https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.setp.html

要凸顯某些組距只要將 facecolor 屬性指定為特定顏色即可, 例如若要凸顯上面範例中人數最多 (80~90) 與最少 (0~20) 的組距可以這麼做 :


範例 3-3 : 學生成績分布之直方圖 (3) : 用 Rectangle 物件凸顯特定區間 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
scores=[20, 75, 45, 68, 92, 34, 65, 29, 88, 31,
        66, 94, 72, 55, 49, 59, 11, 85, 79, 84,
        69, 83, 96, 45, 73, 85, 69, 86, 100, 9]                 #成績
bins_list=[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]  #分布區域 (組距)
n, bins, patches=plt.hist(scores, bins=bins_list,
         color='cyan',
         linewidth=1,
         edgecolor='black',
         label='Students')                                   #繪製直方圖
plt.setp(patches[0:2], facecolor='red')           #設定物件屬性 
plt.setp(patches[8], facecolor='yellow')        #設定物件屬性 
plt.legend()                                                     #加上圖例
plt.title('Score Distribution')                          #設定圖形標題
plt.xlabel('Scores')                                         #設定 Y 軸標籤
plt.ylabel('Students')                                      #設定 X 軸標籤
plt.xticks(bins_list)                                        #設定 X 軸刻度
plt.show()

此例利用 setp() 函數將 hist() 傳回的 tuple 第三個元素 patches 中之 Rectangle 物件 [0], [1], 以及 [8] 的 facecolor 屬性改變, 這樣便能凸顯這三個直條的顏色. facecolor 屬性只改變直條內部的顏色, 不包括直條外框顏色. 如果改用 color 屬性則包含直條邊框顏色也會一起改變. 結果如下 :




可見透過設定 Rectangle 物件的 facecolor 屬性凸顯高標與低標區間使圖表可讀性更高.


4. 圓餅圖 (pie chart) :

圓餅圖是一種描述類別 (categorical) 資料的統計圖, 它將圓形分割成幾個扇形或切片 (slice), 以切片的面積大小來描述資料中各類別數據的相對比例, pyplot 的 pie() 函數可用來繪製圓餅圖, 其介面如下 :

pitches, texts, autotexts=plt.pie(data [, kwargs]*)

參數群包括一個必要參數 data 以及若干選擇性的關鍵字參數, 其中 data 為要繪製之序列資料 (元組, 串列, 或陣列), 每個元素在圓餅中所佔之比例為該元素值除以所有元素值總和. 此函數會會傳回三個串列 : patches 為每個扇形之物件, texts 為 Texts 物件; autotexts 為代表比例值之 Texts 物件. 常用的選擇性參數 kwargs 如下表 :


 pie() 常用參數 說明
 explode 對應資料之 0~1 數值串列, 表示該扇形與圓形分離之程度.
 colors 對應資料之顏色字串串列, 表示該扇形之顏色, 例如 'blue'
 labels 對應資料之標籤字串串列, 表示該扇形之說明.
 autopct 扇形所佔比例之顯示格式字串或可傳回格式字串之函數, 例如 '%1.1f%' 
 pctdistance 比例值距離圓心之顯示位置 (0~1 浮點數), 預設 0.6
 shadow 是否顯示扇形之陰影 (True/False), 預設 False
 startangle 扇形繪圖起始度數 (浮點數), 預設 None 為自 X 軸起逆時鐘繪製
 radius 圓形半徑 (0~1 浮點數), 預設 1
 counterclock 是否為逆時鐘繪製 (True/False), 預設 True
 center 設定圓心座標 (tuple), 預設 (0,0)
 frame 是否在圓形四周顯示座標軸 (True/False), 預設 False
 rotatelabels 是否旋轉扇形之標籤使其與扇形同角度 (True/False), 預設 False


autopct 為以 % 字元開頭之顯示格式字串或是能傳回格式字串的函數, 格式主要是以小數點 '.' 與 'f' 字元規範浮點數的小數位數, 例如 '%.2f' 表示要顯示到小數點後兩位, 若要顯示 '%' 本身則要再用一個 '%' 跳脫, 例如 '%.2f%%' 會顯示例如 '28.45%'.

參考 :

https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.pie.html


範例 4-1 : 資產配置 (1) : 預設圓餅圖 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
data=[60, 20, 10, 5, 5]              #資產配置比率 (%)
plt.pie(data)                              #繪製圓餅圖
plt.title('Asset Allocation')       #設定圖形標題
plt.show()

此例的 data 以串列表示五類資產 (股票, 債券, 現金, 黃金, 不動產) 佔總資產之百分比, 不傳入任何選擇性參數時 matplotlib 會自動為各項資產配色, 結果如下 :



可見 pie(data) 會用自動以不同顏色顯示資料中各類別, 但預設的圓餅圖缺乏說明資訊, 例如切片的類別標籤與比例等, 事實上繪製一個基本的圓餅圖至少要有 data, labels, 與 autopct 這三個參數才算資訊充分, 例如 :


範例 4-2 : 資產配置 (2) : 圓餅圖設定 autopct 顯示比例值 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
data=[600, 200, 100, 50, 50]                                      #資產配置 (百萬元)
labels=['Stock', 'Bond', 'Cash', 'Gold', 'Real estate']   #資產標籤
plt.pie(data, labels=labels, autopct='%.2f%%')          #繪製圓餅圖
plt.title('Asset Allocation')                                          #設定圖形標題
plt.show()

此例傳入了與資料 data 對應的參數 labels 與 autopct, 這樣就會顯示每一個扇形切片的說明標籤以及所佔的百分比. 另外與上例不同的是此處 data 串列的元素不是百分比, 而是百萬元為單位的資產金額, pie() 函數會將所有元素加總後計算百分比再以 autopct 指定格式顯示 (預設是於距圓心 0.6 處), 結果如下 :




這樣圖表的描述功能就很充分了, 這是畫圓餅圖最基本的要求.

參數 autopct 也可以是一個可傳回格式字串的函數, 這樣就能自行控制要顯示的格式, 例如 :


範例 4-3 : 資產配置 (3) : 圓餅圖 autopct 呼叫閉包函 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
def myautopct(data):
   def inner_myautopct(pct):
       total=sum(data)
       val=int(round(pct*total/100.0))
       return '{p:.0f}% ({v:d})'.format(p=pct,v=val)
   return inner_myautopct
data=[600, 200, 100, 50, 50]                                        #資產配置 (百萬元)
labels=['Stock', 'Bond', 'Cash', 'Gold', 'Real estate']     #資產標籤
colors=['#1f77b4', 'yellow', 'green', 'red', 'cyan']          #資產顏色
plt.pie(data, labels=labels, autopct=myautopct(data),
        colors=colors)                                                       #繪製圓餅圖
plt.title('Asset Allocation')                                            #設定圖形標題
plt.show()

此例利用自訂函數 myautopct(), 它會傳回一個閉包 (closure) 函數 inner_myautopct(), 在此閉包中, 對每一個扇形, matplotlib 會傳入其百分比做為參數 pct, 從而可由總和與 pct 反推其值 val, 再用 format() 產生所要的特定字串, 參考 :

How do I use matplotlib autopct?
如何使用 matplotlib autopct?

與上面範例另一個不同處是傳入 colors 串列來自定每個扇形切片的顏色, 結果如下 :




可見百分比後面多出了括弧中的金額 (百萬元).

參數 autopct 也可以傳入匿名函數 (lambda) 以便像上面範例一樣在函數中自訂輸出格式, 例如 :


範例 4-4 : 資產配置 (4) : 圓餅圖 autopct 呼叫 Lambda 函數 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
data=[600, 200, 100, 50, 50]                                     #資產配置 (百萬元)
labels=['Stock', 'Bond', 'Cash', 'Gold', 'Real estate']  #資產標籤
colors=['#1f77b4', 'yellow', 'green', 'red', 'cyan']       #資產顏色
plt.pie(data, labels=labels,
        autopct=lambda p:f'{p:.0f}% ({p*sum(data)/100 :.0f})',   #匿名]函數
        colors=colors)                                                   #繪製圓餅圖
plt.title('Asset Allocation')                                       #設定圖形標題
plt.show()

結果與上面範例完全一樣 :




接下來的範例測試 pie() 的其他常用參數, 例如想要特別凸顯某些類別時可傳入 explode 參數將這些類別從圓形中分離, 這參數是一個相對於 data 資料類別的 0~1 數值串列, 用來表示與圓形的分離程度, 0 表示不分離 (不凸顯), 而 1 表示分離程度最大. 其次 shadow 可設定是否要有陰影, 例如 :


範例 4-5 : 資產配置 (5) : 圓餅圖其它常用參數測試 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
data=[600, 200, 100, 50, 50]                                     #資產配置 (百萬元)
labels=['Stock', 'Bond', 'Cash', 'Gold', 'Real estate']  #資產標籤
colors=['#1f77b4', 'yellow', 'green', 'red', 'cyan']       #資產顏色
explode=[0, 0, 0.2, 0, 0]                                            #凸顯 Cash (扇形分離 0.2)
plt.pie(data, labels=labels,                               #資料與標籤
        autopct='%.0f%%',                                  #百分比格式
        colors=colors,                                          #扇形顏色
        startangle=180,                                        #起始角度
        shadow=True,                                          #顯示陰影
        explode=explode)                                    #扇形分離設定
plt.title('Asset Allocation')                               #設定圖形標題
plt.show()

此例添加了三個常用參數 : startangle 用來指定 data 資料第一個類別 (此處為 Stock) 扇形之起始角度; shadow=True 開啟陰影顯示; explode 則設定將 Cash 類別以 0.2 比例從圓餅中分離以凸顯其重要性, 結果如下 :




可見整個圓餅圖有陰影效果, Cash 類別被分離出來, 且扇形從 180 度 (左方) 開始繪製.


5. 散佈圖 :

散佈圖在統計學中用來描述兩個量變數的相關性, 以自變數為 X 軸, 應變數為 Y 軸, 從散佈圖可看出這兩個變數是呈現正相關, 負相關, 或零相關. pyplot 的 scatter() 函數可用來繪製散佈圖, 其介面如下 :

plt.scatter(x, y [, kwargs]*)

其中 x 為應變數 (X 軸), y 為自變數 (Y 軸), 常用關鍵字參數如下表 :


 scatter() 常用參數 說明
 marker 資料點標記符號字元, 例如 'o' 為圓點, 's' 為方點
 c 或 color 資料點的顏色 (字串, 元組或陣列), 例如 'blue', '#0000ff' 或 (0, 1, 0) 等
 s   資料點的大小 (浮點數, 預設 20)
 alpha 資料點的透明度, 0 (透明)~1 (不透明)
 cmap 當 c 參數為陣列時所使用之顏色映射表名稱 (字串), 例如 'hsv'
 linewidths 資料點邊框寬度 (預設 1.5)
 edgecolors 資料點邊框顏色 (字串或序列), 預設 'face', 無顏色用 'none'


參考 :

https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.scatter.html

其中 cmap 參數是顏色映射表名稱, 用於搭配顏色參數 c 為陣列時顯示顏色變遷方式Matplotlib 定義了 7 種顏色映射表 :


 顏色映射表分類 映射表名稱
 Perceptually Uniform Sequential'viridis', 'plasma', 'inferno', 'magma', 'cividis'
 Sequential'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds', 'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu', 'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn'
 Sequential (2)'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink', 'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia', 'hot', 'afmhot', 'gist_heat', 'copper'
 Diverging'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu', 'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic'
 Cyclic'twilight', 'twilight_shifted', 'hsv'
 Qualitative'Pastel1', 'Pastel2', 'Paired', 'Accent', 'Dark2', 'Set1', 'Set2', 'Set3', 'tab10', 'tab20', 'tab20b', 'tab20c'
 Miscellaneous'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern', 'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg', 'gist_rainbow', 'rainbow', 'jet', 'nipy_spectral', 'gist_ncar'


參考 :

https://matplotlib.org/3.2.1/gallery/color/colormap_reference.html


範例 5-1 : 體重與身高關聯性散佈圖 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
weight=[49, 65, 53, 45, 56, 47, 52, 61]                 #X軸:體重
height=[159, 177, 156, 163, 164, 158, 166, 171]  #Y軸:身高
plt.scatter(weight, height)                                      #繪製散佈圖
plt.title('Relationship of Weight vs Height')           #設定圖形標題
plt.xlabel('Weight(Kg)')                                          #設定X軸標籤
plt.ylabel('Height(Cm)')                                          #設定Y軸標籤
plt.show()

此例以體重 weight 為 X 軸, 以身高 height 為 Y 軸, 繪製散佈圖觀察兩個變數的關係, 結果如下 :




此有限的資料顯示體重與身高似乎呈現正相關, 即身高越高的人也越重. 其實 plot() 函數也可以繪製散佈圖, 只要資料點標記符號傳入 'o' 即可, 例如 :


範例 5-2 : 使用 plot() 繪製散佈圖 [原始碼]

import numpy as np
import matplotlib.pyplot as plt
weight=[49, 65, 53, 45, 56, 47, 52, 61]                 #X軸:體重
height=[159, 177, 156, 163, 164, 158, 166, 171]  #Y軸:身高
plt.plot(weight, height, 'o')                                    #繪製散佈圖
plt.title('Relationship of Weight vs Height')          #設定圖形標題
plt.xlabel('Weight(Kg)')                                         #設定X軸標籤
plt.ylabel('Height(Cm)')                                         #設定Y軸標籤
plt.show()

此例使用 plot() 繪製散佈圖, 傳入 'o' 表示以圓點作為資料點符號, 結果與上面 5-1 的完全一樣 :




雖然 plot() 與 scatter() 都能繪製散佈圖, 但在數據量很多時使用 plot() 繪圖效率會比 scatter() 好, 這是因為 scatter() 對於每個資料點的大小與顏色都是單獨繪製的 (所以可控制各別資料點之顏色與大小), 需要花較多時間處理; 反觀 plot() 的資料點的外觀處理只要做一次, 其他資料點的外觀屬性都是複製而來的, 因此繪製速度較快.

如果要在同一張圖上繪製多組散佈圖, 可以呼叫多次 scatter(), 下面範例以線性與平方函數產生的點繪製散佈圖, 主要是測試 color 與 marker 參數 :


範例 5-3 : 繪製函數散佈圖 (10 個資料點) [原始碼]

import numpy as np
import matplotlib.pyplot as plt
x=np.linspace(0, 100, 10)                                #X軸:體重
y1=[100*x for x in x]                                      #Y軸1:y1=2x
y2=[x**2 for x in x]                                        #Y軸2:y2=2x
plt.scatter(x, y1,
            color='red',
            marker='s',
            label='100*x')                                      #繪製散佈圖1
plt.scatter(x, y2,
            color='blue',
            marker='o',
            label='x**2')                                        #繪製散佈圖2
plt.legend()                                                      #顯示圖例
plt.title('Linear & Square functions')      #設定圖形標題
plt.xlabel('X')                                                   #設定X軸標籤
plt.ylabel('Y')                                                   #設定Y軸標籤
plt.show()

此例用 Numpy 的 linspace() 函數產生X 軸自變數串列,  在 0~100 區間中每 10 個取一點即得串列 [1, 10, 20, ... 100]; 而 Y 軸則有兩個函數, 一是線性函數 y=100*x, 另一個是平方函數 y=x**2, 結果如下 :




可見線性與平方函數分別用紅藍色散佈圖繪製, 各有 10 個資料點. 當資料點增加時, 這些點會連成一線變成類似折線圖效果, 例如將上面範例的 10 個資料點改成 100 個資料點如下 :


範例 5-4 : 繪製函數散佈圖 (100 個資料點) [原始碼]

import numpy as np
import matplotlib.pyplot as plt
x=np.linspace(0, 100, 100)                          #X軸
y1=[100*x for x in x]                                  #Y軸1:y1=100*x
y2=[x**2 for x in x]                                    #Y軸1:y2=x**2
plt.scatter(x, y1,
            color='red',
            marker='s',
            label='100*x')                                   #繪製散佈圖1
plt.scatter(x, y2,
            color='blue',
            marker='o',
            label='x**2')                                     #繪製散佈圖2
plt.legend()                                                   #顯示圖例
plt.title('Linear & Square functions')           #設定圖形標題
plt.xlabel('X')                                                #設定X軸標籤
plt.ylabel('Y')                                               #設定Y軸標籤
plt.show()

此例僅僅是將 linspace() 的資料點改為 100 而已, 結果如下 :




下面的範例使用亂數來繪製散佈圖, 主要是測試參數 s, cmap, 以及 alpha :


範例 5-5 : 繪製函數散佈圖 (隨機函數) [原始碼]

import numpy as np
import matplotlib.pyplot as plt
rng=np.random.RandomState(0)                #偽隨機數生成器
x=rng.randn(100)                                         #X軸:100個隨機數
y=rng.randn(100)                                         #Y軸:100個隨機數
colors=rng.rand(100)                                   #顏色:100個隨機數
sizes=1000*rng.rand(100)                           #資料點大小
plt.scatter(x, y,
            c=colors,
            s=sizes,
            alpha=0.3,
            cmap='hsv')                                        #繪製散佈圖
plt.colorbar()                                                 #顯示顏色刻度
plt.show()

此例呼叫 Numpy 的隨機數生成器函數 random.RandomState(0) 傳回一個 RandomState 物件 rng, 再呼叫 randn()rand() 產生隨機數, randn(100) 會傳回 100 個平均 (高斯) 分布的隨機數, 可正可負; 而方法 rand(100) 則是傳回 100 個 [0, 1) 之間的常態分佈隨機數. 呼叫 colorbar() 會在 Y 軸右邊顯示顏色刻度, 參考 :

numpy.random.RandomState
https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.colorbar.html

結果如下 :




範例 5-6 : 繪製函數散佈圖 (隨機函數) [原始碼]

import numpy as np
import matplotlib.pyplot as plt
x=np.linspace(0, 10, 100)                        #X軸
y=[x**2 for x in x]                                   #Y軸1:y=x**2
line_width=(1+x)**2                               #資料點寬度
plt.scatter(x, y,
            s=line_width,                                #資料點寬度
            color='blue',
            marker='o',
            label='x**2')                                 #繪製散佈圖
plt.legend()                                               #顯示圖例
plt.title('Square Fuction Line Width Test')      #設定圖形標題
plt.xlabel('X')                                            #設定X軸標籤
plt.ylabel('Y')                                            #設定Y軸標籤
plt.show()

此例設定了一個隨 x 變大的資料點寬度 line_width 傳給參數 s, 結果如下 :




可見隨著 x 變大, 資料點大小也變粗了. 下面範例測試顏色映射表 cmap 用法 :


範例 5-7 : 繪製函數散佈圖 (顏色映射表) [原始碼]

import numpy as np
import matplotlib.pyplot as plt
x=np.linspace(0, 100, 500)                       #X軸
y1=x                                                         #Y軸1:y=x
y2=x+10                                                   #Y軸2:y=x+10
y3=x+20                                                   #Y軸3:y=x+20
y4=x-10                                                    #Y軸4:y=x-10
y5=x-20                                                    #Y軸5:y=x-20
plt.scatter(x, y1, c=x, cmap='rainbow')          #繪製散佈圖       
plt.scatter(x, y2, c=x, cmap='twilight')           #繪製散佈圖
plt.scatter(x, y3, c=x, cmap='hsv')                  #繪製散佈圖
plt.scatter(x, y4, c=x, cmap='seismic')           #繪製散佈圖
plt.scatter(x, y5, c=x, cmap='ocean')              #繪製散佈圖
plt.title('Cmap Test')                                       #設定圖形標題
plt.xlabel('X')                                                  #設定X軸標籤
plt.ylabel('Y')                                                  #設定Y軸標籤
plt.show()

此例指定顏色參數 c (color) 隨 x 而變, 對不同函數指定不同之 cmap 之結果如下 :




注意, cmap 需與變動的 color 參數一起使用才會顯現顏色變遷的效果.


6. 箱型圖 : 

箱型圖 (box plot) 又稱為盒鬚圖 (notched box), 用來呈現數據的四分位距統計資訊, 可看出一個統計變數的集中趨勢, 最大與最小值, 變異與偏態等分布特徵.




pyplot 的 boxplot() 函數可用來繪製箱型圖, 其介面如下 :

plt.boxplot(x, notch=False, labels=None [, kwargs]*)

其中 x 為要繪製之資料, 可以是陣列或序列等. 參數 notch 用來指定要繪製箱型圖 (box) 或盒鬚圖 (notched box), 預設是箱型圖, 參數 labels 為字串序列, 用來指定每個箱型的標籤.


範例 6-1 : 繪製箱型圖 [原始碼]



Creating boxplots with Matplotlib

~進行中~

Creating boxplots with Matplotlib

參考 :

[Day20]Matplotlib資料視覺化進階!
# Better visualization of Pie charts by MatPlotLib
Matplotlib可视化最有价值的50个图表
[Day17]Numpy的數學&統計方法!

沒有留言 :