Matplotlib指南

本教程介紹了一些基本的使用模式和最佳實踐,以幫助您開始使用Matplotlib。

常規概念

matplotlib擁有廣泛的代碼庫,對許多新用戶來說可能是令人生畏的。但是,大多數matplotlib都可以通過一個相當簡單的概念框架和一些重要知識來理解。

繪圖需要在一系列級別上進行操作,從最一般的(例如,“畫側像2-D陣列”)到最具體的(例如,“將螢幕像素顏色設置為紅色”)。繪圖包的目的是幫助您盡可能輕鬆地可視化數據,並提供所有必要的控制 - 即通過在大多數時間使用相對高級別的命令,並且仍然能夠使用低需要時使用-level命令。

因此,matplotlib中的所有內容都按層次結構進行組織。在層次結構的頂部是matplotlib狀態機環境,它由matplotlib.pyplot模組提供。在此級別,簡單函數用於將繪圖元素(線條,圖像,文本等)添加到當前圖形中的當前軸。

注意:Pyplot的狀態機環境與MATLAB的行為類似,對於具有MATLAB經驗的用戶來說應該是很熟悉的。

層次結構中的下一級是面向對象的介面的第一級,其中pyplot僅用於少數函數,例如圖形創建,並且用戶顯式創建並跟蹤圖形和軸對象。在此級別,用戶使用pyplot來創建圖形,並且通過這些圖形,可以創建一個或多個軸對象。然後,這些軸對象用於大多數繪圖操作。

對於更多的控制 - 對於在GUI應用程式中嵌入matplotlib圖這一點至關重要 - 可以完全刪除pyplot級別,留下面向對象的方法。

# sphinx_gallery_thumbnail_number = 3
import matplotlib.pyplot as plt
import numpy as np

圖的一部分

Figure

整個Figure記錄了所有子軸,一些“特殊”藝術家(標題,圖形圖例等)和畫布。(不要過於擔心畫布,它是至關重要的,因為實際上繪製的對象可以獲得plot,但是作為用戶它或多或少是不可見的)。一個Figure可以有任意數量的Axes,但是有用的應該至少有一個。

創建新 figure 的最簡單方法是使用pyplot

fig = plt.figure()  # an empty figure with no axes
fig.suptitle('No axes on this figure')  # Add a title so we know which it is

fig, ax_lst = plt.subplots(2, 2)  # a figure with a 2x2 grid of Axes


Axes

plot是具有數據空間的圖像區域。給定的圖形可以包含許多軸,但給定的Axes對象只能在一個圖中。Axes包含兩個(或3D的三個)Axis對象(注意AxesAxis的區別),它們負責數據限制(數據限制也可以通過Axes的set_xlim()set_ylim()方法來設置)。每個Axes都有一個標題(通過set_title()設置),一個x標籤(通過set_xlabel()設置)和一個y標籤(通過set_ylabel()設置)。

Axes類及其成員函數是使用OO介面的主入口點。

Axis

它類似數字行的對象。它們負責設置圖形限制並生成刻度線(軸上的標記)和ticklabels(標記刻度線的字串)。刻度的位置由Locator對象確定,ticklabel字串由Formatter格式化。正確的定位器和格式化器的組合可以非常精確地控制刻度位置和標籤。

Artist

基本上在圖上看到的一切都是Artist(甚至是圖,軸和軸對象)。這包括Text對象,Line2D對象,collection對象,Patch對象等等。渲染圖形時,所有Artist都被繪製到畫布上。大多數Artist都與軸有關; 這樣的Artist不能被多個軸共用,也不能從一個軸移動到另一個軸。

繪製函數的輸入類型

所有繪圖函數都期望np.arraynp.ma.masked_array作為輸入。像“數組一樣”的類(如pandas數據對象和np.matrix)可能會或可能不會按預期工作。最好在繪圖之前將它們轉換為np.array對象。

例如,要轉換pandas.DataFrame

a = pandas.DataFrame(np.random.rand(4,5), columns = list('abcde'))
a_asarray = a.values

並轉換np.matrix,如下代碼所示 -

b = np.matrix([[1,2],[3,4]])
b_asarray = np.asarray(b)

Matplotlib,pyplot和pylab之間的關係

Matplotlib是整個包,matplotlib.pyplot是Matplotlib中的一個模組。

對於pyplot模組中的功能,始終存在“當前”圖形和軸(根據請求自動創建)。例如,在下面的示例中,第一次調用plt.plot會創建軸,然後對plt.plot的後續調用會在相同的軸上添加其他行,並且plt.xlabelplt.ylabelplt.titleplt.legend設置軸標籤和標題並添加圖例。

#! /usr/bin/env python
#coding=utf-8
import matplotlib.pyplot as plt
import numpy as np
import math
import seaborn as sns

plt.rcParams['font.sans-serif'] = ['SimHei'] # 步驟一(替換sans-serif字體)
plt.rcParams['axes.unicode_minus'] = False   # 原文出自【許虎虎】,商業轉載請聯繫作者獲得授權,非商業請保留原文鏈接:


x = np.linspace(0, 2, 100)
plt.plot(x, x, label='線性')
plt.plot(x, x**2, label='二次方')
plt.plot(x, x**3, label='立方')

plt.xlabel('X軸標籤')
plt.ylabel('Y軸標籤')

plt.title("簡單繪製(圖表)")

plt.legend()
plt.show()

執行上面示例代碼,得到以下結果 -

pylab是一個便利模組,可以在單個命名空間中批量導入matplotlib.pyplot(用於繪圖)和numpy(用於數學和使用數組)。但不推薦使用pylab,由於命名空間污染而強烈建議不要使用它,請改用pyplot

對於非互動式繪圖,建議使用pyplot創建圖形,然後使用OO介面進行繪圖。

編碼樣式

查看此文檔和示例時,您將看到有不同的編碼樣式和使用模式。這些風格完全有效,各有利弊。幾乎所有示例都可以轉換為另一種樣式並獲得相同的結果。唯一需要注意的是避免為自己的代碼混合編碼樣式。

在不同的風格中,有兩種是官方支持的。這些是使用matplotlib的首選方法。
對於pyplot樣式,通常在腳本頂部的導入:

import matplotlib.pyplot as plt
import numpy as np

然後一個調用,例如,np.arangenp.zerosnp.piplt.figureplt.plotplt.show等。使用pyplot介面創建圖形,然後使用對象方法,參考以下示例代碼:

#! /usr/bin/env python
#coding=utf-8
import matplotlib.pyplot as plt
import numpy as np
import math
import seaborn as sns

plt.rcParams['font.sans-serif'] = ['SimHei'] # 步驟一(替換sans-serif字體)
plt.rcParams['axes.unicode_minus'] = False   # 原文出自【許虎虎】,商業轉載請聯繫作者獲得授權,非商業請保留原文鏈接:


x = np.arange(0, 10, 0.2)
y = np.sin(x)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.show()

執行上面示例代碼,得到以下結果 -

編碼樣式

那麼,為什麼所有額外的輸入代替了MATLAB風格(依賴於全局狀態和平面命名空間)? 對於像這個例子這樣非常簡單的事情,唯一的優勢就是學術上的:更多的風格更明確,更明確地指出事物的來源和發生的事情。對於更複雜的應用程式,這種顯式性和清晰度變得越來越有價值,更豐富和更完整的面向對象的介面可能使程式更容易編寫和維護。

人們發現自己經常一遍又一遍地製作相同的圖,但只是使用不同的數據集,這導致需要編寫專門的函數來進行繪圖。推薦的函數簽名類似於:

#! /usr/bin/env python
#coding=utf-8
import matplotlib.pyplot as plt
import numpy as np
import math
import seaborn as sns

plt.rcParams['font.sans-serif'] = ['SimHei'] # 步驟一(替換sans-serif字體)
plt.rcParams['axes.unicode_minus'] = False   # 原文出自【許虎虎】,商業轉載請聯繫作者獲得授權,非商業請保留原文鏈接:


def my_plotter(ax, data1, data2, param_dict):
    """
    A helper function to make a graph

    Parameters
    ----------
    ax : Axes
        The axes to draw to

    data1 : array
       The x data

    data2 : array
       The y data

    param_dict : dict
       Dictionary of kwargs to pass to ax.plot

    Returns
    -------
    out : list
        list of artists added
    """
    out = ax.plot(data1, data2, **param_dict)
    return out

# which you would then use as:

data1, data2, data3, data4 = np.random.randn(4, 100)
fig, ax = plt.subplots(1, 1)
my_plotter(ax, data1, data2, {'marker': 'x'})

plt.show()

執行上面示例代碼,得到以下結果 -

或者如果想要2個子圖,那麼可以使用以下代碼:

#! /usr/bin/env python
#coding=utf-8
import matplotlib.pyplot as plt
import numpy as np
import math
import seaborn as sns

plt.rcParams['font.sans-serif'] = ['SimHei'] # 步驟一(替換sans-serif字體)
plt.rcParams['axes.unicode_minus'] = False   # 原文出自【許虎虎】,商業轉載請聯繫作者獲得授權,非商業請保留原文鏈接:


def my_plotter(ax, data1, data2, param_dict):
    """
    A helper function to make a graph

    Parameters
    ----------
    ax : Axes
        The axes to draw to

    data1 : array
       The x data

    data2 : array
       The y data

    param_dict : dict
       Dictionary of kwargs to pass to ax.plot

    Returns
    -------
    out : list
        list of artists added
    """
    out = ax.plot(data1, data2, **param_dict)
    return out

# which you would then use as:

data1, data2, data3, data4 = np.random.randn(4, 100)
fig, (ax1, ax2) = plt.subplots(1, 2)
my_plotter(ax1, data1, data2, {'marker': 'x'})
my_plotter(ax2, data3, data4, {'marker': 'o'})


plt.show()

執行上面示例代碼,得到以下結果 -

2個子圖

同樣,對於這些簡單的例子,這種風格似乎有些過分,但是一旦圖表變得稍微複雜一點,就會有好處。

後端

什麼是後端?

網站和郵件列表中的大量文檔都是指“後端”,許多新用戶對此術語感到困惑。matplotlib針對許多不同的用例有不同的輸出格式。有些人在python shell中交互使用matplotlib,並在鍵入命令時彈出繪圖窗口。有些人運行Jupyter筆記本並繪製內聯圖以進行快速數據分析。其他人將matplotlib嵌入到圖形用戶介面(如wxpython或pygtk)中以構建豐富的應用程式。有些人在批處理腳本中使用matplotlib從數值模擬生成postscript圖像,還有一些人運行Web應用程式伺服器來動態提供圖形。

為了支持所有這些用例,matplotlib可以針對不同的輸出,並且這些功能中的每一個都稱為後端; “前端”是面向用戶的代碼,即繪圖代碼,而“後端”完成幕後的所有艱苦工作以製作圖形。有兩種類型的後端:用戶介面後端(用於pygtk,wxpython,tkinter,qt4或macosx;也稱為“互動式後端”)和硬拷貝後端來製作圖像檔(PNG,SVG,PDF,PS;也被稱為“非互動式後端”)。

配置後端有四種方法。如果它們彼此衝突,將使用以下列表中最後提到的方法,例如,調用use()將覆蓋matplotlibrc中的設置。

第1步 - matplotlibrc檔中的後端參數:

backend : WXAgg   # use wxpython with antigrain (agg) rendering

第2步 - 為當前shell或單個腳本設置MPLBACKEND環境變數。在Unix上:

> export MPLBACKEND=module://my_backend
> python simple_plot.py

> MPLBACKEND="module://my_backend" python simple_plot.py

在Windows上,格式為:

> set MPLBACKEND=module://my_backend
> python simple_plot.py

設置此環境變數將覆蓋任何matplotlibrc中的後端參數,即使當前工作目錄中存在matplotlibrc也是如此。因此,全局設置MPLBACKEND,例如 在.bashrc.profile中,但不鼓勵這樣操作,因為它可能導致反直覺的行為。

第3步 - 如果腳本依賴於特定的後端,則可以這樣使用use()函數:

import matplotlib
matplotlib.use('PS')   # generate postscript output by default

如果要使用use()函數,則必須在導入matplotlib.pyplot之前完成此操作。導入pyplot後調用use()將不起作用。如果用戶想要使用不同的後端,則使用use()將需要更改代碼。因此,除非絕對必要,否則應避免顯式調用use()函數。

注 - 後端名稱規範不區分大小寫; 例如,’GTK3Agg’和’gtk3agg’是相同的。

通過matplotlib的典型安裝,例如二進位安裝程式或Linux發行包,可以設置一個好的默認後端,允許互動式工作和從腳本繪圖,輸出到螢幕和/或檔,所以最初不需要使用上面的任何方法。

但是,如果想編寫圖形用戶介面或Web應用程式伺服器(Web應用程式伺服器中的Matplotlib),或者需要更好地瞭解正在發生的事情,請繼續閱讀。為了使圖形用戶介面可以更加自定義,matplotlib將畫布(繪圖所在的位置)的渲染器(實際繪製的東西)的概念分開。用戶介面的規範渲染器是Agg,它使用Anti-Grain Geometry C++庫來製作圖形的光柵(像素)圖像。除macosx之外的所有用戶介面都可以與agg渲染一起使用,例如:WXAgg,GTK3Agg,QT4Agg,QT5Agg,TkAgg。此外,一些用戶介面支持其他渲染引擎。例如,使用GTK + 3,還可以選擇開羅渲染(後端GTK3Cairo)。

對於渲染引擎,還可以區分向量或光柵渲染器。向量圖形語言發出繪圖命令,例如“從此點到此點繪製一條線”,因此無標度,並且柵格後端生成線的像素表示,其精度取決於DPI設置。

以下是matplotlib渲染器的摘要(每個渲染器都有一個同名的後端;這些是非互動式後端,能夠寫入檔):

渲染 檔類型 描述說明
AGG png 光柵圖形 - 使用Anti-Grain Geometry引擎的高質量圖像。
PS ps eps 向量圖形 - Postscript輸出
PDF pdf 向量圖形 - 可移植文檔格式
SVG svg 向量圖形 - 可縮放向量圖形
Cairo png ps pdf svg 光柵圖形和向量圖形 - 使用Cairo圖形庫

以下是支持的用戶介面和渲染器組合; 這些是互動式後端,能夠顯示到螢幕並使用上表中的適當渲染器寫入檔:

後端 描述
Qt5Agg 在Qt5畫布中進行Agg渲染(需要PyQt5)。可以使用%matplotlib qt5在IPython中啟動此後端。
ipympl 嵌入在Jupyter小部件中的Agg渲染(需要ipympl)。可以在帶有%matplotlib ipympl的Jupyter Notebook中啟用此後端。
GTK3Agg Agg渲染到GTK 3.x畫布(需要PyGObject,pycairo或cairocffi)。可以使用%matplotlib gtk3在IPython中啟動此後端。
macosx 在OSX中將渲染轉換為Cocoa畫布。可以使用%matplotlib osx在IPython中啟動此後端。
TkAgg Agg渲染到Tk畫布(需要TkInter)。可以使用%matplotlib tk在IPython中啟動此後端。
nbAgg 在Jupyter Notebook中嵌入一個互動圖像。可以通過%matplotlib Notebook在Jupyter Notebook中啟用此後端。
WebAgg show()上將啟動一個帶有互動式圖形的 tornado 伺服器。
GTK3Cairo Cairo渲染到GTK 3.x畫布(需要PyGObject,pycairo或cairocffi)。
Qt4Agg Agg渲染到Qt4畫布(需要PyQt4或pyside)。可以使用%matplotlib qt4在IPython中啟動此後端。
WXAgg Agg渲染到wxWidgets畫布(需要wxPython 4)。可以使用%matplotlib wx在IPython中啟動此後端。

ipympl

Jupyter小部件生態系統的移動速度太快,無法直接在Matplotlib中支持。安裝ipympl -

pip install ipympl
jupyter nbextension enable --py --sys-prefix ipympl

或者 -

conda install ipympl -c conda-forge

GTK和Cairo

GTK3後端(GTK3Agg和GTK3Cairo)依賴於開羅(pycairo> = 1.11.0或cairocffi)。

如何選擇PyQt4或PySide?
QT_API環境變數可以設置為pyqt或pyside,分別使用PyQt4或PySide。

由於要使用的綁定的默認值是PyQt4,matplotlib首先嘗試導入它,如果導入失敗,它會嘗試導入PySide。

什麼是交互模式?

使用互動式後端允許 - 但本身不需要或確保繪製到螢幕上。是否以及何時繪製到螢幕,以及在螢幕上繪製繪圖後是否繼續腳本或shell會話取決於調用的函數和方法,以及確定matplotlib是否處於“交互模式”的狀態變數”。默認的布爾值由matplotlibrc檔設置,並且可以像任何其他配置參數一樣進行自定義(請參閱使用樣式表和rcParams自定義Matplotlib)。它也可以通過matplotlib.interactive()設置,並且可以通過matplotlib.is_interactive()查詢其值。在繪圖命令流中間打開和關閉交互模式,無論是在腳本還是在shell中,很少需要並且可能令人困惑,因此在下文中我們將假設所有繪圖都是以交互模式打開或關閉。

注 - 在交互到matplotlib版本1.0時,與交互性相關的主要更改,特別是show()的角色和行為,在1.0.1中修復了錯誤。這裏描述主要互動式後端的版本1.0.1行為,部分例外是macosx。

交互模式也可以通過matplotlib.pyplot.ion()打開,並通過matplotlib.pyplot.ioff()關閉。

交互示例

從普通的python提示符,或者在沒有選項的情況下調用ipython之後,試試以下:

import matplotlib.pyplot as plt
plt.ion()
plt.plot([1.6, 2.7])

假設您運行的是1.0.1或更高版本,並且默認情況下安裝並選擇了互動式後端,應該看到一個圖,並且終端提示也應該是活動的; 可以鍵入其他命令,例如:

plt.title("交互測試")
plt.xlabel("index")

會看到每一行後都更新了繪圖。從版本1.5開始,通過其他方式修改繪圖也應該自動更新大多數後端的顯示。獲取對Axes實例的引用,並調用該實例的方法:

ax = plt.gca()
ax.plot([3.1, 2.2])

如果使用的是某些後端(如macosx)或舊版本的matplotlib,則可能無法立即將新行添加到繪圖中。在這種情況下,需要顯式調用draw()以更新繪圖:

plt.draw()

非互動式示例

像上一個示例中一樣,開始一個新會話,但現在關閉交互模式:

import matplotlib.pyplot as plt
plt.ioff()
plt.plot([1.6, 2.7])

執行上面代碼什麼都沒發生 - 或者至少沒有任何東西出現在螢幕上(除非使用macosx後端,那麼會產生異常)。要顯示繪圖,需要執行以下操作:

plt.show()

現在應該看到了繪圖,但終端命令行沒有回應; show()命令會阻止其他命令的輸入,直到手動終止繪圖窗口。

這有什麼好處? - 被迫使用阻止功能。假設您需要一個腳本,將檔內容繪製到螢幕上。想要查看該圖,然後結束腳本。如果沒有一些阻塞命令(如show()),腳本會閃現繪圖,然後立即結束,螢幕上不顯示任何內容。

此外,非交互模式會延遲所有繪圖,直到調用show()為止; 每次腳本中的一行添加新功能時,這比重繪繪圖更有效。

在版本1.0之前,show()通常不能在單個腳本中多次調用(儘管有時可以使用它); 對於1.0.1及更高版本,此限制被取消,因此可以編寫如下腳本:

import numpy as np
import matplotlib.pyplot as plt

plt.ioff()
for i in range(3):
    plt.plot(np.random.rand(10))
    plt.show()

它將一次繪製三個圖。第一個繪圖結束後,第二個繪圖將出現。

總結

在交互模式下,pyplot功能會自動繪製到螢幕上。互動式繪製時,如果除了pyplot函數之外還使用對象方法調用,則每當要刷新繪圖時調用draw()

在要生成一個或多個圖形的腳本中使用非交互模式,並在結束或生成一組新圖形之前顯示它們。在這種情況下,使用show()顯示圖形並阻止執行,直到手動銷毀它們。

性能

無論是以交互模式探索數據還是以編程方式保存大量繪圖,渲染性能都可能是您管道中的一個瓶頸。Matplotlib提供了幾種方法來大大減少渲染時間,但代價是繪圖外觀略有變化(可設置的容差)。可用於縮短渲染時間的方法取決於正在創建的繪圖類型。

線段簡化

對於具有線段的圖(例如,典型的線圖,多邊形的輪廓等),可以通過matplotlibrc檔中的path.simplifypath.simplify_threshold參數控制渲染性能。path.simplify參數是一個布爾值,表示線段是否完全簡化。path.simplify_threshold參數控制簡化的線段數量; 更高的閾值會導致更快的渲染。

以下腳本將首先顯示數據而不進行任何簡化,然後簡化顯示相同的數據。嘗試與它們交互:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

# Setup, and create the data to plot
y = np.random.rand(100000)
y[50000:] *= 2
y[np.logspace(1, np.log10(50000), 400).astype(int)] = -1
mpl.rcParams['path.simplify'] = True

mpl.rcParams['path.simplify_threshold'] = 0.0
plt.plot(y)
plt.show()

mpl.rcParams['path.simplify_threshold'] = 1.0
plt.plot(y)
plt.show()

Matplotlib目前默認為1/9的保守簡化閾值。如果要更改默認設置以使用其他值,可以更改matplotlibrc檔。或者可以為互動式繪圖(具有最大簡化)創建新樣式,並為發佈品質繪圖創建另一種樣式(最小化簡化)並根據需要啟動它們。有關如何執行這些操作的說明,請參閱使用樣式表和rcParams自定義Matplotlib。

通過將線段迭代地合併成單個向量直到下一個線段與向量的垂直距離(在顯示座標空間中測量)大於path.simplify_threshold參數,簡化工作。

注意 - 與版本細分如何簡化相關的更改在版本2.1中進行。2.1之前的這些參數仍將提高渲染時間,但2.1版及更高版本的某些類型數據的渲染時間將大大改善。

標記簡化

標記也可以簡化,儘管不如線段強大。標記簡化僅適用於Line2D對象(通過markevery屬性)。無論在哪里傳遞Line2D構造參數,例如matplotlib.pyplot.plot()matplotlib.axes.Axes.plot(),都可以使用markevery參數:

plt.plot(x, y, markevery=10)

將線分成較小的塊

如果您正在使用Agg後端,那麼可以使用agg.path.chunksize rc參數。它指定塊大小,並且具有大於該多個頂點的任何行將被分割成多行,每行不超過agg.path.chunksize許多頂點。(除非agg.path.chunksize為零,在這種情況下沒有分塊。)對於某些類型的數據,將線條分成合理的大小可以大大減少渲染時間。

以下腳本將首先顯示沒有任何塊大小限制的數據,然後顯示塊大小為10,000的相同數據。當數字很大時,可以最好地看到差異,嘗試最大化GUI然後與它們進行交互:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['path.simplify_threshold'] = 1.0

# Setup, and create the data to plot
y = np.random.rand(100000)
y[50000:] *= 2
y[np.logspace(1,np.log10(50000), 400).astype(int)] = -1
mpl.rcParams['path.simplify'] = True

mpl.rcParams['agg.path.chunksize'] = 0
plt.plot(y)
plt.show()

mpl.rcParams['agg.path.chunksize'] = 10000
plt.plot(y)
plt.show()

Legends

軸的默認圖例行為嘗試查找覆蓋最少數據點的位置(loc ='best')。如果有大量數據點,這可能是非常昂貴的計算。在這種情況下,可能希望提供特定位置。

使用快速的風格

快速樣式可用於自動將簡化和分塊參數設置為合理設置,以加快繪製大量數據的速度。它可以通過運行簡單地使用:

import matplotlib.style as mplstyle
mplstyle.use('fast')

它重量很輕,所以它與其他風格很好地搭配,只需確保最後應用快速樣式,以便其他樣式不會覆蓋設置:

mplstyle.use(['dark_background', 'ggplot', 'fast'])

上一篇: Matplotlib變換 下一篇: Matplotlib pyplot教學