2022年7月22日 星期五

Python 學習筆記 : Seaborn 資料視覺化 (二)

在前一篇 Seaborn 測試中已了解 Seaborn 並非用來完全取代 Matplotlib, 而是擴充其統計繪圖功能, 使用時還是要匯入 Matplotlib, 顯示圖形仍然是使用 plt.show(). Seaborn 的好處是它提供預設主題背景來使圖形更美觀, 介面更簡單. 本系列之前的文章參考 : 


Seaborn 教學文件參考 :

https://www.python-graph-gallery.com/ (Seaborn 繪圖範例網站)

參考書目 :
  1. Python 網路爬蟲與資料視覺化 (旗標, 2017, 陳允傑) 第 14 章
  2. Python 資料可視化之美 (深智, 2020, 張傑) 第 3 章
  3. Python 資料科學手冊 (碁峰, 2017, 何敏煌譯) 第 4 章
  4. Python資料可視化攻略 (碁峰, 2021)
  5. Pandas 資料清理, 重塑, 過濾, 視覺化 (旗標, 2021) 第 12 章
在資料來源方面, Seaborn 有別於 Matplotlib 之處是支援 Pandas 的 DataFrame 資料結構, 且內建 load_dataset() 可載入線上資料集供學習者測試, 故以下測試均以此資料集為資料來源.

>>> import seaborn as sns   
>>> sns.get_dataset_names()       
['anagrams', 'anscombe', 'attention', 'brain_networks', 'car_crashes', 'diamonds', 'dots', 'exercise', 'flights', 'fmri', 'gammas', 'geyser', 'iris', 'mpg', 'penguins', 'planets', 'taxis', 'tips', 'titanic']


六. 關聯性圖表 (Relational plots) : 

Seaborn 的關聯性圖表有下列三種 : 
  • 折線圖 lineplot()
  • 散布圖 scatterplot() :
  • 散點圖 relplot() : 
折線圖適合用來呈現一個變數對連續時間變數的變化趨勢; 散布圖與散點圖則適合用來觀察兩個變數. 其中散點圖 relplot() 為與 Pandas 緊密結合之畫布等級 (figure-level) 函式, 可用 DataFrame 直接繪製出 Matplotlib 的多軸 (多子圖) 圖形, 一次畫出多種圖形. 折線圖函式 lineplot() 與散布圖函式 scatterplot 則是軸等級函式. 


1. 折線圖 :

折線圖適合繪製時間序列變數, 可用來呈現資料的時間趨勢. 繪製折線圖可用軸等級的 lineplot() 函式或畫布等級的 relplot(kind='line') 函式. 

首先用 sns.lineplot() 繪製折線圖, 其主要參數如下 : 

lineplot(x, y, data [, hue, style, palette, markers, size, sizes, dashes])

說明如下 :
  • x :  X 軸資料欄位名稱 (字串)
  • y : Y 軸資料欄位名稱 (字串)
  • hue : 用來表示第三軸的色調 (字串)
  • data : Pandas 的 DataFrame 資料
  • style : 區分樣式的欄位名稱
  • palette : 色彩範本
  • markers : 是否顯示資料點標記 (True/False), 預設 False 
  • size : 線條寬度類型
  • sizes : 線條寬度
  • dashes : 是否使用虛線 (True/False), 預設 True
其中最重要的是 x, y, data 這三個參數, data 就是 DataFrame 變數名稱, x 與 y 是 DataFrame 中的欄位名稱字串, 分別指定 X, Y 軸的資料來源. 

參考 : 


以航班旅客數量資料集 'flight' 為例, 裡面有自 1949 年以來每個月的旅客數統計, 例如 : 

>>> import seaborn as sns   
>>> import matplotlib.pyplot as plt   
>>> import pandas as pd   
>>> df=sns.load_dataset('flights')       
>>> df      
     year month  passengers
0    1949   Jan         112
1    1949   Feb         118
2    1949   Mar         132
3    1949   Apr         129
4    1949   May         121
..    ...   ...         ...
139  1960   Aug         606
140  1960   Sep         508
141  1960   Oct         461
142  1960   Nov         390
143  1960   Dec         432

[144 rows x 3 columns]

可見這是一個典型的時間序列資料, 有 year, month, 以及 passenger 三個欄位, 資料是逐年再逐月排列, 如果直接繪製 passenger 對 year 的圖形, 會繪製出以平均值為中心, 與其 95% 信賴區間為範圍的折線圖, 例如 :

>>> sns.set()    
>>> sns.lineplot(x="year", y="passengers", data=df)       
<matplotlib.axes._subplots.AxesSubplot object at 0x000001CC3A2827F0>
>>> plt.show()      

結果如下 : 




可見除了折線外, 周圍還有陰影, 這是因為一個時間點 (例如 1949 年) 有多筆資料, Seaborn 會計算其平均值與 95% 的信賴區間, 平均值用來繪製圖中的折線, 而信賴區間則用來繪製折線周圍的陰影. 完整程式碼如下 :


測試 6-1 : 呼叫 lineplot() 繪製折線圖 (1) - 單變數多值 [看原始碼]

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd

df=sns.load_dataset('flights')
sns.set()
sns.lineplot(x="year", y="passengers", data=df)
plt.show()

如果每個時間點只有一筆資料,  則就只有一條折線而沒有信賴區間的陰影區, 例如可呼叫 DataFrame 物件的 query() 函式搜尋五月份的旅客資料 : 

>>> may_flights=df.query("month=='May'")     # 搜尋 5 月份資料    
>>> may_flights     
     year month  passengers
4    1949   May         121
16   1950   May         125
28   1951   May         172
40   1952   May         183
52   1953   May         229
64   1954   May         234
76   1955   May         270
88   1956   May         318
100  1957   May         355
112  1958   May         363
124  1959   May         420
136  1960   May         472

可見 query() 會傳回一個新的 DataFrame 物件, 只包含每年五月份的旅客數, 以此 DatFrame 作為 資料源傳給 data 參數來繪製折線圖 : 

>>> sns.lineplot(x="year", y="passengers", data=may_flights)    
<matplotlib.axes._subplots.AxesSubplot object at 0x000001CC38FC9C50>
>>> plt.show()   

結果如下 :




這樣就不會有信賴區間, 只有折線而已了. 完整程式碼如下 :


測試 6-2 : 呼叫 lineplot() 繪製折線圖 (2) - 單變數單值 [看原始碼]

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd

df=sns.load_dataset('flights')
may_flights=df.query("month=='May'") 
sns.set()
sns.lineplot(x="year", y="passengers", data=may_flights)
plt.show()

可以呼叫 DataFrame 物件的 pivot() 函式做樞紐分析, 這樣每年的資料會分月展開成不同欄位, 亦即原本的 passengers 欄位會被拆成 Jan~Dec 之 12 個月份, 例如 : 

>>> flights_wide=df.pivot("year", "month", "passengers")   
>>> flights_wide   
month  Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec
year                                                             
1949   112  118  132  129  121  135  148  148  136  119  104  118
1950   115  126  141  135  125  149  170  170  158  133  114  140
1951   145  150  178  163  172  178  199  199  184  162  146  166
1952   171  180  193  181  183  218  230  242  209  191  172  194
1953   196  196  236  235  229  243  264  272  237  211  180  201
1954   204  188  235  227  234  264  302  293  259  229  203  229
1955   242  233  267  269  270  315  364  347  312  274  237  278
1956   284  277  317  313  318  374  413  405  355  306  271  306
1957   315  301  356  348  355  422  465  467  404  347  305  336
1958   340  318  362  348  363  435  491  505  404  359  310  337
1959   360  342  406  396  420  472  548  559  463  407  362  405
1960   417  391  419  461  472  535  622  606  508  461  390  432

可見 panssenger 欄位已經根據 month 欄位展開了, 以此做為資料源繪圖 : 

>>> sns.lineplot(data=flights_wide)   
<matplotlib.axes._subplots.AxesSubplot object at 0x00000239302116A0>
>>> plt.ylabel('passengers')       
Text(0, 0.5, 'passengers')   
>>> plt.show()    

此處因為呼叫 lineplot() 時無法指定 Y 軸欄位 (因 'passenger' 欄位已被展開), 畫出的圖形沒有 Y 軸標籤, 因此利用 Matplotlib 的 ylabel() 函式來設定, 結果如下 :




可見 Seaborn 會自動將各月欄位列為圖例 (legend), 圖形呈現每年各月旅客數之折線圖. 完整程式碼如下 : 


測試 6-3 : 呼叫 lineplot() 繪製折線圖 (3) - 繪製多變數折線圖 [看原始碼]

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd

sns.set()
df=sns.load_dataset('flights')
flights_wide=df.pivot("year", "month", "passengers")
sns.lineplot(data=flights_wide)
plt.ylabel('passengers')
plt.show()

從上面圖形可知, Seaborn 對於多變數的折線圖預設會用虛線來繪製, 這可以傳入 dashes=False 參數來改成實線, 例如 : 


測試 6-4 : 呼叫 lineplot() 繪製折線圖 (4) - 顯示實線 [看原始碼]

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd

sns.set()
df=sns.load_dataset('flights')
flights_wide=df.pivot("year", "month", "passengers")
sns.lineplot(data=flights_wide, dashes=False)
plt.ylabel('passengers')
plt.show()

結果如下 : 




也可以傳入 markers=True 來顯示資料點標記, 例如 : 


測試 6-5 : 呼叫 lineplot() 繪製折線圖 (5) - 顯示資料點標記 [看原始碼]

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd

sns.set()
df=sns.load_dataset('flights')
flights_wide=df.pivot("year", "month", "passengers")
sns.lineplot(data=flights_wide, dashes=False, markers=True)
plt.ylabel('passengers')
plt.show()

結果如下 : 




可見每一個資料點 (轉折處) 都自動標示不同的標記了 (依月份, 如圖例所示) .

在上面的範例中使用 DataFrame 物件的 pivot() 方法做樞紐分析將月份展開, 但 Seaborn 繪圖函式的 hue 參數可以直接將月份 month 當作第三維變數, 不需要樞紐分析展開 month 就可以直接畫出多變數的折線圖, 例如 : 


測試 6-6 : 呼叫 lineplot() 繪製折線圖 (6) - 使用 hue 參數 [看原始碼]

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd

sns.set()
df=sns.load_dataset('flights')
sns.lineplot(x='year', y='passengers', hue='month', data=df)
plt.show()

此處除了指定 x, y 欄位外, 還用 hue 參數指定第三維變數 month, 結果如下 : 




另外還可以用 style 參數指定要以哪個欄位區分不同樣式, 例如 :


測試 6-7 : 呼叫 lineplot() 繪製折線圖 (7) - 使用 style 參數 [看原始碼]

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd

sns.set()
df=sns.load_dataset('flights')
sns.lineplot(x='year', y='passengers', hue='month', style='month', data=df)
plt.show()

此例以 month 欄位區分樣式, 結果如下 :



結果與上面的測試 6-3 相同. 

除了用 lineplot() 外, 呼叫 relplot() 並傳入 kind='line' 參數也可以繪製折線圖, 例如 : 


測試 6-8 : 呼叫 relplot() 繪製折線圖 - 使用 kind='line' 參數 [看原始碼]

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd

sns.set()
df=sns.load_dataset('flights')
sns.relplot(x='year', y='passengers', hue='month', kind='line', data=df)
plt.show()

結果如下 : 




與上面用 lineplot() 所繪製的折線圖不同之處是, 圖例 (legend) 是放在圖形外的右側, 優點是不會跟圖形搶地盤. 當然也可以傳入 style 參數將實線改為虛線, 或傳入 markers 參數加上資料點標記. 

沒有留言:

張貼留言