【Matplotlib】
教程:https://morvanzhou.github.io/tutorials/data-manipulation/plt/
官方文檔:https://matplotlib.org/api/pyplot_summary.html
這個模塊是一個Python上用於進行繪圖的模塊。做科研的人經常會使用的matlab就是這個模塊的一個競品。就我個人而言,matplotlib感覺更像是一個echarts.js之類前端庫的一個后端版本。和echarts一樣,它可以支持通過python畫出散點圖,條狀圖,餅狀圖甚至是3D圖和動畫。
由於靠這個畫圖的話一般都會遇到比較復雜的數據處理,所以numpy也經常和matplotlib一起出現一起使用。
■ 基本安裝和使用
在linux上安裝可以通過包管理工具比如yum(我的虛擬機里面已經安裝了matplotlib,但是已經忘了當時是用pip還是yum裝的了…)
windows的話目測用pip也可以一步搞定。如果不行可以參考上面給出的參考文檔連接。
下面是一個matplotlib的helloworld
import matplotlib.pyplot as plt import numpy as np x = np.linspace(-1,1,50) y = 2*x + 1 plt.figure() plt.plot(x,y) plt.show()
如果是在windows下的某個腳本中運行這段程序,那么完了之后會跳出一個窗口,顯示一幅圖片如圖所示:
可以看到這個顯示的圖的組件本身還有一些功能比如跟隨鼠標顯示坐標值,放大局部圖像等。
然后我們來看看這段代碼。首先x是numpy中的一個array對象,其內容是-1到1之間均等地取了大約50個值。這些值其實就是我們圖中后來的橫坐標。然后通過array加減乘除時特別的性質,可以將類似於函數表達式那樣的式子寫在代碼里。即2*x + 1,很接近函數表達式的y=2x+1了。那么y也是一個array,一一對應x中各個值進行2*x+1后的值。這樣的一個array,x和array y,兩個坐標集組成了一幅圖。那么如何將這幅圖顯示出來,用到的就是后三行代碼。
這里需要注意的是,繪制這個圖的過程其實是用直線將各個點之間連接起來的操作。由於這個圖最終本身就是一個直線,所以看起來似乎最初x的樣本量取50個點和兩個點結果都一樣。但是如果表達式換成x**2 + 1那么取點多少就會影響到這個二次函數圖是否光滑了。下面是分別取10個點和50個點時二次函數的圖。
(10個點)
(50個點)
■ 圖像(plot)的控制
● 線條的控制
上面代碼中的plt.figure()方法聲明的其實是一個figure窗口,即windows中彈出來的這個窗口。這個窗口中可以包含很多個圖。上面只是添加了一個圖。實際上還可以嘗試添加更多。
一個圖的概念其實在這里叫plot,正如你所見,plot方法用於向一個figure窗口中添加一個圖。
x = np.linspace(-10,10,50) y1 = 2*x + 1 y2 = x**2 plt.figure() plt.plot(x,y1) plt.plot(x,y2) plt.show()
因為出現了兩個圖,matplotlib自動為兩個圖設置了不同的顏色以示區分。如果想要自定義顏色,線條樣式等等屬性的話,可以將這些參數寫在plot方法中比如:
plot(x,y,linestyle='dashed',linewidth=0.5,color='#3479f7') 其余更多參數以及參數如何取值可以參考https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.html#matplotlib.pyplot.plot
● 坐標軸控制
上面的所有圖中,坐標軸都是根據我們給出的數據范圍自動生成的。如果想要手動指定坐標軸的范圍呢
使用plt.xlim/ylim兩個方法可以分別設置x軸和y軸的范圍。用法就是xlim(min,max)。使用plt.xlabel和ylabel可以設置x/y軸的說明文字。使用plt.xticks/yticks兩個方法可以分別設置X和Y軸的坐標點的值是多少。參數的話是numpy.linspace的返回值。
比如沿用上面用到的y1和y2,進行這樣的代碼畫圖:
plt.figure() plt.plot(x,y1) plt.plot(x,y2,linestyle='dashed',linewidth=0.5,color='red',marker='.',) plt.xlim(1,3) plt.xlabel('value of X') plt.xticks(np.linspace(1,3,11)) plt.show()
需要額外注意的一點是,加入xlim和xticks互相沖突了,那么最終顯示的圖是以后設置者為准。比如這里xticks再xlim后面設置,那么最終畫出的圖以xticks中定的范圍為准。
除了簡單的數值設置外,xticks/yticks還支持label設置。比如xticks([1,2,3,4],['bad','ok','good','verygood'])這樣的方式,來將數值坐標值和文字描述的坐標值一一對應起來。
plt.gca() 這個方法返回一個包含所有坐標軸的對象。gca的全稱估計是get current axes,也就是獲取到當前所有坐標軸。
其中比較重要的一條屬性是spines,這個屬性是一個字典,可以有left,right,top,bottom的取值分別對應一個圖的四條邊。比如令ax = plt.gca(),然后
ax.spines['top'].set_color('none') 以及 ax['right'].set_color('none') 這樣子的話那么就可以讓顯示出來的圖沒有上邊框和右邊框。
除了set_color,還可以set_position。比如ax.spines['bottom'].set_position(('data',0)),首先bottom指的是X軸的那個邊,把它set_position到data是0,即Y軸的值為0的高度。類似的將left也設置一下。然后將top和right設置不可見之后,這樣就得到一個經典的坐標系的圖了。
上面說的是plt.gca().spines這個屬性。其實gca返回的對象中還有一些屬性比如xaxis/yaxis這兩個就是坐標軸的對象(spines的本意是脊柱、干,所以指的更多是構成坐標軸的那條線。而xaxis這種很明顯就是指坐標軸本身,指的更多是坐標軸的數字、文字標簽的部分。)
同樣的,令 ax = plt.gca() 的話,ax.xaxis就是X軸對象,可以有下列方法:
ax.xaxis.set_ticks_position('top') 可以設置X軸的數字和坐標點出現在圖的哪條邊上。如果是top就是出現在上邊。如果配合spines['top'].set_color('none')的話,那么就可以得到只有數據標點(且在圖上方位置),沒有軸線的圖了。
● 圖例
在上面畫兩條線的方法plot中,還可以添加一個參數label,用來通過文字說明這條線的內容。有了這個label之后就可以將其關聯到圖例上。這樣圖例就可以顯示說明。
關聯圖例的方法是plt.legend,不過需要注意的是legend方法一定要在plot完成之后調用,否則將無法呈現圖例(這其實呼應了之前xlim和xticks取值的后設置后決定,這強調了plt對於圖像繪制是線性執行的特點)。比如如下代碼:
plt.figure() plt.plot(x,y1,label='straight line') plt.plot(x,y2,linestyle='dashed',linewidth=0.5,color='red',marker='.',label='square line') plt.legend() plt.ylim(-1,4) plt.xlim(-3,3) ax = plt.gca() ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') ax.spines['left'].set_position(('data',0)) ax.spines['bottom'].set_position(('data',0)) plt.show()
圖例出現的位置默認是在左下方,可以在legend方法中用loc參數指明具體的位置。比如plt.legend(loc='upper right')是出現在右上角。類似的參數值還有lower right,center left等等。此外還有一個best,這個值是說把位置自動交給電腦去判斷,盡量選擇一個不擋住任何東西的好的位置。
● 划線標點 標注
上面說過,plot方法其實是將給出的參數分別解釋為X坐標和Y坐標的集合,然后一一將這些點用直線連接起來組成的圖形。那么要在圖上額外畫一條線段就不難了。
比如plt.plot([1,1],[0,2],linestyle='dashed')這個畫的線段就是(1,0)點和(1,2)點之間的連線,就是一條垂直於X軸的線。
至於標點,其實用的是用來做散點圖的plt.scatter方法。由於散點圖后面還會詳細說這里就不多說了。這里只給出一個調用方法:
plt.scatter([1,],[2,],s=20,color='blue')
除了標點,另外一個重要的標注就是文字標注了。文字標注大概可以通過這樣的方法來實現:
plt.annotate('emphasize point',xy=(1,3),xytext=(15,-15),textcoords='offset pixels',arrowprops=dict(arrowstyle='<|-', connectionstyle="arc3,rad=.2"))
emphasize point是描述文字。xy則是本條標注針對的圖中的點。xytext配合textcoords指出的定位方式,指出了標注文字應該放在什么地方,如果按照上面的這種組合的話,就是說文字的左上角點是標注點的右下方各15個像素的地方。arrowprops參數比較有意思,它指出了是否需要一個標注文字和標注點之間的箭頭標識。arrowstyle可以有<-,<|-,->,-|>,是不是很形象? 當箭頭指向左邊的意思是,圖上的箭頭是從標注點指向標注文字的,向右則相反。connectionstyle是一個完整的字符串,不過它也有很多信息,比如rad指出了箭頭線的弧度。
以上是對於針對某個點的標注文字,如沒有特定的目標點,只是想寫點文字的話可以使用plt.text方法
plt.text(-2.4, 6, 'This is some text',fontdict={'size': 16, 'color': 'r'})
這個方法中(-2.4,6)這個點是文字左上角的坐標,fontdict指定一個指定字體格式的字典。比較好懂就不說了。
總結上面說到的一些東西再給一個示例:

plt.figure() plt.plot(x,y1,label='straight line') plt.plot(x[:35],y2[:35],linestyle='dashed',linewidth=0.5,color='red',label='square line') plt.plot([1,1],[0,3],'k--') plt.scatter([1,],[3,],s=20,color='blue') plt.annotate('emphasize point',xy=(1,3),xytext=(15,-15),textcoords='offset pixels',arrowprops=dict(arrowstyle='<|-', connectionstyle="arc3,rad=.2")) plt.text(-2.4, 6, 'This is some text',fontdict={'size': 16, 'color': 'r'}) plt.legend(loc='best') ax = plt.gca() ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') ax.spines['left'].set_position(('data',0)) ax.spines['bottom'].set_position(('data',0)) plt.show()
■ 分圖類型示例
● 散點圖
n = 1024 # data size X = np.random.normal(0, 1, n) # 每一個點的X值 Y = np.random.normal(0, 1, n) # 每一個點的Y值 T = np.arctan2(Y,X) # for color value plt.figure() ax = plt.gca() ax.spines['bottom'].set_position(('data',0)) ax.spines['left'].set_position(('data',0)) ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') plt.scatter(X,Y,c=T,s=20) plt.xlim(-0.5,0.5) plt.show()
np.random.normal方法是生成一個平均值為0,方差為1,總數為1024的數據集。這樣兩個數據集分別作為X坐標和Y坐標構成了1024個點的集合。再通過調用arctan2將所有隨機點對應的一個弧度求出,把這個弧度當做是一個顏色的替代值。
隨后就是生成圖形的過程,對於gca返回的處理部分是把上右邊去掉,下左邊移到中間形成坐標系的圖。然后scatter方法一次性將所有的點畫到圖上。X是橫坐標的array,Y是縱坐標的array,c是color指定顏色的值,s是每個點的size。最后為了方便看(由於散點圖在X和Y軸上都符合正則分布,導致原點附近的點特別密集,如果scale比較大中間就是一片糊),limit一下X軸的坐標。
最終看到的圖形是這樣的:
● 其他圖種,都是依葫蘆畫瓢的事,就不多說了,可以網上隨便搜一個看一看就行了。
■ 多圖合一顯示
上面的所有實例中,plt.figure()得到的窗口對象里面都只顯示了一幅圖,圖種可能有多條線。
如果要多張圖顯示在同一個窗口中,那么需要用到plt.subplot這個方法。這個方法的調用方式通常是這樣的:
plt.subplot(2,2,1)或者plt.subplot(221)。221的意思是,如果將整個窗口分隔成2*2格式的話,那么現在切換plt到從左到右從上至下第1個網格內進行編輯。直到再次調用subplot切換網格之前,plt做的所有操作都會這個網格內進行。
matplotlib嚴格按照代碼中畫圖的順序來畫圖,如果后來畫的圖和前面的圖有重疊,那么前面的圖就會被整個刪除,接下來界面上就會只剩下后來畫的圖了。
另外subplot並不要求前后調用的幾次都是統一規格網格的圖,比如下面這段代碼顯示出的圖是這樣的:
def dicars(plt): ax = plt.gca() ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') ax.spines['left'].set_position(('data',0)) ax.spines['bottom'].set_position(('data',0)) plt.figure() # 第一幅圖 plt.subplot(221) plt.plot([1,1],[0,1]) dicars(plt) # 第二幅圖 plt.subplot(222) plt.plot([2,2],[0,1]) dicars(plt) # 第三幅圖 plt.subplot(212) plt.plot([3,3],[0,1]) dicars(plt) plt.show()
另外進行畫圖的布局還可以用grid方式的流式布局。參考https://morvanzhou.github.io/tutorials/data-manipulation/plt/4-2-subplot2/
多圖布局除了同一窗口多個圖外,還可以有圖中圖這種模式化,也可以參考上面鏈接中的教程。
■ 多坐標軸顯示
多坐標軸也是一種常見的需求。常見的就是需要在相對小的區域分顯示兩個值域出入比較大的函數的時候,即多坐標軸多指多Y軸的情況。
matplotlib實現多坐標軸的代碼如下:
import matplotlib.pyplot as plt import numpy as np import math x = np.arange(-2,2,0.1) y1 = map(math.exp,x) y2 = -1 * np.array(y1) fig, left_ax = plt.subplots() # 獲取當前的坐標,是第二個返回對象 right_ax = left_ax.twinx() # twinx生成另一個坐標對象 left_ax.plot(x,y1,color='blue') left_ax.set_ylabel('value of Y1',color='blue') right_ax.plot(x,y2,color='green') right_ax.set_ylabel('value of Y2',color='green') plt.show()
生成圖: