【翻譯搬運】Matplotlib - 用Python繪製2D和3D圖像

Matplotlib是Python中最常用的可視化工具之一,可以非常方便地創建海量類型的2D圖表和一些基本的3D圖表。本文翻譯自Jupyter nbviewer中的第四講,主要介紹了繪製2D圖像的相關信息,圖像的位置、大小,曲線的樣式、寬度,坐標軸的刻度、數值、標籤,以及圖例、標題參數的設置,還包括各種類型的圖像的繪製,如柱狀圖、色圖、等高線圖等等。作為延伸,又介紹了3D曲線圖、框線圖和投影圖,以及動圖的製作。最後作為了解,介紹了後端以及圖片視頻格式的相關內容。

  • 作者:J.R. Johansson (郵箱:jrjohansson@gmail.com)
  • 最新版本的用法介紹見網站http://github.com/jrjohansson/scientific-python-lectures. 其他相關介紹見http://jrjohansson.github.io.

In [1]:n# 利用matplotlib生成的圖像嵌入notebook中,而不用每次生成圖像時打開一個新的窗口, n# 後面會再次提到它的用法。如果你在使用舊版本的Python,請運行 『%pylab inline』,如新版本,n則輸入 n%matplotlib inlinen

簡介

Matplotlib是一個繪製2D和3D科學圖像的庫,它包含了以下的優點:

  1. 容易學習和掌握
  2. 兼容LaTeX格式的標題和文檔
  3. 可以控制圖像中的每個元素,包括圖像大小和掃描精度。
  4. 對於很多格式都可以高質量的輸出圖像,包括PNG,PDF,SVG,EPS和PGF.
  5. 可以生成圖形用戶界面(GUI),做到互動式的獲取圖像以及無腦生成圖像文件(通常用於批量作業)

Matplotlib最重要的一個特點,也是它作出的圖像非常適合作為科學出版物的原因,是因為圖像可以完全被程序所控制。這一點對於圖像重現非常重要,同時為更新數據後重新作圖以及改變圖像形狀提供了方便。更多關於Matplotlib網頁請見matplotlib.org/

在Python中調用Matplotlib函數包有兩種方法,一種是在pylab模式中包含一個星號(簡單的方法)

In [2]: from pylab import *n

另一種是在matplotlib.pyplot模式下使用plt(整潔的方法):

In [3]: import matplotlibn import matplotlib.pyplot as pltnIn [4]: import numpy as npn

MATLAB樣式的API

學慣用matplotlib繪製圖像最簡單的方法使用matplotlib自身提供的類似MATLAB的API。它和MATLAB繪製圖像的函數非常相近,所以熟悉MATLAB的用戶可以非常容易的上手。採用在pylab模式中包含星號的方式可以使用matplotlib中的API:

In [5]: from pylab import *n

例:

採用一個類似MATLAB作圖的API,能夠做出以下簡單的圖像:

In [6]: x = np.linspace(0, 5, 10)n y = x ** 2nIn [7]: figure() plot(x, y, r) xlabel(x) ylabel(y) title(title) show()n

MATLAB中大多數繪圖相關的函數都能在pylab模式下實現。例如將多個圖像繪製在一個窗口中,以及選擇顏色和線條類型:

In [8]: subplot(1,2,1)n plot(x, y, r--)n subplot(1,2,2)n plot(y, x, g*-);n

pylab這種MATLAB格式的API有一個優點,對於MATLAB熟悉的用戶能夠非常容易上手,而且對於繪製簡單圖像而言不需要花費很多精力去學習。

然而,對於並不是特別簡單的圖像,並不推薦使用MATLAB類似的API,學習使用matplotlib面向對象的繪圖API是一種更好更強大的方法。對於多個複雜圖像繪製在一個窗口中,插入圖像和加入其它成分這樣的複雜操作,matplotlib的API能夠很好的解決。

matplotlib面向對象的API

面向對象的程序的主要思路是讓用戶能夠面向對象來使用函數和進行操作,而不是像MATLAB類似的API一樣採用全局的程序狀態。Matplotlib的優勢在繪製多個圖像或者一個圖像窗口中包含多個子圖像的時候能夠彰顯出來。

我們這次採用面向對象的API來繪製和前一個例子相似的圖像,但是這次我們存儲一個引用在新創建的fig變數的圖像中,而並不直接創建一個全局的圖像,然後我們創建一個新的坐標軸圖像axes(採用Figure函數族中的add_axes方法):

In [9]: fig = plt.figure()nn axes = fig.add_axes([0.1, 0.1, 0.8, 0.8])n # 左側間距,底部間距,寬度,高度 (從0到1)nn axes.plot(x, y, r)nn axes.set_xlabel(x)n axes.set_ylabel(y)n axes.set_title(title);n

儘管代碼看起來多了一點,但是我們現在能夠完全掌控圖像的坐標軸位置,並且能夠在圖像上輕易增加多個坐標軸:

In [10]: fig = plt.figure()nn axes1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # 主坐標軸n axes2 = fig.add_axes([0.2, 0.5, 0.4, 0.3]) # 插入的坐標軸nn # 主要圖像n axes1.plot(x, y, r)n axes1.set_xlabel(x)n axes1.set_ylabel(y)n axes1.set_title(title)nn # 插入的圖像n axes2.plot(y, x, g)n axes2.set_xlabel(y)n axes2.set_ylabel(x)n axes2.set_title(insert title);n

如果我們並不關心坐標軸的位置是否要明確處於畫圖窗口的哪個位置,我們可以採用matplotlib布局工具中的一個,例如subplots,用法如下:

In [11]: fig, axes = plt.subplots()nn axes.plot(x, y, r)n axes.set_xlabel(x)n axes.set_ylabel(y)n axes.set_title(title);n

In [12]: fig, axes = plt.subplots(nrows=1, ncols=2)nn for ax in axes:n ax.plot(x, y, r)n ax.set_xlabel(x)n ax.set_ylabel(y)n ax.set_title(title)n

這樣的代碼很簡單,但是如果坐標軸或者標籤重合在一起,就顯得不太美觀了。

我們可以採用fig.tight_layout方法來解決這個問題,它能夠更自動調整坐標軸在圖像窗口的位置,從而避免重合的發生:

In [13]: fig, axes = plt.subplots(nrows=1, ncols=2)nn for ax in axes:n ax.plot(x, y, r)n ax.set_xlabel(x)n ax.set_ylabel(y)n ax.set_title(title)n n fig.tight_layout()n

圖像大小,縱橫比和圖像精度

Matplotlib在繪製Figure對象時,允許用戶確定圖像縱橫比、圖像精度和大小,採用figsize和dpi關鍵字參數。figsize是關於圖像寬度和高度(單位:英寸)的元組型變數,dpi是每英寸點數(像素)。為創建一個800×400像素,每英寸點數為100的圖像,代碼如下:

In [14]: fig = plt.figure(figsize=(8,4), dpi=100)n

<matplotlib.figure.Figure at 0x7f7f385be950>

同樣的操作可以在布局工具中運行,例如subplots函數:

In [15]: fig, axes = plt.subplots(figsize=(12,3))nn axes.plot(x, y, r)n axes.set_xlabel(x)n axes.set_ylabel(y)n axes.set_title(title);n

保存圖像

我們可以採用Figure函數族中的savefig方法來存儲圖像:

In [16]: fig.savefig("filename.png")n

這裡我們還可以確定圖像精度,以及選擇不同的輸出格式:

In [17]: fig.savefig("filename.png", dpi=200)n

可以存儲什麼樣的格式?為獲取最高質量圖像,我們應該選擇哪種格式?

Matplotlib可以產生各種高質量的輸出格式,包括PNG,JPG,EPS,SVG,PGF和PDF。在學術論文中,如果可以的話推薦使用PDF格式(LaTeX文件編譯pdflatex可以採用includegraphics來編譯PDF)。在一些情況下,PGF格式也是一種好的選擇。

圖例、標籤和標題

既然我們已經介紹了繪圖和添加坐標軸的基本方法,我們現在來介紹如何添加圖例、標籤和標題。

  • 標題

標題可以加在每個圖像上,可以採用set_title方法來設置標題:

In [18]: ax.set_title("title");n

  • 坐標軸標籤

同樣的,用set_xlabel和set_ylabel可以設置X和Y軸的標籤:

In [19]: ax.set_xlabel("x")n ax.set_ylabel("y");n

  • 圖例

圖像中曲線的圖例可以用兩種方式添加,一種是用坐標軸對象的legend指令,對於之前定義的曲線添加列表或元組形式的文本:

In [20]: ax.legend(["curve1", "curve2", "curve3"]);n

上面這種方法其實是MATLAB的API,如果圖像上的曲線被添加或者刪除時可能會報錯(導致錯誤的添加圖例)。

一種更好的方法是在繪圖或添加其他元素的時候利用label="label text" 關鍵字參數,然後用無參數的legend指令把圖例添加到圖像上:

In [21]: ax.plot(x, x**2, label="curve1")n ax.plot(x, x**3, label="curve2")n ax.legend();n

這種方法的優點是,如果在圖像上添加或者刪除曲線,圖例會隨之自動更新。

legend函數有一個可供選擇的關鍵字參數loc,用來確定圖例添加的位置,loc參數的允許值是數值型代碼,詳見http://matplotlib.org/users/legend_guide.html#legend-location.下面列舉了一些常見的loc值:

In [22]: ax.legend(loc=0) # 由matplotlib確定最優位置n ax.legend(loc=1) # 右上角n ax.legend(loc=2) # 左上角n ax.legend(loc=3) # 左下角n ax.legend(loc=4) # 右下角n # .. 還有一些其他的選擇,不一一列舉nnOut[22]: <matplotlib.legend.Legend at 0x7f7f441f33d0>n

運用上面介紹的關於標題,坐標軸標籤和圖例的內容,我們可以作出如下圖像:

In [23]: fig, ax = plt.subplots()nn ax.plot(x, x**2, label="y = x**2")n ax.plot(x, x**3, label="y = x**3")n ax.legend(loc=2); # 左上角n ax.set_xlabel(x)n ax.set_ylabel(y)n ax.set_title(title);n

文本格式: LaTeX,字體大小,字體樣式

上面的繪製圖像的方法都非常實用,但是還不能滿足發表論文所需的標準。第一也是最重要的,我們需要採用LaTeX文本格式;第二,我們需要能夠調整字體大小以適應出版社所需的要求。

Matplotlib與LaTeX非常兼容,我們只需採用美元符號來封裝LaTeX的文本(圖例,題目,標籤等等),例如:"y=x3y=x3"。

但是這裡我們可能在轉換LaTeX代碼和Python字元串的過程中出現一點問題。在LaTeX中,我們經常用反斜杠符號,例如用alpha來產生符號α. 但是反斜杠在Python中已經有別的含義(轉義碼字元)。為了避免Python和LaTeX代碼混淆,我們採用「原始」字元串。原始字元串帶有前綴「r」,例如r"alpha" 或者 ralpha 而不是 "alpha" or alpha:

In [24]: fig, ax = plt.subplots()nn ax.plot(x, x**2, label=r"$y = alpha^2$")n ax.plot(x, x**3, label=r"$y = alpha^3$")n ax.legend(loc=2) # 左上角n ax.set_xlabel(r$alpha$, fontsize=18)n ax.set_ylabel(r$y$, fontsize=18)n ax.set_title(title);n

我們也可以改變全局的字體大小和字體樣式,使得圖像中的所有文本元素都適用(刻度標記、坐標軸標籤,標題和圖例等等):

In [25]: # 更新matplotlib的布局參數:n matplotlib.rcParams.update({font.size: 18, font.family: serif})nnIn [26]: fig, ax = plt.subplots()nn ax.plot(x, x**2, label=r"$y = alpha^2$")n ax.plot(x, x**3, label=r"$y = alpha^3$")n ax.legend(loc=2) # 左上角n ax.set_xlabel(r$alpha$)n ax.set_ylabel(r$y$)n ax.set_title(title);n

全局字體選擇STIX字體樣式是一個好的選擇:

In [27]: # 更新matplotlib的布局參數:n matplotlib.rcParams.update({font.size:18, font.family:STIXGeneral,n mathtext.fontset: stix})nnIn [28]: fig, ax = plt.subplots()nn ax.plot(x, x**2, label=r"$y = alpha^2$")n ax.plot(x, x**3, label=r"$y = alpha^3$")n ax.legend(loc=2) # 左上角n ax.set_xlabel(r$alpha$)n ax.set_ylabel(r$y$)n ax.set_title(title);n

或者,我們可以要求matplotlib在圖像中採用LaTeX文本元素:

In [29]: matplotlib.rcParams.update({font.size: 18, text.usetex: True})nnIn [32]: fig, ax = plt.subplots()nn ax.plot(x, x**2, label=r"$y = alpha^2$")n ax.plot(x, x**3, label=r"$y = alpha^3$")n ax.legend(loc=2) # 左上角n ax.set_xlabel(r$alpha$)n ax.set_ylabel(r$y$)n ax.set_title(title);n

In [31]: # 存儲n matplotlib.rcParams.update({font.size: 12, font.family: sans, n text.usetex: False})n

設置顏色,線條寬度和線條類型

  • 顏色

用matplotlib,我們可以運用各種方法定義線條顏色和其他圖像元素。首先,我們可以運用MATLAB的語法,定義b代表藍色,g代表綠色,等等。同樣,matplotlib也支持用MATLAB的API設置線條類型,例如:b.-代表藍色虛點線:

In [33]: # MATLAB樣式的線條顏色和類型n ax.plot(x, x**2, b.-) # 藍色虛點線n ax.plot(x, x**3, g--) # 綠色短劃線nnOut[33]: [<matplotlib.lines.Line2D at 0x7f7f2892a790>]n

我們也可以定義用顏色的英文名稱定義,或者RGB十六進位碼,或者用color和alpha關鍵字參數來選擇性提供α值:

In [34]: fig, ax = plt.subplots()nn ax.plot(x, x+1, color="red", alpha=0.5) # 半透明紅色n ax.plot(x, x+2, color="#1155dd") # 淺藍色的RGB十六進位碼n ax.plot(x, x+3, color="#15cc55") # 淺綠色的RGB十六進位碼nnOut[34]: [<matplotlib.lines.Line2D at 0x7f7f382a0050>]n

  • 線條和標記樣式

我們可以用linewidth或者lw關鍵字參數來調整線寬度,線條樣式則可以在linestyle或者ls關鍵字參數中選擇:

In [35]: fig, ax = plt.subplots(figsize=(12,6))nn ax.plot(x, x+1, color="blue", linewidth_=0.25)n ax.plot(x, x+2, color="blue", linewidth_=0.50)n ax.plot(x, x+3, color="blue", linewidth_=1.00)n ax.plot(x, x+4, color="blue", linewidth_=2.00)nn # 線條樣式選擇n ax.plot(x, x+5, color="red", lw=2, linestylex=-)n ax.plot(x, x+6, color="red", lw=2, ls=-.)n ax.plot(x, x+7, color="red", lw=2, ls=:)nn # 自定義設置n line, = ax.plot(x, x+8, color="black", lw=1.50)n line.set_dashes([5, 10, 15, 10]) # 格式:線長, 間距, ...nn # 標記符號n ax.plot(x, x+ 9, color="green", lw=2, ls=--, marker=+)n ax.plot(x, x+10, color="green", lw=2, ls=--, marker=o)n ax.plot(x, x+11, color="green", lw=2, ls=--, marker=s)n ax.plot(x, x+12, color="green", lw=2, ls=--, marker=1)nn # 標記大小和顏色n ax.plot(x, x+13, color="purple", lw=1, ls=-, marker=o, markersize=2)n ax.plot(x, x+14, color="purple", lw=1, ls=-, marker=o, markersize=4)n ax.plot(x, x+15, color="purple", lw=1, ls=-, marker=o, markersize=8,n markerfacecolor="red")n n ax.plot(x, x+16, color="purple", lw=1, ls=-, marker=s, markersize=8,n markerfacecolor="yellow", markeredgewidth_=2, markeredgecolor="blue");n

控制坐標軸外觀

坐標軸外觀是一個圖像重要的方面,特別是我們經常需要更改它以滿足出版刊物對於圖像的要求。我們需要控制刻度和標籤的位置,更改字體大小和坐標軸標籤。這一節中,我們將會學習如何控制matplotlib圖像的這些參數。

  • 繪圖範圍

首先我們想要設置坐標軸的範圍,可以運用坐標軸對象中的set_ylim和set_xlim,或者axis(tight)來自動設置「緊密結合」的坐標範圍:

In [36]: fig, axes = plt.subplots(1, 3, figsize=(12, 4))nn axes[0].plot(x, x**2, x, x**3)n axes[0].set_title("default axes ranges")nn axes[1].plot(x, x**2, x, x**3)n axes[1].axis(tight)n axes[1].set_title("tight axes")nn axes[2].plot(x, x**2, x, x**3)n axes[2].set_ylim([0, 60])n axes[2].set_xlim([2, 5])n axes[2].set_title("custom axes range");n

  • 對數標度

對於單個或者兩個坐標軸都可以設置對數標度。這個功能其實僅僅是Matplotlib全部變換系統的一個應用。每個坐標標度可以分別用set_xscale和set_yscale來設置(值填入「log」即可):

In [37]: fig, axes = plt.subplots(1, 2, figsize=(10,4))n n axes[0].plot(x, x**2, x, np.exp(x))n axes[0].set_title("Normal scale")nn axes[1].plot(x, x**2, x, np.exp(x))n axes[1].set_yscale("log")n axes[1].set_title("Logarithmic scale (y)");n

刻度的放置以及用戶定義的刻度標籤

我們可以用set_xticks和set_yticks來明確確定坐標軸的刻度位置,二者都需要提供一個列表型數值。對於每個刻度位置,我們可以用set_xticklabels和set_yticklabels來提供一個用戶定義的文本標籤:

In [38]: fig, ax = plt.subplots(figsize=(10, 4))nn ax.plot(x, x**2, x, x**3, lw=2)nn ax.set_xticks([1, 2, 3, 4, 5])n ax.set_xticklabels([r$alpha$, r$beta$, r$gamma$, r$delta$,n r$epsilon$], fontsize=18)nn yticks = [0, 50, 100, 150]n ax.set_yticks(yticks)n ax.set_yticklabels(["$%.1f$" % y for y in yticks], fontsize=18); n # 採用LaTeX格式標籤n

Matplotlib圖像還有很多更為高級的方法來控制主刻度和副刻度的位置,比如在不同環境下自動確定其位置,詳見http://matplotlib.org/api/ticker_api.html.

  • 科學計數法

對於坐標軸上出現的較大的數字,通常運用科學計數法:

In [39]: fig, ax = plt.subplots(1, 1)n n ax.plot(x, x**2, x, np.exp(x))n ax.set_title("scientific notation")nn ax.set_yticks([0, 50, 100, 150])nn from matplotlib import tickern formatter = ticker.ScalarFormatter(useMathText=True)n formatter.set_scientific(True) n formatter.set_powerlimits((-1,1)) n ax.yaxis.set_major_formatter(formatter)n

坐標數字以及坐標標籤的位置

In [40]: # x和y軸的距離和坐標軸上的數字n matplotlib.rcParams[xtick.major.pad] = 5n matplotlib.rcParams[ytick.major.pad] = 5nn fig, ax = plt.subplots(1, 1)n n ax.plot(x, x**2, x, np.exp(x))n ax.set_yticks([0, 50, 100, 150])nn ax.set_title("label and axis spacing")nn # 坐標軸標籤和坐標軸數字的距離n ax.xaxis.labelpad = 5n ax.yaxis.labelpad = 5nn ax.set_xlabel("x")n ax.set_ylabel("y");n

In [41]: # 存儲默認值n matplotlib.rcParams[xtick.major.pad] = 3n matplotlib.rcParams[ytick.major.pad] = 3n

  • 坐標軸位置調整

不幸的是,當保存圖像時候,標籤有時會被縮短,因此需要微調坐標軸的位置,這可以由subplots_adjust來實現:

In [42]: fig, ax = plt.subplots(1, 1)n n ax.plot(x, x**2, x, np.exp(x))n ax.set_yticks([0, 50, 100, 150])nn ax.set_title("title")n ax.set_xlabel("x")n ax.set_ylabel("y")nn fig.subplots_adjust(left=0.15, right=.9, bottom=0.1, top=0.9);n

  • 坐標軸網格

用坐標軸對象中的grid可以使用和取消網格線。我們也可以用plot函數中同樣的關鍵字參數來定製網格樣式:

In [44]: fig, axes = plt.subplots(1, 2, figsize=(10,3))nn # 默認網格外觀n axes[0].plot(x, x**2, x, x**3, lw=2)n axes[0].grid(True)nn # 用戶定義的網格外觀n axes[1].plot(x, x**2, x, x**3, lw=2)n axes[1].grid(color=b, alpha=0.5, linestylex=dashed, linewidth_=0.5)n

軸刻度標記線

我們也可以改變軸刻度標記線的參數:

In [45]: fig, ax = plt.subplots(figsize=(6,2))nn ax.spines[bottom].set_color(blue)n ax.spines[top].set_color(blue)nn ax.spines[left].set_color(red)n ax.spines[left].set_linewidth(2)nn # 取消右側的坐標軸刻度n ax.spines[right].set_color("none")n ax.yaxis.tick_left() # 只在左側有刻度n

雙刻度

有時在圖像中採用兩個x或y軸是十分有用的,例如單位不同的多條曲線畫在一個圖中。Matplotlib提供了twinx和twiny函數:

In [46]: fig, ax1 = plt.subplots()nn ax1.plot(x, x**2, lw=2, color="blue")n ax1.set_ylabel(r"area $(m^2)$", fontsize=18, color="blue")n for label in ax1.get_yticklabels():n label.set_color("blue")n n ax2 = ax1.twinx()n ax2.plot(x, x**3, lw=2, color="red")n ax2.set_ylabel(r"volume $(m^3)$", fontsize=18, color="red")n for label in ax2.get_yticklabels():n label.set_color("red")n

x和y值為0的坐標軸

In [47]: fig, ax = plt.subplots()nn ax.spines[right].set_color(none)n ax.spines[top].set_color(none)nn ax.xaxis.set_ticks_position(bottom)n ax.spines[bottom].set_position((data,0)) # 設置x坐標軸刻度位置於x=0nn ax.yaxis.set_ticks_position(left)n ax.spines[left].set_position((data,0)) # 設置y坐標軸刻度位置於y=0nn xx = np.linspace(-0.75, 1., 100)n ax.plot(xx, xx**3);n

其他二維繪圖樣式

除了常規的plot方法,還有一些其他的函數能夠實現不同樣式的繪圖,所有可以繪製的圖像種類請見http://matplotlib.org/gallery.html. 下面展示一些有用的樣式:

In [49]: fig, axes = plt.subplots(1, 4, figsize=(12,3))nn axes[0].scatter(xx, xx + 0.25*np.random.randn(len(xx)))n axes[0].set_title("scatter")nn axes[1].step(n, n**2, lw=2)n axes[1].set_title("step")nn axes[2].bar(n, n**2, align="center", width_=0.5, alpha=0.5)n axes[2].set_title("bar")nn axes[3].fill_between(x, x**2, x**3, color="green", alpha=0.5);n axes[3].set_title("fill_between");nn /opt/conda/envs/python2/lib/python2.7/site-packages/matplotlib/n collections.py:590: FutureWarning: elementwise comparison failed; n returning scalar instead, but in the future will perform elementwise n comparisonn if self._edgecolors == str(face):n

In [50]: # 用add_axes的極坐標圖和球極投影n fig = plt.figure()n ax = fig.add_axes([0.0, 0.0, .6, .6], polar=True)n t = np.linspace(0, 2 * np.pi, 100)n ax.plot(t, t, color=blue, lw=3);n

In [51]: # 柱狀圖n n = np.random.randn(100000)n fig, axes = plt.subplots(1, 2, figsize=(12,4))nn axes[0].hist(n)n axes[0].set_title("Default histogram")n axes[0].set_xlim((min(n), max(n)))nn axes[1].hist(n, cumulative=True, bins=50)n axes[1].set_title("Cumulative detailed histogram")n axes[1].set_xlim((min(n), max(n)));n

文字注釋

採用text函數可以完成matplotlib圖像的文字注釋功能。和文字以及標題一樣,它也支持LaTeX格式:

In [52]: fig, ax = plt.subplots()nn ax.plot(xx, xx**2, xx, xx**3)nn ax.text(0.15, 0.2, r"$y=x^2$", fontsize=20, color="blue")n ax.text(0.65, 0.1, r"$y=x^3$", fontsize=20, color="green");n

多個子圖像的繪製和插入

採用fig.add_axes可以手動將坐標軸加入matplotlib圖像中,或者用子圖繪製的布局管理器,如subplots,subplot2grid或者gridspec:

  • subplots

In [53]: fig, ax = plt.subplots(2, 3)n fig.tight_layout()n

  • subplot2grid

In [54]: fig = plt.figure()n ax1 = plt.subplot2grid((3,3), (0,0), colspan=3)n ax2 = plt.subplot2grid((3,3), (1,0), colspan=2)n ax3 = plt.subplot2grid((3,3), (1,2), rowspan=2)n ax4 = plt.subplot2grid((3,3), (2,0))n ax5 = plt.subplot2grid((3,3), (2,1))n fig.tight_layout()n

  • gridspec

In [55]: import matplotlib.gridspec as gridspecnnIn [56]: fig = plt.figure()nn gs = gridspec.GridSpec(2, 3, height_ratios=[2,1], width_ratios=[1,2,1])n for g in gs:n ax = fig.add_subplot(g)n n fig.tight_layout()n

  • add_axes

用add_axes手動添加坐標軸對於添加元素於圖像中非常有用:

In [57]: fig, ax = plt.subplots()nn ax.plot(xx, xx**2, xx, xx**3)n fig.tight_layout()nn # 插入n inset_ax = fig.add_axes([0.2, 0.55, 0.35, 0.35]) # X, Y, 寬度,高度nn inset_ax.plot(xx, xx**2, xx, xx**3)n inset_ax.set_title(zoom near origin)nn # 設置坐標軸範圍n inset_ax.set_xlim(-.2, .2)n inset_ax.set_ylim(-.005, .01)nn # 設置坐標軸刻度位置n inset_ax.set_yticks([0, 0.005, 0.01])n inset_ax.set_xticks([-0.1,0,.1]);n

色圖和等高線圖

色圖和等高線圖對於兩個變數的繪圖函數非常有用。在大多數函數中,我們採用色圖編碼一個維度的數據。下面列出了一些之前定義好的色圖,他們對於確定定製版的色圖是一種直接的方法,詳見:http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps.

In [58]: alpha = 0.7n phi_ext = 2 * np.pi * 0.5nn def flux_qubit_potential(phi_m, phi_p):n return 2 + alpha - 2 * np.cos(phi_p) * np.cos(phi_m) - alpha * np.cons(phi_ext - 2*phi_p)nnIn [59]: phi_m = np.linspace(0, 2*np.pi, 100)n phi_p = np.linspace(0, 2*np.pi, 100)n X,Y = np.meshgrid(phi_p, phi_m)n Z = flux_qubit_potential(X, Y).Tn

  • pcolor函數

In [60]: fig, ax = plt.subplots()nn p = ax.pcolor(X/(2*np.pi), Y/(2*np.pi), Z, cmap=matplotlib.cm.RdBu, vminn =abs(Z).min(), vmax=abs(Z).max())n cb = fig.colorbar(p, ax=ax)n

  • imshow函數

In [61]: fig, ax = plt.subplots()nn im = ax.imshow(Z, cmap=matplotlib.cm.RdBu, vmin=abs(Z).min(),vmax=abs(Z)n .max(), extent=[0, 1, 0, 1])n im.set_interpolation(bilinear)nn cb = fig.colorbar(im, ax=ax)n

  • contour函數

In [62]: fig, ax = plt.subplots()nn cnt = ax.contour(Z, cmap=matplotlib.cm.RdBu, vmin=abs(Z).min(), vmax=absn (Z).max(), extent=[0, 1, 0, 1])n /opt/conda/envs/python2/lib/python2.7/site-packages/matplotlib/collection ns.py:650: FutureWarning: elementwise comparison failed;returning scalarn instead, but in the future will perform elementwise comparisonn if self._edgecolors_original != str(face):n

3D 圖像

在使用matplotlib中的3D圖像之前,我們首先需要創建Axes3D類。3D坐標軸和2D坐標軸創建的方法一樣;或者更方便的方法是,在add_axes或者add_subplot中採用projection=3d關鍵字參數。

In [63]: from mpl_toolkits.mplot3d.axes3d import Axes3Dn

  • 曲面圖

In [64]: fig = plt.figure(figsize=(14,6))nn # `ax` 是一個3D坐標軸,由於添加了projection=3d關鍵字參數於add_subplotn ax = fig.add_subplot(1, 2, 1, projection=3d)nn p = ax.plot_surface(X, Y, Z, rstride=4, cstride=4, linewidth_=0)nn # 帶有顏色梯度和顏色條的曲面圖n ax = fig.add_subplot(1, 2, 2, projection=3d)n p = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=matplotlib.cm.con olwarm, linewidth_=0, antialiased=False)nn cb = fig.colorbar(p, shrink=0.5)n

  • 三維線框圖

In [65]: fig = plt.figure(figsize=(8,6))nn ax = fig.add_subplot(1, 1, 1, projection=3d)nn p = ax.plot_wireframe(X, Y, Z, rstride=4, cstride=4)n

  • 帶投影的等高線圖

In [66]: fig = plt.figure(figsize=(8,6))nn ax = fig.add_subplot(1,1,1, projection=3d)nn ax.plot_surface(X, Y, Z, rstride=4, cstride=4, alpha=0.25)n cset = ax.contour(X, Y, Z, zdir=z, offset=-np.pi, cmap=matplotlib.cm.cn oolwarm)n cset = ax.contour(X, Y, Z, zdir=x, offset=-np.pi, cmap=matplotlib.cm.cn oolwarm)n cset = ax.contour(X, Y, Z, zdir=y, offset=3*np.pi, cmap=matplotlib.cm.n coolwarm)nn ax.set_xlim3d(-np.pi, 2*np.pi);n ax.set_ylim3d(0, 3*np.pi);n ax.set_zlim3d(-np.pi, 2*np.pi);n

  • 改變視角

採用view_init可以改變3D圖像的視角,該命令有兩個參數,elevation和azimuth角度(度數):

In [67]: fig = plt.figure(figsize=(12,6))nn ax = fig.add_subplot(1,2,1, projection=3d)n ax.plot_surface(X, Y, Z, rstride=4, cstride=4, alpha=0.25)n ax.view_init(30, 45)nn ax = fig.add_subplot(1,2,2, projection=3d)n ax.plot_surface(X, Y, Z, rstride=4, cstride=4, alpha=0.25)n ax.view_init(70, 30)nn fig.tight_layout()n

  • 動圖

Matplotlib也包含了一個簡單的API用來產生一系列圖像的動圖。採用FuncAnimation函數,我們可以產生由一系列圖像組成的視頻文件。該函數採用了如下命令:fig圖像窗口,func更新圖像所用的函數,init_func組織圖像的函數,frame產生的幀數,和blit指導動圖函數只在幀數變動的時候更新(對於光滑動圖而言):

def init():n # setup figurenndef update(frame_counter):n # update figure for new framennanim = animation.FuncAnimation(fig, update, init_func=init, frames=200,blit=True)nnanim.save(animation.mp4, fps=30) # fps = frames per secondn

為了使用matplotlib中的動圖函數,我們首先調用matplotlib.animation:

In [68]: from matplotlib import animationnnIn [69]: # 解決復擺的ODE(常微分方程)問題nn from scipy.integrate import odeintn from numpy import cos, sinnn g = 9.82; L = 0.5; m = 0.1nn def dx(x, t):n x1, x2, x3, x4 = x[0], x[1], x[2], x[3]n n dx1 = 6.0/(m*L**2) * (2 * x3 - 3 * cos(x1-x2) * x4)/(16 - 9 * cos(x1n -x2)**2)n dx2 = 6.0/(m*L**2) * (8 * x4 - 3 * cos(x1-x2) * x3)/(16 - 9 * cos(x1n -x2)**2)n dx3 = -0.5 * m * L**2 * ( dx1 * dx2 * sin(x1-x2) + 3 * (g/L) * sin(xn 1))n dx4 = -0.5 * m * L**2 * (-dx1 * dx2 * sin(x1-x2) + (g/L) * sin(x2))n return [dx1, dx2, dx3, dx4]nn x0 = [np.pi/2, np.pi/2, 0, 0] # 初始狀態n t = np.linspace(0, 10, 250) # 時間坐標n x = odeint(dx, x0, t) # 求常微分方程的解n

生成的動畫顯示了鐘擺的位置作為時間的函數:

fig, ax = plt.subplots(figsize=(5,5))nax.set_ylim([-1.5, 0.5])ax.set_xlim([1, -1])npendulum1, = ax.plot([], [], color="red", lw=2)pendulum2, = ax.plot([], [], colorn="blue", lw=2)nndef init():n pendulum1.set_data([], [])n pendulum2.set_data([], [])ndef update(n): n # n = 幀計數器n # 計算復擺的位置n x1 = + L * sin(x[n, 0])n y1 = - L * cos(x[n, 0])n x2 = x1 + L * sin(x[n, 1])n y2 = y1 - L * cos(x[n, 1])n n # 更新數據n pendulum1.set_data([0 ,x1], [0 ,y1])n pendulum2.set_data([x1,x2], [y1,y2])nanim = animation.FuncAnimation(fig, update, init_func=init, frames=len(t), blit=Tnrue)n# anim.save可以採用不同的方法運行,在不同平台、# 不同版本的matplotlib和視頻編碼器上可能有n的方法不能使用#anim.save(animation.mp4, fps=20, extra_args=[-vcodec, libx264]n, writer=animation.FFMpegWriter())#anim.save(animation.mp4, fps=20, extra_args=n[-vcodec, libx264])#anim.save(animation.mp4, fps=20, writer="ffmpeg", codecn="libx264")anim.save(animation.mp4, fps=20, writer="avconv", codec="libx264")nplt.close(fig)n/opt/conda/envs/python2/lib/python2.7/site-packages/matplotlib/animation.py:742: nUserWarning: MovieWriter avconv unavailablen warnings.warn("MovieWriter %s unavailable" % writer)n

Note: 為了產生視頻文件,我們需要安裝ffmpeg或者avconv. 在Ubuntu上安裝的指令:

$ sudo apt-get install ffmpegn

或者(更新的版本)

$ sudo apt-get install libav-toolsn

在MacOSX中, 嘗試:

$ sudo port install ffmpegn

有興趣的用戶可以自行安裝,這裡不再演示視頻文件。

from IPython.display import HTMLnvideo = open("animation.mp4", "rb").read()nvideo_encoded = video.encode("base64")nvideo_tag = <video controls alt="test" src_="data:video/x-m4v;base64,{0}">.formant(video_encoded)nHTML(video_tag)n---------------------------------------------------------------------------n

後端

Matplotlib有許多「後端」對產生的圖像負責,不同的後端能夠更產生不同樣式的圖和視頻。非互動式的後端(如 agg,svg,pdf等)是用來產生圖像文件(如savefig函數),與此不同,互動式的後端(如Qt4Agg,GTK,MaxOSX)能夠運行GUI窗口,供用戶進行互動式的使用圖像。

可供選擇的後端有:

print(matplotlib.rcsetup.all_backends)nn[uGTK, uGTKAgg, uGTKCairo, uMacOSX, uQt4Agg, uQt5Agg, uTkAgg, uWXn,uWXAgg, uCocoaAgg, uGTK3Cairo, uGTK3Agg, uWebAgg, unbAgg, uagg, uncairo, uemf, ugdk, updf, upgf, ups, usvg, utemplate]n

默認後端是agg,它基於柵格圖形庫,非常適合生成像PNG這樣的光柵格式。

通常來說,我們並不需要改變默認後端,但是有時轉換到例如PDF或者GTKcariro(如果是Linux系統)時會非常有用,能夠更產生高質量矢量圖形而不是柵格圖。

#n# 重啟Notebook: matplotlib後端只能在pylab中選擇n# (e.g. Kernel > Restart)n# nimport matplotlibnmatplotlib.use(svg)nimport matplotlib.pylab as pltnimport numpynfrom IPython.display import Image, SVGn/opt/conda/envs/python2/lib/python2.7/site-packages/matplotlib/__init__.py:1318: nUserWarning: This call to matplotlib.use() has no effectnbecause the backend has already been chosen;nmatplotlib.use() must be called *before* pylab, matplotlib.pyplot,nor matplotlib.backends is imported for the first time.nn warnings.warn(_use_error_msg)nn#n# 現在我們用svg後端產生SVG矢量圖n#nfig, ax = plt.subplots()nt = numpy.linspace(0, 10, 100)nax.plot(t, numpy.cos(t)*numpy.sin(t))nplt.savefig("test.svg")n

#n# 顯示產生的SVG文件. n#nSVG(filename="test.svg")n

IPython notebook 內聯後端

當我們使用IPython notebook時,可以很方便的用matplotlib後端輸出嵌入在notebook的圖形文件。要激活這個後端,需要在開始的某處添加:

%matplotlib inlinen

採用如下格式也能夠激活內聯後端:

%pylab inlinen

不同之處在於%pylab inline調用了一系列函數包到全局地址空間(scipy,numpy),然而%matplotlib inline只在內聯繪圖時才調用。在IPython 1.0+的新的notebook中,建議使用%matplotlib inline,因為它更整潔,對於函數包的調用控制更多。通常,scipy和numpy分別通過如下形式調用:

import numpy as npnimport scipy as spnimport matplotlib.pyplot as pltn

內聯後端有一系列的設置選擇,可以通過IPython的命令%config來更新InlandBackend中的設置。例如,我們可以轉換SVG圖像或者更高分別率的圖像通過:

%config InlineBackend.figure_format=svgn

或者

%config InlineBackend.figure_format=retinan

如需了解更多內容,請輸入:

%config InlineBackendn

%matplotlib inlinen%config InlineBackend.figure_format=svgnnimport matplotlib.pylab as pltnimport numpynn#n# 現在我們替換notebook中的內聯後端,使用SVG矢量圖。n#nfig, ax = plt.subplots()nt = numpy.linspace(0, 10, 100)nax.plot(t, numpy.cos(t)*numpy.sin(t))nplt.savefig("test.svg")n

  • 互動式後端(這使得Python腳本文件更有意義)

#n# 重啟Notebook: matplotlib後端只能在pylab中選擇n# (e.g. Kernel > Restart)n# nimport matplotlibnmatplotlib.use(Qt4Agg) # or for example MacOSXnimport matplotlib.pylab as pltnimport numpy as npn/opt/conda/envs/python2/lib/python2.7/site-packages/matplotlib/__init__.py:1318: nUserWarning: This call to matplotlib.use() has no effectnbecause the backend has already been chosen;nmatplotlib.use() must be called *before* pylab, matplotlib.pyplot,nor matplotlib.backends is imported for the first time.nn warnings.warn(_use_error_msg)nn# 現在採用Qt4Agg打開一個互動式的繪圖窗口nfig, ax = plt.subplots()nt = np.linspace(0, 10, 100)nax.plot(t, np.cos(t) * np.sin(t))nplt.show()n

注意,當我們採用互動式後端是,需要調用plt.show()命令將圖像顯示在屏幕上。

推薦閱讀

http://www.matplotlib.org- 官方網頁

github.com/matplotlib/m - matplotlib源代碼

matplotlib.org/gallery. - 展示各種各樣matplotlib函數包能夠繪製的圖像,強烈推薦!

loria.fr/~rougier/teach- matplotlib課程

scipy-lectures.github.io- 其他參考文獻

到JoinQuant查看原文並參與討論:【翻譯搬運】Matplotlib - 用Python繪製2D和3D圖像


推薦閱讀:

『事件驅動』指數成份股調整帶來的投資機會
『毀研報系列』廣發凸組合優化的雙均線策略
『資產配置』風險平價組合(risk parity)理論與實踐
國內有哪些大學有量化金融相關專業?
PonderRNN對HS300指數預測

TAG:量化 | Python | Matplotlib |