【Python環境】matplotlib - 2D 與 3D 圖的繪製
類MATLAB API
最簡單的入門是從類 MATLAB API 開始,它被設計成相容 MATLAB 繪圖函式。
讓我們載入它:
from pylab import *
使用 qt 作為圖形後端:
%matplotlib qt
示例
類MATLAB API 繪圖的簡單例子:
from numpy import *
x = linspace(0, 5, 10)
y = x ** 2figure()
plot(x, y, 'r')
xlabel('x')
ylabel('y')
title('title')
show()
建立子圖,選擇繪圖用的顏色與描點符號:
subplot(1,2,1) plot(x, y, 'r--') subplot(1,2,2) plot(y, x, 'g*-');
此類 API 的好處是可以節省你的程式碼量,但是我們並不鼓勵使用它處理複雜的圖表。處理複雜圖表時, matplotlib 面向物件 API 是一個更好的選擇。
matplotlib 面向物件 API
首先讓我們載入它:
import matplotlib.pyplot as plt
使用面向物件API的方法和之前例子裡的看起來很類似,不同的是,我們並不建立一個全域性例項,而是將新建例項的引用儲存在 fig
變數中,如果我們想在圖中新建一個座標軸例項,只需要 呼叫 fig
例項的 add_axes
方法:
fig = plt.figure() axes = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # left, bottom, width, height (range 0 to 1)axes.plot(x, y, 'r') axes.set_xlabel('x') axes.set_ylabel('y') axes.set_title('title') fig
儘管會寫更多的程式碼,好處在於我們對於圖表的繪製有了完全的控制權,可以很容易地多加一個座標軸到圖中:
fig = plt.figure() axes1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # main axesaxes2 = fig.add_axes([0.2, 0.5, 0.4, 0.3]) # inset axes# main figureaxes1.plot(x, y, 'r') axes1.set_xlabel('x') axes1.set_ylabel('y') axes1.set_title('title')# insertaxes2.plot(y, x, 'g') axes2.set_xlabel('y') axes2.set_ylabel('x') axes2.set_title('insert title');fig
如果我們不在意座標軸在圖中的排放位置️,那麼就可以使用matplotlib的佈局管理器了,我最喜歡的是subplots,使用方式如下:
fig, axes = plt.subplots()
axes.plot(x, y, 'r')
axes.set_xlabel('x')
axes.set_ylabel('y')
axes.set_title('title');
fig
fig, axes = plt.subplots(nrows=1, ncols=2)for ax in axes:
ax.plot(x, y, 'r')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('title')
fig
很簡單吧,但是標籤重疊就不好看了。可以使用 fig.tight_layout
解決這個問題,它會自動調整標籤的位置:
fig, axes = plt.subplots(nrows=1, ncols=2)for ax in axes:
ax.plot(x, y, 'r')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('title')
fig.tight_layout()
fig
圖表尺寸,長寬比 與 DPI
在建立 Figure
物件的時候,使用figsize
與 dpi
引數能夠設定圖表尺寸與DPI, 建立一個800*400畫素,每英寸100畫素的圖就可以這麼做:
fig = plt.figure(figsize=(8,4), dpi=100)<matplotlib.figure.Figure at 0x4cbd390>
同樣的引數也可以用在佈局管理器上:
fig, axes = plt.subplots(figsize=(12,3))
axes.plot(x, y, 'r')
axes.set_xlabel('x')
axes.set_ylabel('y')
axes.set_title('title');
儲存圖表
可以使用 savefig
儲存圖表
fig.savefig("filename.png")
這裡我們也可以有選擇地指定DPI,並且選擇不同的輸出格式:
fig.savefig("filename.png", dpi=200)
有哪些格式?哪種格式能獲得最佳質量?
Matplotlib 可以生成多種格式的高質量影象,包括PNG,JPG,EPS,SVG,PGF 和 PDF。如果是科學論文的話,我建議儘量使用pdf格式。 (pdflatex
編譯的 LaTeX 文件使用 includegraphics
命令就能包含 PDF 檔案)。 一些情況下,PGF也是一個很好的選擇。
圖例,軸標 與 標題
現在我們已經介紹瞭如何建立圖表畫布以及如何新增新的座標軸例項,讓我們看一看如何加上標題,軸標和圖例
標題
每一個座標軸例項都可以加上一個標題,只需呼叫座標軸例項的 set_title
方法:
ax.set_title("title");
軸標
類似的, set_xlabel
與 set_ylabel
可以設定座標軸的x軸與y軸的標籤。
ax.set_xlabel("x")ax.set_ylabel("y");
圖例
有兩種方法在圖中加入圖例。一種是呼叫座標軸物件的 legend
方法,傳入與之前定義的幾條曲線相對應地圖例文字的 列表/元組:
ax.legend(["curve1", "curve2", "curve3"]);
不過這種方式容易出錯,比如增加了新的曲線或者移除了某條曲線。更好的方式是在呼叫 plot
方法時使用 label="label text"
引數,再呼叫 legend
方法加入圖例:
ax.plot(x, x**2, label="curve1")ax.plot(x, x**3, label="curve2")ax.legend();
legend
還有一個可選引數 loc
決定畫出圖例的位置,詳情見:http://matplotlib.org/users/legend_guide.html#legend-location
最常用的值如下:
ax.legend(loc=0) # let matplotlib decide the optimal locationax.legend(loc=1) # upper right cornerax.legend(loc=2) # upper left cornerax.legend(loc=3) # lower left cornerax.legend(loc=4) # lower right corner# .. many more options are available=> <matplotlib.legend.Legend at 0x4c863d0>
下面這個例子同時包含了標題,軸標,與圖例的用法:
fig, ax = plt.subplots()
ax.plot(x, x**2, label="y = x**2")
ax.plot(x, x**3, label="y = x**3")
ax.legend(loc=2); # upper left cornerax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('title');
fig
格式化文字,LaTeX,字型大小,字型型別
Matplotlib 對 LaTeX 提供了很好的支援。我們只需要將 LaTeX 表示式封裝在$
符號內,就可以在圖的任何文字中顯示了,比如 "$y=x^3$"
。
不過這裡我們會遇到一些小問題,在 LaTeX 中我們常常會用到反斜槓,比如alpha
來產生符號 $alpha$ 。但反斜槓在 python 字串中是有特殊含義的。為了不出錯,我們需要使用原始文字,只需要在字串的前面加個r就行了,像是 r"alpha"
或者 r'alpha'
:
fig, ax = plt.subplots()
ax.plot(x, x**2, label=r"$y = alpha^2$")
ax.plot(x, x**3, label=r"$y = alpha^3$")
ax.legend(loc=2) # upper left cornerax.set_xlabel(r'$alpha$', fontsize=18)
ax.set_ylabel(r'$y$', fontsize=18)
ax.set_title('title');
fig
我們可以更改全域性字型大小或者型別:
# Update the matplotlib configuration parameters:from matplotlib import rcParams
rcParams.update({'font.size': 18, 'font.family': 'serif'})
fig, ax = plt.subplots()
ax.plot(x, x**2, label=r"$y = alpha^2$")
ax.plot(x, x**3, label=r"$y = alpha^3$")
ax.legend(loc=2) # upper left cornerax.set_xlabel(r'$alpha$')
ax.set_ylabel(r'$y$')
ax.set_title('title');
fig
STIX 字型是一種好選擇:
# Update the matplotlib configuration parameters:matplotlib.rcParams.update({'font.size': 18, 'font.family': 'STIXGeneral', 'mathtext.fontset': 'stix'})
fig, ax = plt.subplots()
ax.plot(x, x**2, label=r"$y = alpha^2$")
ax.plot(x, x**3, label=r"$y = alpha^3$")
ax.legend(loc=2) # upper left cornerax.set_xlabel(r'$alpha$')
ax.set_ylabel(r'$y$')
ax.set_title('title');
fig
我們也可以將圖中的文字全用 Latex 渲染:
matplotlib.rcParams.update({'font.size': 18, 'text.usetex': True})fig, ax = plt.subplots()
ax.plot(x, x**2, label=r"$y = alpha^2$")
ax.plot(x, x**3, label=r"$y = alpha^3$")
ax.legend(loc=2) # upper left cornerax.set_xlabel(r'$alpha$')
ax.set_ylabel(r'$y$')
ax.set_title('title');
fig
# 重置matplotlib.rcParams.update({'font.size': 12, 'font.family': 'sans', 'text.usetex': False})
設定顏色,線寬 與 線型
顏色
有了matplotlib,我們就有很多方法能夠定義線的顏色和很多其他圖形元素。首先,我們可以使用類MATLAB語法,'b'
代表藍色,'g'
代表綠色,依此類推。matplotlib同時也支援 MATLAB API 選擇線型所使用的方式:比如 'b.-' 意味著藍線標著點:
# MATLAB style line color and style ax.plot(x, x**2, 'b.-') # blue line with dotsax.plot(x, x**3, 'g--') # green dashed linefig
=> [<matplotlib.lines.Line2D at 0x4985810>]
我們也可以以顏色的名字或者RGB值選擇顏色,alpha引數決定了顏色的透明度:
fig, ax = plt.subplots()
ax.plot(x, x+1, color="red", alpha=0.5) # half-transparant redax.plot(x, x+2, color="#1155dd") # RGB hex code for a bluish colorax.plot(x, x+3, color="#15cc55") # RGB hex code for a greenish colorfig
=> [<matplotlib.lines.Line2D at 0x4edbd10>]
線與描點風格
linewidth
或是 lw
引數改變線寬。 linestyle
或是 ls
引數改變線的風格。
fig, ax = plt.subplots(figsize=(12,6))
ax.plot(x, x+1, color="blue", linewidth=0.25)
ax.plot(x, x+2, color="blue", linewidth=0.50)
ax.plot(x, x+3, color="blue", linewidth=1.00)
ax.plot(x, x+4, color="blue", linewidth=2.00)
# possible linestype options ‘-‘, ‘–’, ‘-.’, ‘:’, ‘steps’
ax.plot(x, x+5, color="red", lw=2, linestyle='-')
ax.plot(x, x+6, color="red", lw=2, ls='-.')
ax.plot(x, x+7, color="red", lw=2, ls=':')
# custom dash
line, = ax.plot(x, x+8, color="black", lw=1.50)
line.set_dashes([5, 10, 15, 10]) # format: line length, space length, ...
# possible marker symbols: marker = '+', 'o', '*', 's', ',', '.', '1', '2', '3', '4', ...
ax.plot(x, x+ 9, color="green", lw=2, ls='*', marker='+')
ax.plot(x, x+10, color="green", lw=2, ls='*', marker='o')
ax.plot(x, x+11, color="green", lw=2, ls='*', marker='s')
ax.plot(x, x+12, color="green", lw=2, ls='*', marker='1')
# marker size and color
ax.plot(x, x+13, color="purple", lw=1, ls='-', marker='o', markersize=2)
ax.plot(x, x+14, color="purple", lw=1, ls='-', marker='o', markersize=4)
ax.plot(x, x+15, color="purple", lw=1, ls='-', marker='o', markersize=8, markerfacecolor="red")
ax.plot(x, x+16, color="purple", lw=1, ls='-', marker='s', markersize=8,
markerfacecolor="yellow", markeredgewidth=2, markeredgecolor="blue")
fig
控制座標軸的樣式
座標軸樣式也是通常需要自定義的地方,像是標號或是標籤的位置或是字型的大小等。
圖的範圍
我們想做的第一件事也許是設定座標軸的範圍,可以使用 set_ylim
或是set_xlim
方法或者 axis('tight')
自動將座標軸調整的緊湊 The first thing we might want to configure is the ranges of the axes. We can do this using the set_ylim
and set_xlim
methods in the axis object, oraxis('tight')
for automatrically getting "tightly fitted" axes ranges:
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
axes[0].plot(x, x**2, x, x**3)
axes[0].set_title("default axes ranges")
axes[1].plot(x, x**2, x, x**3)
axes[1].axis('tight')
axes[1].set_title("tight axes")
axes[2].plot(x, x**2, x, x**3)
axes[2].set_ylim([0, 60])
axes[2].set_xlim([2, 5])
axes[2].set_title("custom axes range");
fig
對數刻度
也可以將軸的刻度設定成對數刻度,呼叫 set_xscale
與 set_yscale
設定刻度,引數選擇 "log" :
fig, axes = plt.subplots(1, 2, figsize=(10,4))
axes[0].plot(x, x**2, x, exp(x))
axes[0].set_title("Normal scale")
axes[1].plot(x, x**2, x, exp(x))
axes[1].set_yscale("log")
axes[1].set_title("Logarithmic scale (y)");
fig
自定義標號位置與符號
set_xticks
與 set_yticks
方法可以顯示地設定標號的位置,set_xticklabels
與 set_yticklabels
為每一個標號設定符號:
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(x, x**2, x, x**3, lw=2)
ax.set_xticks([1, 2, 3, 4, 5])
ax.set_xticklabels([r'$alpha$', r'$beta$', r'$gamma$', r'$delta$', r'$epsilon$'], fontsize=18)
yticks = [0, 50, 100, 150]
ax.set_yticks(yticks)
ax.set_yticklabels(["$%.1f$" % y for y in yticks], fontsize=18); # use LaTeX formatted labelsfig
=> [<matplotlib.text.Text at 0x5d75c90>,
<matplotlib.text.Text at 0x585fe50>,
<matplotlib.text.Text at 0x575c090>,
<matplotlib.text.Text at 0x599e610>]
還有許多方法可以控制主次標號,參考http://matplotlib.org/api/ticker_api.html
科學計數法
如果軸上涉及非常大的數,最好使用科學計數法:
fig, ax = plt.subplots(1, 1)
ax.plot(x, x**2, x, exp(x))
ax.set_title("scientific notation")
ax.set_yticks([0, 50, 100, 150])from matplotlib import ticker
formatter = ticker.ScalarFormatter(useMathText=True)
formatter.set_scientific(True)
formatter.set_powerlimits((-1,1))
ax.yaxis.set_major_formatter(formatter)
fig
軸上數與標籤的間距
# distance between x and y axis and the numbers on the axesrcParams['xtick.major.pad'] = 5rcParams['ytick.major.pad'] = 5fig, ax = plt.subplots(1, 1)
ax.plot(x, x**2, x, exp(x))
ax.set_yticks([0, 50, 100, 150])
ax.set_title("label and axis spacing")# padding between axis label and axis numbersax.xaxis.labelpad = 5ax.yaxis.labelpad = 5ax.set_xlabel("x")
ax.set_ylabel("y");
fig
# restore defaultsrcParams['xtick.major.pad'] = 3rcParams['ytick.major.pad'] = 3
調整座標軸的位置:
fig, ax = plt.subplots(1, 1)
ax.plot(x, x**2, x, exp(x))
ax.set_yticks([0, 50, 100, 150])
ax.set_title("title")
ax.set_xlabel("x")
ax.set_ylabel("y")
fig.subplots_adjust(left=0.15, right=.9, bottom=0.1, top=0.9);
fig
座標軸網格
grid
方法可以開啟關閉網格線,也可以自定義網格的樣式:
fig, axes = plt.subplots(1, 2, figsize=(10,3))# default grid appearanceaxes[0].plot(x, x**2, x, x**3, lw=2)
axes[0].grid(True)# custom grid appearanceaxes[1].plot(x, x**2, x, x**3, lw=2)
axes[1].grid(color='b', alpha=0.5, linestyle='dashed', linewidth=0.5)
fig
軸
我們也可以改變軸的屬性:
fig, ax = plt.subplots(figsize=(6,2))
ax.spines['bottom'].set_color('blue')
ax.spines['top'].set_color('blue')
ax.spines['left'].set_color('red')
ax.spines['left'].set_linewidth(2)# turn off axis spine to the rightax.spines['right'].set_color("none")
ax.yaxis.tick_left() # only ticks on the left sidefig
雙座標軸
twinx
與 twiny
函式能設定雙座標軸:
fig, ax1 = plt.subplots()
ax1.plot(x, x**2, lw=2, color="blue")
ax1.set_ylabel(r"area $(m^2)$", fontsize=18, color="blue")for label in ax1.get_yticklabels():
label.set_color("blue")
ax2 = ax1.twinx()
ax2.plot(x, x**3, lw=2, color="red")
ax2.set_ylabel(r"volume $(m^3)$", fontsize=18, color="red")for label in ax2.get_yticklabels():
label.set_color("red")
fig
設定座標原點在(0,0)點
fig, ax = plt.subplots()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0)) # set position of x spine to x=0ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0)) # set position of y spine to y=0xx = np.linspace(-0.75, 1., 100)
ax.plot(xx, xx**3);
fig
其他 2D 圖表風格
包括一般的 plot
方法, 還有很多其他函式能夠生成不同型別的圖表,詳情請見http://matplotlib.org/gallery.html 這裡列出其中幾種比較常見的函式方法。
n = array([0,1,2,3,4,5])
fig, axes = plt.subplots(1, 4, figsize=(12,3))
axes[0].scatter(xx, xx + 0.25*randn(len(xx)))
axes[0].set_title("scatter")
axes[1].step(n, n**2, lw=2)
axes[1].set_title("step")
axes[2].bar(n, n**2, align="center", width=0.5, alpha=0.5)
axes[2].set_title("bar")
axes[3].fill_between(x, x**2, x**3, color="green", alpha=0.5);
axes[3].set_title("fill_between");
fig
# polar plot using add_axes and polar projectionfig = plt.figure()ax = fig.add_axes([0.0, 0.0, .6, .6], polar=True)t = linspace(0, 2 * pi, 100)
ax.plot(t, t, color='blue', lw=3);
# A histogramn = np.random.randn(100000)
fig, axes = plt.subplots(1, 2, figsize=(12,4))
axes[0].hist(n)
axes[0].set_title("Default histogram")
axes[0].set_xlim((min(n), max(n)))
axes[1].hist(n, cumulative=True, bins=50)
axes[1].set_title("Cumulative detailed histogram")
axes[1].set_xlim((min(n), max(n)));fig
文字註釋
text
函式可以做文字註釋,且支援 LaTeX 格式:
fig, ax = plt.subplots()
ax.plot(xx, xx**2, xx, xx**3)
ax.text(0.15, 0.2, r"$y=x^2$", fontsize=20, color="blue")
ax.text(0.65, 0.1, r"$y=x^3$", fontsize=20, color="green");
fig
帶有多子圖與插圖的圖
fig.add_axes
在圖中加入新座標軸
subplots
, subplot2grid
,gridspec
等 子圖佈局管理器
subplots
fig, ax = plt.subplots(2, 3)
fig.tight_layout()
fig
subplot2grid
fig = plt.figure()ax1 = plt.subplot2grid((3,3), (0,0), colspan=3)ax2 = plt.subplot2grid((3,3), (1,0), colspan=2)ax3 = plt.subplot2grid((3,3), (1,2), rowspan=2)ax4 = plt.subplot2grid((3,3), (2,0))ax5 = plt.subplot2grid((3,3), (2,1))
fig.tight_layout()
fig
gridspec
import matplotlib.gridspec as gridspec
fig = plt.figure()
gs = gridspec.GridSpec(2, 3, height_ratios=[2,1], width_ratios=[1,2,1])for g in gs:
ax = fig.add_subplot(g)
fig.tight_layout()
fig
add_axes
fig, ax = plt.subplots()
ax.plot(xx, xx**2, xx, xx**3)
fig.tight_layout()# insetinset_ax = fig.add_axes([0.2, 0.55, 0.35, 0.35]) # X, Y, width, heightinset_ax.plot(xx, xx**2, xx, xx**3)
inset_ax.set_title('zoom near origin')# set axis rangeinset_ax.set_xlim(-.2, .2)
inset_ax.set_ylim(-.005, .01)# set axis tick locationsinset_ax.set_yticks([0, 0.005, 0.01])
inset_ax.set_xticks([-0.1,0,.1]);
fig
顏色對映圖與輪廓圖
顏色對映圖與輪廓圖適合繪製兩個變數的函式。
有許多預定義的顏色對映圖,參考:http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps
alpha = 0.7phi_ext = 2 * pi * 0.5def flux_qubit_potential(phi_m, phi_p):
return 2 + alpha - 2 * cos(phi_p)*cos(phi_m) - alpha * cos(phi_ext - 2*phi_p)
phi_m = linspace(0, 2*pi, 100)
phi_p = linspace(0, 2*pi, 100)
X,Y = meshgrid(phi_p, phi_m)
Z = flux_qubit_potential(X, Y).T
pcolor
fig, ax = plt.subplots()
p = ax.pcolor(X/(2*pi), Y/(2*pi), Z, cmap=cm.RdBu, vmin=abs(Z).min(), vmax=abs(Z).max())
cb = fig.colorbar(p, ax=ax)
fig
imshow
fig, ax = plt.subplots()
im = ax.imshow(Z, cmap=cm.RdBu, vmin=abs(Z).min(), vmax=abs(Z).max(), extent=[0, 1, 0, 1])
im.set_interpolation('bilinear')
cb = fig.colorbar(im, ax=ax)
fig
contour
fig, ax = plt.subplots()
cnt = ax.contour(Z, cmap=cm.RdBu, vmin=abs(Z).min(), vmax=abs(Z).max(), extent=[0, 1, 0, 1])
fig