教程 | 如何優雅而高效地使用Matplotlib實現數據可視化

 2018-01-16 11:04:00.0

原標題:教程 | 如何優雅而高效地使用Matplotlib實現數據可視化

選自pbpython

Matplotlib 能創建非常多的可視化圖表,它也有一個豐富的 Python 工具生態環境,很多更高級的可視化工具使用 Matplotlib 作爲基礎庫。因此本文旨在提供一種高效的 Matplotlib 使用方法,並希望該方法可以幫助大家理解如何更有效地進行日常數據分析工作。

簡介

對新手來說 Python 可視化實在有些令人挫敗。有很多不同的選項,如何選擇正確的選項是一個挑戰。例如,兩年前這篇文章《Overview of Python Visualization Tools》仍然吸引了大量讀者。在那篇文章中,我否定了 Matplotlib。但是,在使用過 pandas、scikit-learn、seaborn 和其他 Python 數據科學包之後,我覺得之前否認 Matplotlib 的行爲有點不成熟。坦白講,當時我不是很瞭解 Matplotlib,也不懂如何在我的工作流中高效使用 Matplotlib。

現在我學習了一些工具,瞭解瞭如何基於 Matplotlib 使用這些工具,Matplotlib 逐漸變成了可視化工具的核心。本文將展示如何使用 Matplotlib。我堅定地認爲 Matplotlib 是 Python 數據科學包必不可少的一部分,希望這篇文章可以幫助大家瞭解如何使用 Matplotlib 進行 Python 可視化。

爲什麼大家都在否定 Matplotlib?

我認爲,Matplotlib 對於新手來說比較難存在幾個原因。首先,Matplotlib 有兩個界面。第一個界面基於 MATLAB,使用基於狀態的接口。第二個界面是面向對象的接口。本文就不展開介紹 Matplotlib 有兩個界面的原因,但瞭解這兩種方法在使用 Matplotlib 繪圖時會很重要。兩個界面會引起混淆的原因可以通過 Stack Overflow 和谷歌搜索查找一些信息。此外,新用戶將發現混淆問題有多個解決方案,但是這些問題看起來類似卻不完全相同。從我的個人經驗來講,我們從以前的代碼中可以看出有一些 Matplotlib 代碼的混雜。

關鍵點

Matplotlib 新手應該學習和使用面向對象的接口。

使用 Matplotlib 的另一個歷史性挑戰是一些默認的樣式缺乏吸引力。在 R 使用 ggplot 就可以生成相當不錯的圖,而 Matplotlib 相對來說有點醜。好消息是 Matplotlib 2.0 中的樣式好看了很多,你可以用最小的努力生成可視化。

第三個挑戰是你不確定什麼時候該使用 Matplotlib,什麼時候該使用基於 Matplotlib 構建的工具,如 pandas 或 seaborn。大部分時候做一件事都有多種選擇,但是對於新手來說選擇正確的道路有些困難。

爲什麼使用 Matplotlib?

儘管 Matplotlib 有這麼多問題,我還是喜歡用它。因爲它很強大,這個庫允許你創建幾乎所有的可視化圖表。此外,圍繞 Matplotlib 有一個豐富的 Python 工具生態環境,很多更高級的可視化工具使用 Matplotlib 作爲基礎庫。因此如果你想在 Python 數據科學工具包中進行任何操作,你需要對如何使用 Matplotlib 有一些基礎瞭解。這就是本文其餘部分的重點,提供一種高效使用 Matplotlib 的基礎方法。

前提

推薦以下步驟學習如何使用 Matplotlib:

1. 學習 Matplotlib 的基本術語,具體來說就是什麼是 Figure 和 Axes。

2. 一直使用面向對象的界面,養成習慣。

3. 用基礎的 pandas 繪圖開始可視化。

4. 使用 seaborn 進行稍微複雜的數據可視化。

5. 使用 Matplotlib 自定義 pandas 或 seaborn 可視化。

下圖非常重要,有助於理解圖的不同術語。

大部分術語很直接易懂,需要牢記的是 Figure 是可能包含一或多個 axes 的最終圖像。Axes 代表單個圖。一旦你理解這些是什麼以及如何通過面向對象的 API 評估它們,其餘步驟就很簡單了。

瞭解這個知識還有一個好處,就是當你在網絡上看東西的時候有一個出發點。如果你花時間瞭解了這個點,那麼其他的 Matplotlib API 纔有意義。此外,很多高級 Python 包,如 seaborn 和 ggplot 依賴於 Matplotlib 構建,因此理解了基礎,學習更強大的框架才更加容易。

最後,我不是說你應該逃避其他優秀選項,如 ggplot(又名 ggpy)、bokeh、plotly 或 altair。我只是認爲你需要對 matplotlib + pandas + seaborn 有一個基礎的瞭解。瞭解基礎可視化棧之後,你就可以探索其他優秀工具,根據需求做出合適的選擇。

開始

下面主要介紹如何在 pandas 中創建基礎的可視化以及使用 Matplotlib 定製最常用的項。瞭解基礎流程有助於更直觀地進行自定義。

我主要關注最常見的繪圖任務,如標註軸、調整圖形界限(limit)、更新圖標題、保存圖像和調整圖例。

開始,我打算設置輸入,讀取一些數據:

  1. importpandas aspdimport matplotlib.pyplot aspltfrom matplotlib.ticker importFuncFormatter

  2. df =pd.read_excel("https://github.com/chris1610/pbpython/blob/master/data/sample-salesv3.xlsx?raw=true")

  3. df.head()

數據包括 2014 年的銷售交易額。爲簡短起見,我將總結這些數據,列出前十名客戶的採購次數和交易額。繪圖時我將對各列進行重命名。

  1. top_10 =(df.groupby('name')['ext price','quantity'].agg({'ext price':'sum','quantity':'count'})

  2. .sort_values(by='ext price',ascending=False))[:10].reset_index()

  3. top_10.rename(columns={'name':'Name','ext price':'Sales','quantity':'Purchases'},inplace=True)

下圖是數據。

現在數據以簡單的表格形式呈現,我們再來看一下如何將數據繪製成條形圖。如前所述,Matplotlib 具備多種不同風格,可用於渲染圖表。你可以使用 plt.style.available 查看你的系統可用的風格。

  1. plt.style.available

  2. ['seaborn-dark','seaborn-dark-palette','fivethirtyeight','seaborn-whitegrid','seaborn-darkgrid','seaborn','bmh','classic','seaborn-colorblind','seaborn-muted','seaborn-white','seaborn-talk','grayscale','dark_background','seaborn-deep','seaborn-bright','ggplot','seaborn-paper','seaborn-notebook','seaborn-poster','seaborn-ticks','seaborn-pastel']

使用如下簡單風格:

  1. plt.style.use('ggplot')

現在我們有了好看的風格,第一步就是使用標準 pandas 繪圖函數繪製數據:

  1. top_10.plot(kind='barh',y="Sales",x="Name")

推薦使用 pandas 繪圖的原因在於它是一種快速便捷地建立可視化原型的方式。

自定義圖表

如果你對該圖表的重要部分都很滿意,那麼下一步就是對它執行自定義。一些自定義(如添加標題和標籤)可以使用 pandas plot 函數輕鬆搞定。但是,你可能會發現自己需要在某個時刻跳出來。這就是我推薦你養成以下習慣的原因:

  1. fig,ax =plt.subplots()

  2. top_10.plot(kind='barh',y="Sales",x="Name",ax=ax)

生成的圖表和原始圖表基本一樣,不過我們向 plt.subplots() 添加了一個額外的調用,並將 ax 傳輸至繪圖函數。因此,通過 ax 或 fig 對象可以執行任何自定義。

我們利用 pandas 實現快速繪圖,現在利用 Matplotlib 獲取所有功能。通過使用命名慣例,調整別人的解決方案適應自己的需求變得更加直接簡單了。

假設我們想調整一些軸標籤,且 ax 變量中有多個軸,可以進行一些操作:

  1. fig,ax =plt.subplots()

  2. top_10.plot(kind='barh',y="Sales",x="Name",ax=ax)

  3. ax.set_xlim([-10000,140000])

  4. ax.set_xlabel('Total Revenue')

  5. ax.set_ylabel('Customer');

這是另一種改變標題和標籤的簡單方式:

  1. fig,ax =plt.subplots()

  2. top_10.plot(kind='barh',y="Sales",x="Name",ax=ax)

  3. ax.set_xlim([-10000,140000])

  4. ax.set(title='2014 Revenue',xlabel='Total Revenue',ylabel='Customer')

爲了進一步展示該方法,我們還可以使用 plt.subplots() 函數可以定義圖像尺寸,一般以英寸爲單位。我們還可以使用 ax.legend().set_visible(False) 移除圖例。

  1. fig,ax =plt.subplots(figsize=(5,6))

  2. top_10.plot(kind='barh',y="Sales",x="Name",ax=ax)

  3. ax.set_xlim([-10000,140000])

  4. ax.set(title='2014 Revenue',xlabel='Total Revenue')

  5. ax.legend().set_visible(False)

要想修改這個圖像,你可能需要執行很多操作。圖中最礙眼的可能是總收益額的格式。Matplotlib 可以使用 FuncFormatter 解決這一問題。該函數用途多樣,允許用戶定義的函數應用到值,並返回格式美觀的字符串。

以下是貨幣格式化函數,用於處理數十萬美元區間的數值:

  1. defcurrency(x,pos):

  2. 'The two args are the value and tick position'

  3. ifx >=1000000:return'${:1.1f}M'.format(x*1e-6)return'${:1.0f}K'.format(x*1e-3)

現在我們有了格式化程序函數,就需要定義它,並將其應用到 x 軸。完整代碼如下:

  1. fig,ax =plt.subplots()

  2. top_10.plot(kind='barh',y="Sales",x="Name",ax=ax)

  3. ax.set_xlim([-10000,140000])

  4. ax.set(title='2014 Revenue',xlabel='Total Revenue',ylabel='Customer')

  5. formatter =FuncFormatter(currency)

  6. ax.xaxis.set_major_formatter(formatter)

  7. ax.legend().set_visible(False)

這張圖美觀多了,非常好地展示了自定義問題解決方案的靈活性。最後要說的自定義特徵是向圖表添加註釋。你可以使用 ax.axvline() 畫垂直線,使用 ax.text() 添加自定義文本。就以上示例,我們可以畫一條表示平均值的線,包括代表 3 個新客戶的標籤。以下是完整代碼(包括註釋):

  1. # Create the figure and the axes

  2. fig,ax =plt.subplots()

  3. # Plot the data and get the averaged

  4. top_10.plot(kind='barh',y="Sales",x="Name",ax=ax)

  5. avg =top_10['Sales'].mean()

  6. # Set limits and labels

  7. ax.set_xlim([-10000,140000])

  8. ax.set(title='2014 Revenue',xlabel='Total Revenue',ylabel='Customer')

  9. # Add a line for the average

  10. ax.axvline(x=avg,color='b',label='Average',linestyle='--',linewidth=1)

  11. # Annotate the new customers

  12. forcust in[3,5,8]:

  13. ax.text(115000,cust,"New Customer")

  14. # Format the currency

  15. formatter =FuncFormatter(currency)

  16. ax.xaxis.set_major_formatter(formatter)

  17. # Hide the legend

  18. ax.legend().set_visible(False)

圖表

目前,我們所做的所有改變都是針對單個圖表。我們還能夠在圖像上添加多個表,使用不同的選項保存整個圖像。

如果我們確定要在同一個圖像上放置兩個表,那麼我們應該對如何做有一個基礎瞭解。首先,創建圖像,然後創建軸,再將它們繪製成圖表。使用 plt.subplots() 可以完成該操作:

  1. fig,(ax0,ax1)=plt.subplots(nrows=1,ncols=2,sharey=True,figsize=(7,4))

在這個例子中,我使用 nrows 和 ncols 指定大小,這對新用戶來說比較清晰易懂。我還使用 sharey=True 以使 y 軸共享相同的標籤。

該示例很靈活,因爲不同的軸可以解壓成 ax0 和 ax1。現在我們有了這些軸,就可以像上述示例中那樣繪圖,然後把一個圖放在 ax0 上,另一個圖放在 ax1。

  1. # Get the figure and the axes

  2. fig,(ax0,ax1)=plt.subplots(nrows=1,ncols=2,sharey=True,figsize=(7,4))

  3. top_10.plot(kind='barh',y="Sales",x="Name",ax=ax0)

  4. ax0.set_xlim([-10000,140000])

  5. ax0.set(title='Revenue',xlabel='Total Revenue',ylabel='Customers')

  6. # Plot the average as a vertical line

  7. avg =top_10['Sales'].mean()

  8. ax0.axvline(x=avg,color='b',label='Average',linestyle='--',linewidth=1)

  9. # Repeat for the unit plot

  10. top_10.plot(kind='barh',y="Purchases",x="Name",ax=ax1)

  11. avg =top_10['Purchases'].mean()

  12. ax1.set(title='Units',xlabel='Total Units',ylabel='')

  13. ax1.axvline(x=avg,color='b',label='Average',linestyle='--',linewidth=1)

  14. # Title the figure

  15. fig.suptitle('2014 Sales Analysis',fontsize=14,fontweight='bold');

  16. # Hide the legends

  17. ax1.legend().set_visible(False)

  18. ax0.legend().set_visible(False)

現在,我已經在 jupyter notebook 中用 %matplotlib inline 展示了很多圖像。但是,在很多情況下你需要以特定格式保存圖像,將其和其他呈現方式整合在一起。

Matplotlib 支持多種不同文件保存格式。你可以使用 fig.canvas.get_supported_filetypes() 查看系統支持的文件格式:

  1. fig.canvas.get_supported_filetypes()

  2. {'eps':'Encapsulated Post','jpeg':'Joint Photographic Experts Group','jpg':'Joint Photographic Experts Group','pdf':'Portable Document Format','pgf':'PGF code for LaTeX','png':'Portable Network Graphics','ps':'Post','raw':'Raw RGBA bitmap','rgba':'Raw RGBA bitmap','svg':'Scalable Vector Graphics','svgz':'Scalable Vector Graphics','tif':'Tagged Image File Format','tiff':'Tagged Image File Format'}

我們有 fig 對象,因此我們可以將圖像保存成多種格式:

  1. fig.savefig('sales.png',transparent=False,dpi=80,bbox_inches="tight")

結論

該版本將圖表保存爲不透明背景的 png 文件。我還指定 dpi 和 bbox_inches="tight" 以最小化多餘空白。最後,希望該方法可以幫助大家理解如何更有效地使用 Matplotlib 進行日常數據分析。

原文鏈接:http://pbpython.com/effective-matplotlib.html


文章來源:機器之心