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'])