這篇筆記主要來自《利用python進行數據分析》的第八章第一節,對matplotlib的基本使用介紹的特別清晰。關于matplotlib的基本概念(figure, subplot, axes)的介紹還可以看這里。
- Figure和Subplot
- 顏色、標記和線型
- 刻度、標簽和圖例
- 注解
- 保存到文件
pyplot模塊的API文檔,這里。
1. Figure和Subplot
plt.figure()
matplotlib的圖像都位于Figure對象中,可以使用plt.figure()創建一個新的figure:
>>> fig = plt.figure()
在ipython中,執行后會彈出一個空窗口:
該窗口的大小可以通過在plt.figure()中傳遞參數figsize=(width,height)來指定。
可以看到窗口的標題為“Figure 1”,要想設定Figure編號為3,則可執行:
>>> fig = plt.figure(3)
可通過plt.gcf()來獲得最后一次創建的Figure對象的引用。
Figure.add_subplot()
現在雖然有了一個Figure對象fig,但是還不能在其上畫圖。必須使用add_subplot()在fig上創建一個或多個subplot才行:
# 等價于fig.add_subplot(221)
# 意思是將fig劃分為2x2,指定第一個為畫圖位置
# 這里的subplot從1開始編號
# 其實就是在fig中固定位置創建了一個Axes對象。
>>> ax1 = fig.add_subplot(2, 2, 1)
執行后會得到:
若接著執行:
# 執行后fig中就有了三個Axes對象
>>> ax2 = fig.add_subplot(2, 2, 2)
>>> ax3 = fig.add_subplot(2, 2, 3)
得到:
fig.add_subplot(2, 2, 1)并不意味著對于該fig,固定劃分了2x2個subplot,這只是指出一個固定的位置來確定要創建的Axes對象。比如還可以接著執行下面的代碼再創建個小的Axes對象:
>>> ax4 = fig.add_subplot(339)
結果為:
可以看到Figure.add_subplot()的功能是將Axes對象創建于規則的網格(grid)上的,如果想要在Figure中的任意位置創建任意大小的Axes對象可使用Figure.add_axes()。
plot()
如果這時發出一條繪圖命令,matplotlib會在最后一個用過的Axes對象上(如果沒有則自動創建一個)進行繪制。
# 繪制黑色'k'虛線'--'
>>> plt.plot(np.random.randn(50).cumsum(), 'k--')
結果為:
如果想要在其他的axes中繪圖,則可以調用之前的Axes對象中的繪圖方法:
# hist方法有三個返回值,不想顯示就將其賦給以變量
>>> _ = ax1.hist(np.random.randn(100), bins=20, color='k', alpha=0.3)
>>> ax2.scatter(np.arange(30), np.arange(30) + 3*np.random.randn(30))
結果如下:
plt.subplots()
有一條語句可以快速的建立Figure和一組subplots,也就是plt.subplots()。它可以創建一個新的Figure,并返回一個含有已創建的Axes對象的numpy數組:
# 創建一個Figure對象fig2,并在其上繪制2x3個subplots
# fig2為新創建的Figure對象
# axess為一2x3數組,其中元素為Axes對象。
>>> fig2, axess = plt.subplots(2,3)
執行上述語句,會彈出一個新的Figure窗口:
之前使用plt.figure()創建Figure對象時,可以傳遞figsize參數來指定Figure對象的大小。在plt.subplots()中也可以傳遞該參數,見文檔中對**fig_kw參數的描述。
但是如果在創建完Figure對象后想改變figure size該怎么辦呢?可調用Figure類的方法set_figheight()和set_figweight()來分別設置Figure對象的長和寬。可見Figure類的文檔說明。
可以看axess的內容:
>>> axess
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x1178f9240>,
<matplotlib.axes._subplots.AxesSubplot object at 0x117920438>,
<matplotlib.axes._subplots.AxesSubplot object at 0x117947710>],
[<matplotlib.axes._subplots.AxesSubplot object at 0x117b28438>,
<matplotlib.axes._subplots.AxesSubplot object at 0x117b50748>,
<matplotlib.axes._subplots.AxesSubplot object at 0x117b7a9e8>]], dtype=object)
可以通過指定參數sharex=True使各列Axes對象使用同一個x軸(這樣,調整某列的xlim將會影響對應的axes),而設置sharey=True使各行Axes對象使用同一個y軸(這樣,調整某行的ylim將會影響對應的axes)。
>>> plt.close(2)
>>> fig2, axess = plt.subplots(2,3, sharex=Ture, sharey=Ture)
結果為:
plt.subplots_adjust()
默認情況下(matplotlib有默認的配置文件),matplotlib會在subplot外圍留下一定的邊距,并在subplot之間留下一定的間距。利用plt.subplots_adjust()或Figure.subplots_adjust()可以修改這些間距。兩個函數的參數都是一樣的(見文檔),但前者是在當前Figure中操作,而后者是在指定的Figure中操作的。
# 操作的是fig2
>>> plt.subplots_adjust(wspace=0, hspace=0)
結果為:
可以看到matplotlib中的畫圖是很自由的,并不會檢查坐標的重疊。
# 操作的是上面的Figure 1
>>> fig.subplots_adjust(wspace=0, hspace=0)
結果為:
當畫完圖之后,要記得使用plt.close()關掉對應的Figure,釋放內存。有多種關閉方式,可看文檔。
# 關閉所有Figure。
>>> plt.close('all')
總結一下,Figure對象就相當于一個巨大的畫板,可以在畫板上放置多個畫紙,各個畫紙也就是Axes對象,畫圖就是畫在Axes對象上的。各個Axes對象都有自己的坐標軸,可以分別控制。文檔中對這幾個概念的描述在這里。其中有張圖可以很好的理解Figure與Axes的各部分。
下面都以plot畫線來介紹。
2. 顏色、標記和線型
上一節中調用plot()畫線時(plt.plot()或axes.plot()),傳遞了一個參數'k--',指出畫的線為黑色虛線,這是對線的顏色和形式一種便捷的設置,這種簡便寫法中還可以加入一個標記的形式控制(也就是(x,y)點的形式)。
注意,plot()的字典參數**kwargs用于指定要畫的Line2D線對象的各種屬性。
控制格式為,注意順序不能變:
fmt = '[color][marker][line]'
如:
>>> fig, ax = plt.subplots(1,1)
# 相當于,這種顯示控制:
# ax.plot(np.random.randn(20), linestyle='-', color='r', marker='o')
>>> ax.plot(np.random.randn(20), 'ro-')
得到下圖:
fmt中的三項都是可選的,各項可配置的值可看這里的Notes部分。
但是要注意,fmt這種簡便設置方式與顯示參數的設置還是不同的。
# 只設置顏色和marker形式,則只畫點,不畫線
>>> ax.plot(np.random.randn(20), 'bo‘)
# 雖只設置顏色和marker形式,依然畫出了線
>>> ax.plot(np.random.randn(20), color='g', marker='o')
另外使用color參數設置顏色時,或fmt中只設置顏色時,可以使用更多的顏色設置方式,具體可看這里。
在線型圖中,非實際的數據點默認是按照線性方式插值的。可以通過plot()的參數drawstyle來修改插值方式:
# 清除axes上的繪制
>>> ax.clear()
>>> ax.plot(np.random.randn(20), 'ko-', drawstyle='steps-post')
3. 刻度、標簽和圖例
這一節講解的就是對坐標軸的控制,包括軸標簽、軸刻度的標簽、軸刻度、軸刻度范圍、圖例。下面都是顯示指定要控制的Axes對象,使用Axes類中的方法。
先繪制一個簡單的圖像,并在其上舉例。
>>> fig, ax = plt.subplots(1,1)
>>> np.random.seed(985)
>>> ax.plot(np.random.randn(30), 'ro-')
- 設置該Axes對象的標題為'random plot',并設置軸標簽為'x','y',分別使用Axes.set_title(),Axes.set_xlabel(),Axes.set_ylabel()。
# 設置標題
>>> ax.set_title('random plot')
# 設置xy軸標簽
>>> ax.set_xlabel('x')
>>> ax.set_ylabel('y')
對應的,可以通過Axes.get_title(),Axes.get_xlabel(),Axes.get_ylabel()來得到對應的值。
>>> ax.get_title()
'random plot'
>>> ax.get_xlabel()
'x'
>>> ax.get_ylabel()
'y'
-
軸的范圍
由于沒有指定要畫線的x值,所以默認為range(0,30),而看圖上的x軸坐標范圍,其在左右明顯要小于0并大于29,我們可以使用Axes.get_xlim()和Axes.get_ylim()來分別得到x軸和y軸的坐標范圍。
>>> ax.get_xlim()
(-1.4500000000000002, 30.449999999999999)
>>> ax.get_ylim()
(-1.271508964835969, 2.6919009419421762)
可見畫圖時默認增加了些冗余,當然我們可以對其更改。
可以使用Axes.set_xlim()和Axes.set_ylim()來分別設置軸的范圍:
>>> ax.set_xlim(-10, 40)
同樣也可以縮小:
>>> ax.set_xlim(10, 20)
兩次縮放了軸的范圍,與原始圖對比下可發現,在縮放中,x坐標(只操作了x軸)刻度也在自動變化。這是因為我們沒有顯示設置x軸的坐標刻度值,所以matplotlib自動設置的。如果顯示設置了,那么會一直使用我們設置好的,不論如何縮放軸范圍。
-
軸刻度的控制
刻度,也就是tick,可通過Axes.get_xticks()和Axes.get_yticks()來看看tick當前的軸刻度是什么:
# 接著上面執行代碼。
>>> ax.get_xticks()
array([ 10., 12., 14., 16., 18., 20.])
>>> ax.get_yticks()
array([-1.5, -1. , -0.5, 0. , 0.5, 1. , 1.5, 2. , 2.5, 3. ])
可見也就是軸上的坐標值,同樣的可以使用Axes.set_xticks()和Axes.set_yticks()來更改:
# 會返回一個列表,包含11個創建的XTick對象。
>>> ticks = ax.set_xticks(range(0,33,3))
此時x軸的坐標刻度也就固定了,再放大x軸范圍也不會改變坐標刻度,而只是縮放了圖像。如:
# 設置為最初的x軸范圍
>>> ax.set_xlim(-1.4500000000000002, 30.449999999999999)
-
軸刻度標簽的控制
目前在x軸上有11個刻度,都是數字表示的,還可以使用Axes.set_xticklabels()將其他任何值用做標簽:
# 改為其他數值
>>> labels = ax.set_xticklabels(range(10,43,3))
# 改為字符串
>>> labels = ax.set_xticklabels([i for i in 'abcdefghijk'])
# 指定的字符串還可以為LateX數學表達式。
>>> labels = ax.set_xticklabels([r'$x^{%s}$'%i for i in range(1,12)])
此外,可以對函數Axes.set_xticklabels()的**kwargs傳入其他參數來控制Text對象的屬性。如字體角度rotation和字體大小fontsize(其他屬性可看文檔):
>>> labels = ax.set_xticklabels([r'$x^{%s}$'%i for i in range(1,12)], rotation = 30, fontsize='large')
此外,因為我們使用了Axes.set_xticks()設置了11個坐標刻度,所以在Axes.set_xticklabels()中需要傳遞長度11的列表指定這11刻度的label。如果列表長度小于11的話,如5,那么,只改變前5個刻度的label,后6個刻度的label為空,也就啥也沒有。如果列表中的元素多于11個,那么只用前11個。
>>> labels = ax.set_xticklabels([r'$x^{%s}$'%i for i in range(1,5)], rotation = 30, fontsize='large')
-
圖例
可以使用Axes.legend()在Axes中添加圖例,對繪制進行說明。
對于上面畫的紅線,現在加入一個圖例:
# 返回Legend對象
>>> l1 = ax.legend(['red'])
這種方法是不推薦的,因為當Axes中繪制了多條線后,需要在legend()中傳入對應著畫線順序的label,而該順序很容易搞混的。
常用的一種加入圖例方法是在畫圖是傳入label參數,然后在使用legend():
>>> np.random.seed(211)
>>> ax.plot(np.random.randn(30), 'ko--', label='black')
>>> ax.legend()
還用一種更為全面的控制方式,即指出需要圖例的那條線和其對應的字符串,清除掉這兩條線,重畫下:
>>> ax.clear()
>>> np.random.seed(233)
# 注意,plot()返回的是包含Line2D對象的列表,如果不加',‘,那么l1是列表。
>>> l1, = ax.plot(np.random.randn(30), 'ko-')
>>> np.random.seed(666)
>>> l2, = ax.plot(np.random.randn(30), 'ro--')
# 將Line2D對象放入第一個列表中,第二個列表中的字符串順序要一致。
>>> ax.legend([l1,l2], ['black', 'red'])
關于圖例的控制(位置、字體等),可看文檔Axes.legend()。
4. 注解
- 文本的繪制
在Axes對象中繪制文本可使用函數Axes.text()。該函數的使用很簡單,傳入坐標和文本,就會將文本繪制在對應的坐標上。可以向函數傳入Text屬性來控制文本的顯示。
>>> fig, ax = plt.subplots(1,1)
>>> x = np.linspace(-np.pi,np.pi,100, endpoint=True)
>>> y = np.sin(x)
>>> ax.plot(x,y, 'k-')
>>> ax.set_xticks(np.arange(-np.pi,np.pi+1,np.pi/4))
# 這些tick labels本身就是Text對象。該函數會返回包含對應的9個Text對象的列表。
>>> ax.set_xticklabels([r'$-\pi$', r'$-\frac{3}{4}\pi$', r'$-\frac{1}{2}\pi$', r'$-\frac{1}{4}\pi$', r'0', r'$\frac{1}{4}\pi$', r'$\frac{1}{2}\pi$',r'$\frac{3}{4}\pi$', r'$\pi$'])
# 在原點繪制文本,后面的參數設置文本的顯示屬性。
# ha和va設置的是文本相對(0,0)的位置,‘center'是以點為中心。
>>> ax.text(0,0, r'hello world!', color='r', ha='center', va='center',rotation=30, size='medium', style = 'italic' )
- 注釋
在圖上繪制注釋使用的是Axes.annotate)()。關于該函數的使用例子可看這里。
# 在坐標(-1,0.5)處寫Text對坐標(0,0)進行注釋。
>>> ax.annotate(s=r'origin', xy=(0,0), xytext=(-1,0.5), arrowprops={'arrowstyle':'->'})
5. 保存到文件
使用plt.save()可將當前Figure中的內容保存到文件(也可以是file-like object)中。
文件的類型可以通過給定的文件名擴展名推斷出來,或者指定函數參數format。