深入淺出 Python 視覺化工具 matplotlib、Seaborn 的基礎與架構全指南與教學

學.誌|Chris Kang
不止數據|Not Only Data
16 min readMar 9, 2021

--

Photo by Clay Banks on Unsplash

最近在使用 matplotlib 畫一些圖時,常常會遇到如子圖、共用 y 軸等問題,甚至在網路上看到範例使用 axisaxe 時,總覺得一頭霧水。而網路上的教學卻僅使用 plt.plot 來繪圖,卻不說明為何要使用這些工具。

因此,我希望這篇文章可以為那些剛入門 matplotlib 的讀者提供一個簡單卻容易理解的基礎觀念。本文會提到下面幾個核心的概念:

  • matplotlib 的核心 API 觀念與組成
  • Matplotlib 的核心繪圖觀念
  • 如何精細控制 Matplotlib 繪圖元件
  • Seaborn 是如何和 matplotlib 協作
  • The future of plot in Python

非常推薦先從核心的 API 與繪圖觀念開始看起,能夠更瞭解 matplotlib 的設計概念,也能夠在未來使用 matplotlib 時,瞭解不同情況下的適用參數。

一、Matplotlib API 概觀

Photo by Yancy Min on Unsplash

我相信很多讀者包括我,在一開始接觸到 matplotlib 時一定會遇到 plt.plotax.plot 到底有什麼差異?為什麼有時候要用 plt.show 有時候不用?

其實在 matplotlibAPI (Application Programming Interface)共分成兩種型態,一種是我們常見到的 plt.plot 或是 df.plot 的格式,另一種則是 ax.plot 的格式。

  • plt.plot|繪圖的懶人包(包括 df.plot ),會幫你直接在最近使用的圖表上繪圖,沒有的話就會自動新建一個。
  • ax.plot|手動建立的 API,提供多項客製化的參數與圖表控制。

以我個人的經驗,我會強烈建議先以 ax.plot 的完整 API 來學習,弄懂背後的完整原理,再來使用簡單的 df.plot 懶人包。

瞭解自己為何而用,才能讓工具發揮最大效益。

那麼,就讓我們來深入瞭解 matplotlib 的概念吧!

二、Matplotlib 的核心繪圖觀念

From matplotlib official website

在繪圖之前,要先搞清楚不同參數代表的意思。

  • Figure:可以把他當作畫布,一張畫布上可以有很多的 Axes。
  • Axes:就是我們俗稱的「子圖」,每個 Axes 一次只能在一張畫布上。

這時就有人說,不對啊?我之前都用 plt.plot 來繪圖,根本沒有用到 figure 和 axes。plt.plot 其實是一個黑盒子,他幫你把基礎的參數設定好,他就會在最近一張子圖生成圖表(沒有則會創建一張);但這不代表背後就沒有 figureaxes 的運作。

Add_subplot: 如何生成 “Figure” and “Axes”?

這裡就來實際操作一次畫布 figure 與子圖 axe 的生成:

這是如果純粹生成一個 figure 的狀態

此時如果直接以 plt.plot(df) 等指令,會在距離最近的途中建立圖。以上面的例子就會是 axe_3。那如果要在 axe_1 上面畫圖怎麼做呢?只要用以下兩種簡單的方式,就可以繪在指定的圖上了:

Sub_plots: 如何快速生成大量 plots ?

如果我們這一次要建立好幾個子圖(axes),那我們不就要新增一整排的變數axe_x?因此衍生的更便利的用法 plt.subplots() 可以使用(和前面的方式差了一個 ”s”),因為 axes 可以直接像二維陣列一樣做索引,也可以一起設定不同子圖間的關連。

這裡也放上子圖(subplots / add_subplot)可以設定的選項以供參考:

  • nrows|子圖有幾列,通常都會直接放上數字。
  • ncols|子圖有幾欄,通常都會直接放上數字。
  • sharex|所有子圖是否共用一樣的 x 軸,調整 xlim 會影響呈現範圍。
  • sharey|所有子圖是否共用一樣的 x 軸,調整 ylim 會影響呈現範圍。

三、如何精細控制 Matplotlib 繪圖元件

當我們已經可以輕鬆掌握 Figure 和 axe 來進行繪製後,接下來可以更細部操作的便是每張圖的細節了。這也是最讓人興奮與實用的各項小技巧,如果在閱讀時對特定的設計方式感興趣,也推薦去官網的 gallary 瀏覽。

在這個章節中,筆者會介紹下面幾個常用的環節:

  1. matplotlib 細節設定
  2. 如何控制各種繪圖風格
  3. 刻度、標籤和圖形
  4. 註釋與繪製箭頭等形狀
  5. 儲存圖檔

1. matplotlib 細節設定

matplotlib 本身在繪製時,就有許多的配置方案,可以用來控制各種細節甚至適用於出版的圖片。如果想要直接控制這些設定,可以使用 rc 方法:

如果想要一勞永逸地改掉這些設定呢?一樣可以到 home 目錄下修改:

matplotlib/mpl-data/.matplotlibrc

2. 如何控制各種繪圖風格

到這個階段,真的要來繪製 plot 本身了!我們就先從最常見到的折線圖開始說起。當我們繪製折線圖時,其實本身點與點之間是以線性插值來連接,但如果我想要能夠看到變化率呢?我們變可以更改 drawstyle

在設定參數時,多數的繪圖方法都可以接受直接以 axe 作為參數,來更有彈性地繪製子圖。下面變順便列出大部分常用的參數:

  • label|圖例用的標籤,如上圖的 “Default”。
  • ax|指定要拿來畫圖的 axe,若沒有指定則會由最靠近呼叫的 axe 畫起
  • style|用來傳樣式字串,例如 ‘b-’ 等。
  • alpha|設定 plot 的透明度
  • kind|可用 areabarbarhdensityhistkdelinepie
  • logy|在 y 軸使用對數呈現,logx 亦同。
  • use_index|以物件的 index 作為刻度標籤,預設為 True。
  • rot|選轉刻度的標籤,如 rot=30 即為選轉 30 度。
  • xticks|x 軸刻度用的值,可以傳入一個 list 來指定,yticks 亦同。
  • xlim|x 軸的範圍,如 [0,100];ylim 亦同。
  • grid|顯示軸的格線,預設為 False

同樣地,如果想要直接以 DataFrame 的黑盒子快速繪圖,也可以參考以下的參數:

  • subplots|如傳入整個 df,則可將每欄繪製到不同的子圖中。
  • sharex|如為 True 時,所有的 x 軸、刻度、範圍皆相同;sharey 亦同
  • figsize|以 figsize=(6,4) 指定 figure 大小。
  • title|字串型態,指定標題。
  • legend|加入子圖的圖例,預設為 True
  • sort_columns|預設為使用既有的欄位排序,可調整排序。

b. Other Plot Usage

  • Barplot|以 DataFrame 作為輸入時,會以一列中的多個值來作為圖例標題使用;若在參數中加入 stacked=True,則會形成疊加長條圖。
  • density|密度圖(KDE),呼叫 plot.kde 時會使用混合常態估計法。
  • Hist|直方圖,要指定 bins=50 的數量,常用數量為資料量的開根號

3. 刻度、標籤和圖形

大部分的繪圖都會由 xlimxticksxticklabelsxlabels 四個方法組成圖面。其中分成以 plt 的介面和原生的 matplotlib 的 API。個別的意義分別為:

  • xlim|x 軸的範圍,如 [0,100];ylim 亦同。
  • xticks|x 軸刻度用的值,可以傳入一個 list 來指定,yticks 亦同。
  • xticklabels|用於設定離散的 x 軸,可以傳入一組 list 來設定。

Pyplot 的設定方式:
會繪製在最後一個子圖或目前使用的子圖上。

  • 不帶參數呼叫|會傳回當前的參數之值,例如 plt.xlim() 會傳回範圍。
  • 帶著參數呼叫|會設定新的值,如 plt.xlim([0, 100]) 會設定新範圍。

原生 API 的設定方式
會直接繪製在指定的子圖上,主要分成 get_set_ 兩種設定方法。

  • axe.get_xlabel()|便會傳回當下 x 軸的軸名稱。
  • axe.set_xlabel()|便會設定當下 x 軸的軸名稱。

設定方法亦可以加入 rotation=30fontsize=’small’ 等參數如 set_xlabel(ratation=10),來進一步設定子圖的刻度與標籤。與上面的例子相同,我們也可以一次用 arg 的方式傳入所有的變數。

有了客製化的標籤,就要加入圖例讀者才容易瞭解不同的線代表哪筆資料。加入的方法主要有兩種:

  • axe.plot(label=’label_1’)
  • axe.legend(labels=[‘label_1’, ‘label_2’])

接著如果使用第一種,則要記得呼叫 plt.legend()axe.legend()。另外如果要設定圖例的位置,可以簡單地使用 loc='best' ,會幫你找不會蓋到圖表的地方呈現,抑或直接指定位置。

4. 註釋與繪製箭頭等形狀

除了圖例之外,有時也會需要繪製一些文字(text)、箭頭(arrow)或註釋(annotate)等,這時就可以使用下面三個方法:

  • axe.text|在子圖的指定座標加上文字
  • axe.arrow|在子圖的指定座標加上箭頭
  • axe.annotate|註釋可以直接劃上文字和箭頭。

a. axe.text/axe.arrow 可以簡單如以下方式呈現:

b. axe.annotate 則可設定複雜的方法來呈現:

5. 儲存圖檔

當我們繪製好圖表後,有時最後一階段便需要輸出圖檔,這時便可簡單使用 savefig 來儲存當前的 figure。舉例來說,如果想要把圖存成 png:

plt.savefig('savefig.png')

程式會根據你附上的附檔名進行判別,自動生成該類型的檔案。主要儲存的方式一樣分成兩種,一種是 pyplot 如 df.plot 的 API;另一種則是 matplotlib 的原生 API:

  • plt.savefig|會儲存當前的 figure。
  • fig.savefig|會儲存 figure 實例化後 fig 畫布上的圖。

在輸出檔案時,有下列參數能夠更細部地調整輸出的圖,舉例來說:

  • fname|檔案名稱與位置路徑,以附檔名為檔案型態判斷依據。
  • dpi|圖片每英吋的解析度,預設為 100dpi。
  • facecolor|子圖外的背景顏色,預設為 ‘w’(白色)
  • edgecolor|子圖外的邊框顏色,預設為 ‘w’(白色)
  • format|明確指定要用的檔案格式。
  • bbox_inches|指定圖存檔部分,若指定為 tight 則會將周圍空白去掉

以上就是我們從建立 figure 畫布、axe 子圖一路到輸出圖表的過程,相信讀者也已經對於如何製作與調整一張圖表,有了比較明確的概念。接著,我們就來講 matplotlib 的高階 API –––– Seaborn。

五、How does Seaborn work?

如果讀者曾經聽過深度學習的框架,那 matplotlibSeaborn 的關係就和 TenserflowKerasPytorchFast.ai 的關係一模一樣。

Seaborn 背後的底層工具都仍然屬於 matplotlib,但透過封裝的方式大幅度地簡化許多設定上的細節,且也美化的圖表本身的輸出。因此就算只是 import seaborn 本身,也能夠提升 matplotlib 的繪圖視覺美感。

沒事多用 seaborn,圖表看起來就會更漂亮:

import seaborn as sns

為了讓讀者感受一下差異,我們下面就實作幾個圖表來示範。本文會帶到下面五個常用的顯示方式:

  1. 使用 Seaborn 調整 Theme
  2. Barplot 直方圖
  3. Displot 分布密度圖
  4. Regplot 分布密度圖
  5. Pairplot 散佈圖矩陣
  6. Facetgrid 層面圖與分組

1. 使用 Seaborn 調整 Theme

可以看到從 seaborn 輸出的圖明顯多了外框,整體視覺也比較好看。因此筆者在後來在進行 EDA 時,若圖需要輸出都會習慣使用 seaborn 來美化圖。

若要在一開始就調整 seaborn 的風格,可以簡單地使用 set 來調整:

sns.set(style="white") # darkgrid, whitegrid, dark, white, ticks

2. Barplot 直方圖

image from seaborn

3. Displot 分布密度圖

使用 seaborn 的 displot 時,會同時呈現直方圖和密度圖,可以更好地去評估分布的組成。

4. Regplot 分布密度圖

Seaborn 的 regplot 可同時繪製出散佈圖(scatter)和回歸(Regression),可以很快速地評估該資料是否有某種趨勢。

5. Pairplot 散佈圖矩陣

在探索資料時,若能看到不同變數間的散佈圖非常有幫助,但如果一個一個繪製不僅容易忘記,也很難直接看到不同變數間的關係。這時 pairplot 就派上用場了。

sns.pairplot(penguins, hue="species", diag_kind="hist")

也許你已經看到函式裡有 diag_kind 的參數,這個參數能讓我們調整離散的資料會以 histkde 等方式呈現。更詳細的設定內容請察看官網。

6. Facetgrid 層面圖與分組

我們可以使用 facetgrid 來依照多個分類進行視覺化。

axe = sns.FacetGrid(tips, col="time",  row="sex")
axe.map(sns.scatterplot, "total_bill", "tip")

更細節的操作可以直接參考官方文件,但官方文件也提到若在使用 FacetGrid 時,仍然盡可能地轉換資料到正確的型態,如類別資料轉換成 category;數值資料則轉換成 intfloat

同樣地如果要讓 figure 空白變少,可以使用 tight_layout() 來縮減空白。

fig.tight_layout()

六、The future of plot in Python

隨著資料視覺化的發展,越來越多的人發現靜態的圖表仍然很難呈現所需的所有資料,因此便開始出現了互動圖表,其中在 Python 的領域最有名的新工具便是 Plotly,打著簡單好用的方式便能生成出互動圖表。

互動圖表特別適合應用在 Data-wharehose 的資料視覺化工具,就像 Tableau 的 Dashboard 一樣能夠即時檢視與操作,特別推薦有興趣與需求的讀者,可以來上面的官方網站瀏覽看看喔!

Summary

過去在使用 matplotlib 時,總是必須要東找西找才能找到自己需要的圖表,也常常不確定 axe.plot 和 df.plot 的差異在哪裡,始終囫圇吞棗的用。今天終於把這篇文章整理完了,也算是把之前欠的資料視覺化的債還了一些。

在學習的過程中,推薦讀者也能跟著文中的案例實際操作,並多結合現在工作上、學習上的實際例子進行視覺化的練習,最終學會從資料中判別洞見才是最重要的環節。希望這篇文章能夠對讀者有幫助。

**【希望用你的掌聲來投票與支持】**
拍 5~10 下:簽個到,表示支持(感謝你的鼓勵啊啊啊)
拍 10~30 下:希望我可以多寫一些文章!有你這位讀者,寫這篇也心滿意足了!
拍 30~50 下:內容對你感覺很有共鳴,希望能多分享給周圍的朋友!

謝謝你/妳,願意把我的文章閱讀完

如果你喜歡筆者在 Medium 的文章,可以拍個手(Claps),
也歡迎你分享給你覺得有需要的朋友們。

You May Interest In:

--

--

學.誌|Chris Kang
不止數據|Not Only Data

嗨!我是 Chris,一位擁有技術背景的獵頭,熱愛解決生活與職涯上的挑戰。專注於產品管理/資料科學/前端開發 / 人生成長,在這條路上,歡迎你找我一起聊聊。歡迎來信合作和交流: chriskang0917@gmail.com