matplotlib基礎(chǔ)
matplotlib是面向?qū)ο蟮睦L圖工具包,繪制的圖形中的每一個(gè)元素都是一個(gè)對(duì)象,可以修改這些對(duì)象的屬性,從而改變圖樣式。
matplotlib中主要繪圖對(duì)象列表如下:
- Figure對(duì)象,可以想象為一張畫(huà)布;
- Axes對(duì)象,字面理解為坐標(biāo)軸也可以認(rèn)為時(shí)子圖,在一個(gè)Figure對(duì)象中可以包含多個(gè)Axes對(duì)象,也就是說(shuō)一張畫(huà)布可以包含多個(gè)子圖;* Line2D對(duì)象,代表線條;
- Text對(duì)象,代表了文字;
雖然matplotlib是面向?qū)ο蟮睦L圖工具包,但是也提供了一些常用的繪圖方法,可以直接調(diào)用plt模塊相關(guān)方法就可以完成繪圖需求,這是因?yàn)閜lt模塊內(nèi)部保存了當(dāng)前的Figure對(duì)象信息,當(dāng)使用plt的相關(guān)方法繪圖時(shí),底層實(shí)際調(diào)用了當(dāng)前Figure對(duì)象的相關(guān)方法??梢酝ㄟ^(guò)plt.gcf()和plt.gca()分別獲取當(dāng)前用于繪圖的Figure和Axes對(duì)象。
通過(guò)plt.figure()創(chuàng)建一個(gè)新的Figure對(duì)象,然后在此Figure對(duì)象上創(chuàng)建Axies對(duì)象:
In [1]: import numpy as np
In [2]: import matplotlib.pyplot as plt
In [3]: fig1 = plt.figure()
In [4]: ax1 = fig1.add_subplot(1, 1, 1)
In [5]: fig1
Out[5]: <matplotlib.figure.Figure at 0x2b4d0bdf4e0>
In [6]: plt.gcf()
Out[6]: <matplotlib.figure.Figure at 0x2b4d0bdf4e0>
In [7]: ax1
Out[7]: <matplotlib.axes._subplots.AxesSubplot at 0x2b4d0bff780>
In [9]: plt.gca()
Out[9]: <matplotlib.axes._subplots.AxesSubplot at 0x2b4d0bff780>
In [10]: fig2 = plt.figure()
In [11]: ax2 = fig2.add_subplot(1, 1, 1)
In [12]: fig2
Out[12]: <matplotlib.figure.Figure at 0x2b4d0f602e8>
In [13]: plt.gcf()
Out[13]: <matplotlib.figure.Figure at 0x2b4d0f602e8>
In [14]: ax2
Out[14]: <matplotlib.axes._subplots.AxesSubplot at 0x2b4d0f882e8>
In [15]: plt.gca()
Out[15]: <matplotlib.axes._subplots.AxesSubplot at 0x2b4d0f882e8>
每當(dāng)創(chuàng)建新的Figure對(duì)象時(shí),plt.gcf()獲取到的當(dāng)前的Figure對(duì)象也變成了新創(chuàng)建的Figure對(duì)象,因?yàn)樗鼈兊膬?nèi)存地址相同。fig1.add_subplot(1, 1, 1)方法,用于在Figure對(duì)象上創(chuàng)建一個(gè)新的Axes對(duì)象,由于Figure可以包含多個(gè)Axes對(duì)象,所以這里的參數(shù)含義是說(shuō),添加一個(gè)Axes對(duì)象到布局為一行一列的第一個(gè)位置上。
在Figure對(duì)象上插入四個(gè)Axes對(duì)象,布局為兩行兩列:
In [16]: fig = plt.figure()
In [17]: ax1 = fig.add_subplot(2, 2, 1)
In [18]: ax2 = fig.add_subplot(2, 2, 2)
In [19]: ax3 = fig.add_subplot(2, 2, 3)
In [20]: ax1.plot(np.random.randn(50).cumsum(), 'k--')
Out[20]: [<matplotlib.lines.Line2D at 0x2b4d1ab5550>]
In [21]: _ = ax2.hist(np.random.randn(100), bins=20, color='k')
In [22]: ax3.scatter(np.arange(30), np.arange(30) + 3 * np.random.randn(30))
Out[22]: <matplotlib.collections.PathCollection at 0x2b4d1b1f9b0>
In [23]: fig.show()
以上代碼,首先創(chuàng)建一個(gè)Figure對(duì)象,然后插入3個(gè)Axes對(duì)象,接著分別在Axes對(duì)象上繪制了累積和線圖,直方圖以及散點(diǎn)圖,最后通過(guò)fig.show()方法顯示出圖形如下:

常用屬性設(shè)置
matplotlib繪制的圖形可以設(shè)置各種屬性,如坐標(biāo)系的刻度,標(biāo)題,標(biāo)簽等屬性。
In [24]: fig = plt.figure()
In [25]: ax = fig.add_subplot(1, 1, 1)
In [26]: # 設(shè)置標(biāo)題
In [27]: ax.set_title("Axes Example")
Out[27]: Text(0.5,1,'Axes Example')
In [28]: major_ticks = np.arange(0, 101, 20)
In [29]: minor_ticks = np.arange(0, 101, 5)
In [30]: # 設(shè)置刻度
In [31]: ax.set_xticks(major_ticks)
In [33]: ax.set_xticks(minor_ticks, minor=True)
In [34]: ax.set_yticks(major_ticks)
In [35]: ax.set_yticks(minor_ticks, minor=True)
In [36]: # 設(shè)置X, Y軸標(biāo)簽
In [37]: ax.set_xlabel("X axis")
Out[37]: Text(0.5,0,'X axis')
In [38]: ax.set_ylabel("Y axis")
Out[38]: Text(0,0.5,'Y axis')
In [39]: # 設(shè)備網(wǎng)格
In [41]: ax.grid(which='minor', alpha=0.2)
In [42]: ax.grid(which='major', alpha=0.5)
In [43]: # 添加文字
In [44]: ax.text(42.5, 50, "shiyanlou")
Out[44]: Text(42.5,50,'shiyanlou')
In [45]: fig.show()
執(zhí)行以上代碼,繪制的圖形如下:

以上代碼首先創(chuàng)建了一個(gè)Figure對(duì)象,接著創(chuàng)建了唯一一個(gè)Axes對(duì)象,后續(xù)的所有屬性都基于該Axes對(duì)象設(shè)置:
- ax.set_title 設(shè)置圖形的標(biāo)題;
- ax.set_xticks 設(shè)置X軸的刻度,其中minor=True參數(shù)表示設(shè)置更小的刻度;
- ax.set_yticks 設(shè)置Y軸的刻度;
- ax.set_xlabel 設(shè)置X軸的標(biāo)簽;
- ax.set_ylabel 設(shè)置X軸的標(biāo)簽;
- ax.grid 開(kāi)啟圖形的刻度網(wǎng)格,其中minor=True參數(shù)表示顯示小刻度的網(wǎng)格;
- ax.text 為圖形添加文字,前兩個(gè)參數(shù)表示添加的文字在坐標(biāo)系中的位置;
繪制曲線的顏色,設(shè)置圖例,代碼如下:
In [46]: x = np.linspace(0, 1, 100)
In [47]: fig = plt.figure()
In [48]: ax = fig.add_subplot(1, 1, 1)
In [49]: ax.set_title("shiyanlou")
Out[49]: Text(0.5,1,'shiyanlou')
In [50]: ax.plot(x, x ** (1/8), 'b--', label=r'$y = x^{1/8}$')
Out[50]: [<matplotlib.lines.Line2D at 0x2b4d1453908>]
In [51]: ax.plot(x, x ** 8, 'r--', label=r'$y = x^{8}$')
Out[51]: [<matplotlib.lines.Line2D at 0x2b4d1453f98>]
In [52]: ax.plot(x, x ** (1/2), 'r.', label=r'$y = x^{1/2}$')
Out[52]: [<matplotlib.lines.Line2D at 0x2b4d146c358>]
In [53]: ax.plot(x, x ** 2, 'b.', label=r'$y = x^{2}$')
Out[53]: [<matplotlib.lines.Line2D at 0x2b4d1453588>]
In [54]: ax.plot(x, x, 'g-', label=r'$y = x$')
Out[54]: [<matplotlib.lines.Line2D at 0x2b4d1453e48>]
In [55]: ax.legend()
Out[55]: <matplotlib.legend.Legend at 0x2b4d1453128>
In [56]: ax.axis([0, 1, 0, 1])
Out[56]: [0, 1, 0, 1]
In [58]: fig.show()
圖形顯示如下:

在上面的代碼中,通過(guò) np.linspace(0, 1, 100) 創(chuàng)建了 100 個(gè)值,這些值平均分布在 0 到 1 的范圍內(nèi)。接著我們?cè)?Axes 對(duì)象上繪制了 5 條曲線,這 5 條曲線分別對(duì)應(yīng)于 5 個(gè)函數(shù),函數(shù)名稱已經(jīng)通過(guò)圖例的形式顯示在圖形上了。
其中,我們使用類(lèi)似于 ax.plot(x, x ** (1/8), 'b--', label=r'y = x^{1/8}y=x
1/8
') 方法繪制曲線,該方法有四個(gè)參數(shù),前兩個(gè)參數(shù)分別對(duì)應(yīng)于 X, Y 軸的數(shù)據(jù),繪圖時(shí)會(huì)根據(jù) x 和 x ** (1/8) 值序列確定曲線的位置。第三個(gè)參數(shù) b-- 代表繪制的曲線是 blue 藍(lán)色,樣式是虛線。matplotlib 中有多種指定線條顏色和樣式的辦法,如 r-- 指明紅色虛線,r. 代表紅色點(diǎn),更多的樣式可以參考 matplotlib 文檔。最后一個(gè)參數(shù) label=r'y = x^{1/8}y=x
1/8
') 設(shè)置了線條的標(biāo)簽,該標(biāo)簽文字內(nèi)容是數(shù)學(xué)公式,matplotlib 支持 LaTeX 語(yǔ)法顯示各種數(shù)學(xué)公式。
有的時(shí)候,我們想在圖形上顯示曲線標(biāo)簽信息,這個(gè)時(shí)候可以使用 ax.legend() 方法,該方法使用曲線的標(biāo)簽信息(也就是繪制曲線是傳入的 label 參數(shù))自動(dòng)生成圖例,并將圖例放到圖形合適的位置。
最后為了方便查看圖形,我們將圖形的視口坐標(biāo)設(shè)置成了 [0, 1, 0, 1] ,代表X, Y坐標(biāo)軸范圍都設(shè)置為 0 到 1。如果圖形顯示范圍超過(guò)了 0 到 1 的范圍,則會(huì)自動(dòng)截取 0 到 1 范圍內(nèi)的圖形進(jìn)行顯示。
matplotlib 繪圖時(shí)可以設(shè)置很多屬性,比如設(shè)置注解,設(shè)置外部圖片等,這些都可以通過(guò)調(diào)用 Axes 對(duì)象相應(yīng)方法進(jìn)行,更多的用法可以查看 Axes 對(duì)象 API 文檔。
常用圖形
線形圖
線性圖更多時(shí)候是用來(lái)觀察變量之間的函數(shù)關(guān)系,如果滿足y = 2x的線性關(guān)系,那么繪制的圖形是一條直線,如果沒(méi)關(guān)系,則是一堆涂鴉。
使用Axes.plot方法繪制線形圖:
In [78]: fig1 = plt.figure()
In [79]: ax1 = fig1.add_subplot(1, 1, 1)
In [80]: x = np.random.randn(100)
In [81]: y = np.random.randn(100)
In [83]: ax1.plot(x, y)
Out[83]: [<matplotlib.lines.Line2D at 0x2b4d2a6ba58>]
In [84]: fig1.show()
以上代碼創(chuàng)建了兩個(gè)隨機(jī)的數(shù)值序列,這兩個(gè)數(shù)值序列之間理論上沒(méi)有任何關(guān)系,顯示的圖形如下:

直方圖
直方圖可以顯示數(shù)據(jù)分布情況,X 軸一般是統(tǒng)計(jì)的樣本,而 Y 是樣本對(duì)應(yīng)的統(tǒng)計(jì)度量。為了構(gòu)建直方圖,第一步是將值的范圍分段,即將整個(gè)值的范圍分成一系列間隔,然后計(jì)算每個(gè)間隔中有多少值。 這些值通常被指定為連續(xù)的,不重疊的變量間隔。 間隔必須相鄰,并且通常是(但不是必須的)相等的大小。在 matplotlib 中可以使用 Axes.hist 方法繪制直方圖:
In [85]: fig2 = plt.figure()
In [86]: ax2 = fig2.add_subplot(1, 1, 1)
In [87]: data = np.random.normal(0, 20, 1000)
In [88]: bins = np.arange(-100, 100, 5)
In [90]: ax2.hist(data, bins=bins)
Out[90]:
(array([ 0., 0., 0., 0., 0., 1., 0., 2., 2.,
4., 8., 9., 14., 30., 39., 60., 73., 83.,
88., 100., 80., 103., 75., 69., 58., 44., 25.,
7., 12., 7., 4., 0., 2., 1., 0., 0.,
0., 0., 0.]),
array([-100, -95, -90, -85, -80, -75, -70, -65, -60, -55, -50,
-45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5,
10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60,
65, 70, 75, 80, 85, 90, 95]),
<a list of 39 Patch objects>)
In [91]: fig2.show()
上面的代碼首先通過(guò) np.random.normal 方法,在 0 到 20 的范圍產(chǎn)生 1000 個(gè)符合正態(tài)分布的數(shù)據(jù)值,然后通過(guò) np.arange(-100, 100, 5) 創(chuàng)建了 X 軸的區(qū)間刻度。創(chuàng)建的圖形類(lèi)似下圖:

散點(diǎn)圖
散點(diǎn)圖,將所有的數(shù)據(jù)值在圖形中繪制成點(diǎn),這樣有多少數(shù)據(jù)值在圖形中就會(huì)有多少個(gè)點(diǎn)。通過(guò)這些數(shù)據(jù)點(diǎn)可以看出數(shù)據(jù)值的分布模式,比如是否有聚類(lèi)模式,或者相關(guān)關(guān)系或者發(fā)現(xiàn)離群點(diǎn)。在 matplotlib 中可以通過(guò) Axes.scatter 繪制散點(diǎn)圖:
In [92]: fig3 = plt.figure()
In [93]: ax3 = fig3.add_subplot(1, 1, 1)
In [94]: x = np.arange(1, 101)
In [95]: y = 20 + 3 * x + np.random.normal(0, 60, 100)
In [96]: ax3.scatter(x, y)
Out[96]: <matplotlib.collections.PathCollection at 0x2b4d4747198>
In [97]: fig3.show()
為了模擬散點(diǎn)圖,先通過(guò) np.arange(1, 101) 創(chuàng)建了 100 個(gè)數(shù)據(jù)值,接著基于此創(chuàng)建了 100 個(gè) Y 軸數(shù)據(jù),可以看到這里兩組數(shù)據(jù)值之間有相關(guān)關(guān)系,因?yàn)槭腔诰€性關(guān)系然后加上隨機(jī)數(shù)生成的,繪制的散點(diǎn)圖也證明了這點(diǎn)(圖形向上傾斜):

箱線圖
箱線圖可以看出數(shù)據(jù)的分散程度,異常值等信息,箱線圖根據(jù)一組數(shù)據(jù)的以下5個(gè)統(tǒng)計(jì)值進(jìn)行繪制:
- 最小值
- 第1四分位數(shù)
- 中位數(shù)
- 第3四分位數(shù)
- 最大值
其中四分位數(shù),是指將一組數(shù)據(jù)值按大小排序后分成四等分,每一部分包含 1/4 的數(shù)據(jù),這種劃分的分割點(diǎn)就是四分位數(shù)。其中第1部分和第2部分的分割點(diǎn)稱為第1分位數(shù)Q 1, 也被稱為第25百分位數(shù),第3部分和第4部分的分割點(diǎn)稱為第3四分位數(shù) Q 3,也被稱為第75百分位數(shù)。而第二部分和第三部分的分割點(diǎn)是第2四分?jǐn)?shù),也就是中位數(shù)。其中四分位距 IQRIQR 是指第三四分位數(shù)和第一分四分位數(shù)的差,也就是IQR=Q 3?Q 1。四分位距反映了中間 50% 數(shù)據(jù)的離散程度,數(shù)值越小代表數(shù)據(jù)越集中,越大代表數(shù)據(jù)越分散。
在 matplotlib 中可以使用 Axes.boxplot 方法繪制箱線圖:
In [98]: fig4 = plt.figure()
In [99]: ax4 = fig4.add_subplot(1, 1, 1)
In [100]: # 產(chǎn)生50個(gè)小于100的隨機(jī)數(shù)
In [101]: spread = np.random.rand(50) * 100
In [102]: # 產(chǎn)生25個(gè)值為50的數(shù)據(jù)
In [103]: center = np.ones(25) * 50
In [104]: # 異常值
In [105]: outlier_high = np.random.rand(10) * 100 +100
In [106]: outlier_low = np.random.rand(10) * -100
In [107]: data = np.concatenate((spread, center, outlier_high, outlier_low), 0)
In [108]: ax4.boxplot(data)
Out[108]:
{'boxes': [<matplotlib.lines.Line2D at 0x2b4d507eb70>],
'caps': [<matplotlib.lines.Line2D at 0x2b4d5087438>,
<matplotlib.lines.Line2D at 0x2b4d508e828>],
'fliers': [<matplotlib.lines.Line2D at 0x2b4d508ef28>],
'means': [],
'medians': [<matplotlib.lines.Line2D at 0x2b4d508eb00>],
'whiskers': [<matplotlib.lines.Line2D at 0x2b4d507ec18>,
<matplotlib.lines.Line2D at 0x2b4d5087668>]}
In [109]: fig4.show()
上面代碼中,我們特意創(chuàng)建data數(shù)據(jù),可以推斷出該數(shù)據(jù)的中位數(shù)是50,還有一些其他異常值,繪制圖形如下:

Pandas繪圖
Pandas高度整合了matplotlib,可以直接調(diào)用DataFrame對(duì)象的某些方法進(jìn)行繪圖,更加方便的時(shí)候DataFrame的標(biāo)簽信息會(huì)自動(dòng)繪制到圖形上。
直接在Series對(duì)象上繪制線型圖,代碼如下:

在DataFrame對(duì)象上繪制箱線圖:
In [117]: df = pd.DataFrame(np.random.rand(5, 4), columns=['A', 'B', 'C', 'D'])
In [118]: df.boxplot()
Out[118]: <matplotlib.axes._subplots.AxesSubplot at 0x2b4d8f7a4e0>
In [119]: plt.show()

其它繪圖工具
Seaborn 建立在 matplotlib 之上,提供了更高級(jí)更方便的使用方式。
通過(guò) Seaborn 可以很方面繪制出線各種常見(jiàn)關(guān)系的圖形,比如聚類(lèi)關(guān)系,線性關(guān)系,分布關(guān)系等:
In [120]: import numpy as np
In [121]: import matplotlib.pyplot as plt
In [122]: import seaborn as sns
In [123]: np.random.seed(sum(map(ord, "aesthetics")))
In [124]:
In [124]: def sinplot(flip=1):
...: x = np.linspace(0, 14, 100)
...: for i in range(1, 7):
...: plt.plot(x, np.sin(x + i * .5) * (7 - i) * flip)
...:
In [125]: sns.set()
In [126]: sinplot()
In [127]: plt.show()
如下圖:

Bokeh 是一個(gè)現(xiàn)代化的繪圖工具,底層使用 D3.js 進(jìn)行繪圖,也就是說(shuō) Bokeh 繪制的圖形是基于瀏覽器顯示的,這也非常符合當(dāng)今的技術(shù)潮流,而且繪制的圖形交互功能更加強(qiáng)大。
In [131]: import numpy as np
In [132]: from bokeh.layouts import gridplot
In [133]: from bokeh.plotting import figure, show, output_file
In [134]: # sin曲線
In [135]: x = np.linspace(0, 4*np.pi, 100)
In [136]: y = np.sin(x)
In [137]: p = figure(title="Shiyanlou Bokeh Example")
In [138]: p.circle(x, y, legend="sin(x)")
Out[138]: GlyphRenderer(id='9c2202a5-15c2-4282-b074-d3a964790ba7', ...)
In [139]: p.circle(x, 2*y, legend="2*sin(x)", color="orange")
Out[139]: GlyphRenderer(id='d3e000b9-c792-4321-a1a5-b51db3981841', ...)
In [140]: p.circle(x, 3*y, legend="3*sin(x)", color="green")
Out[140]: GlyphRenderer(id='ba52e19d-3238-463d-bfda-7dc9063fc070', ...)
In [141]: show(p)
