您的位置:首頁>正文

如何優雅而高效地使用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)、更新圖標題、保存圖像和調整圖例。

開始, 我打算設置輸入, 讀取一些資料:

import pandas as pdimport matplotlib.pyplot as pltfrom matplotlib.ticker import FuncFormatterdf = pd.read_excel("https://github.com/chris1610/pbpython/blob/master/data/sample-salesv3.xlsx?raw=true")df.head()

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

top_10 = (df.groupby('name')['ext price', 'quantity'].agg({'ext price': 'sum', 'quantity': 'count'}) .sort_values(by='ext price', ascending=False))[:10].reset_index()top_10.rename(columns={'name': 'Name', 'ext price': 'Sales', 'quantity': 'Purchases'}, inplace=True)

下圖是數據。

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

plt.style.available['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']

使用如下簡單風格:

plt.style.use('ggplot')

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

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

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

自訂圖表

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

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

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

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

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

fig, ax = plt.subplots()top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)ax.set_xlim([-10000, 140000])ax.set_xlabel('Total Revenue')ax.set_ylabel('Customer');

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

fig, ax = plt.subplots()top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)ax.set_xlim([-10000, 140000])ax.set(title='2014 Revenue', xlabel='Total Revenue', ylabel='Customer')

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

fig, ax = plt.subplots(figsize=(5, 6))top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)ax.set_xlim([-10000, 140000])ax.set(title='2014 Revenue', xlabel='Total Revenue')ax.legend().set_visible(False)

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

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

def currency(x, pos): 'The two args are the value and tick position' if x >= 1000000: return '${:1.1f}M'.format(x*1e-6) return '${:1.0f}K'.format(x*1e-3)

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

fig, ax = plt.subplots()top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)ax.set_xlim([-10000, 140000])ax.set(title='2014 Revenue', xlabel='Total Revenue', ylabel='Customer')formatter = FuncFormatter(currency)ax.xaxis.set_major_formatter(formatter)ax.legend().set_visible(False)

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

# Create the figure and the axesfig, ax = plt.subplots()# Plot the data and get the averagedtop_10.plot(kind='barh', y="Sales", x="Name", ax=ax)avg = top_10['Sales'].mean()# Set limits and labelsax.set_xlim([-10000, 140000])ax.set(title='2014 Revenue', xlabel='Total Revenue', ylabel='Customer')# Add a line for the averageax.axvline(x=avg, color='b', label='Average', linestyle='--', linewidth=1)# Annotate the new customersfor cust in [3, 5, 8]: ax.text(115000, cust, "New Customer")# Format the currencyformatter = FuncFormatter(currency)ax.xaxis.set_major_formatter(formatter)# Hide the legendax.legend().set_visible(False)

圖表

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

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

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

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

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

# Get the figure and the axesfig, (ax0, ax1) = plt.subplots(nrows=1,ncols=2, sharey=True, figsize=(7, 4))top_10.plot(kind='barh', y="Sales", x="Name", ax=ax0)ax0.set_xlim([-10000, 140000])ax0.set(title='Revenue', xlabel='Total Revenue', ylabel='Customers')# Plot the average as a vertical lineavg = top_10['Sales'].mean()ax0.axvline(x=avg, color='b', label='Average', linestyle='--', linewidth=1)# Repeat for the unit plottop_10.plot(kind='barh', y="Purchases", x="Name", ax=ax1)avg = top_10['Purchases'].mean()ax1.set(title='Units', xlabel='Total Units', ylabel='')ax1.axvline(x=avg, color='b', label='Average', linestyle='--', linewidth=1)# Title the figurefig.suptitle('2014 Sales Analysis', fontsize=14, fontweight='bold');# Hide the legendsax1.legend().set_visible(False)ax0.legend().set_visible(False)

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

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

fig.canvas.get_supported_filetypes(){'eps': 'Encapsulated Postscript', 'jpeg': 'Joint Photographic Experts Group', 'jpg': 'Joint Photographic Experts Group', 'pdf': 'Portable Document Format', 'pgf': 'PGF code for LaTeX', 'png': 'Portable Network Graphics', 'ps': 'Postscript', '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 物件,因此我們可以將圖像保存成多種格式:

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

結論

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

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

plt.style.available['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']

使用如下簡單風格:

plt.style.use('ggplot')

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

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

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

自訂圖表

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

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

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

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

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

fig, ax = plt.subplots()top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)ax.set_xlim([-10000, 140000])ax.set_xlabel('Total Revenue')ax.set_ylabel('Customer');

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

fig, ax = plt.subplots()top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)ax.set_xlim([-10000, 140000])ax.set(title='2014 Revenue', xlabel='Total Revenue', ylabel='Customer')

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

fig, ax = plt.subplots(figsize=(5, 6))top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)ax.set_xlim([-10000, 140000])ax.set(title='2014 Revenue', xlabel='Total Revenue')ax.legend().set_visible(False)

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

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

def currency(x, pos): 'The two args are the value and tick position' if x >= 1000000: return '${:1.1f}M'.format(x*1e-6) return '${:1.0f}K'.format(x*1e-3)

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

fig, ax = plt.subplots()top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)ax.set_xlim([-10000, 140000])ax.set(title='2014 Revenue', xlabel='Total Revenue', ylabel='Customer')formatter = FuncFormatter(currency)ax.xaxis.set_major_formatter(formatter)ax.legend().set_visible(False)

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

# Create the figure and the axesfig, ax = plt.subplots()# Plot the data and get the averagedtop_10.plot(kind='barh', y="Sales", x="Name", ax=ax)avg = top_10['Sales'].mean()# Set limits and labelsax.set_xlim([-10000, 140000])ax.set(title='2014 Revenue', xlabel='Total Revenue', ylabel='Customer')# Add a line for the averageax.axvline(x=avg, color='b', label='Average', linestyle='--', linewidth=1)# Annotate the new customersfor cust in [3, 5, 8]: ax.text(115000, cust, "New Customer")# Format the currencyformatter = FuncFormatter(currency)ax.xaxis.set_major_formatter(formatter)# Hide the legendax.legend().set_visible(False)

圖表

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

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

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

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

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

# Get the figure and the axesfig, (ax0, ax1) = plt.subplots(nrows=1,ncols=2, sharey=True, figsize=(7, 4))top_10.plot(kind='barh', y="Sales", x="Name", ax=ax0)ax0.set_xlim([-10000, 140000])ax0.set(title='Revenue', xlabel='Total Revenue', ylabel='Customers')# Plot the average as a vertical lineavg = top_10['Sales'].mean()ax0.axvline(x=avg, color='b', label='Average', linestyle='--', linewidth=1)# Repeat for the unit plottop_10.plot(kind='barh', y="Purchases", x="Name", ax=ax1)avg = top_10['Purchases'].mean()ax1.set(title='Units', xlabel='Total Units', ylabel='')ax1.axvline(x=avg, color='b', label='Average', linestyle='--', linewidth=1)# Title the figurefig.suptitle('2014 Sales Analysis', fontsize=14, fontweight='bold');# Hide the legendsax1.legend().set_visible(False)ax0.legend().set_visible(False)

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

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

fig.canvas.get_supported_filetypes(){'eps': 'Encapsulated Postscript', 'jpeg': 'Joint Photographic Experts Group', 'jpg': 'Joint Photographic Experts Group', 'pdf': 'Portable Document Format', 'pgf': 'PGF code for LaTeX', 'png': 'Portable Network Graphics', 'ps': 'Postscript', '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 物件,因此我們可以將圖像保存成多種格式:

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

結論

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

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示