Matplotlib 最重要的特性之一就是具有良好的操作系統兼容性和圖形顯示底層接口兼容性。雖然,近幾年Matplotlib 的界面與風格似乎有點跟不上時代,但仍然是數據可視化技術不可缺少的一環。
1. 最常用的基本知識
類似numpy和pandas,matplotlib也有自己約定俗成的引入方式:
In [20]: import matplotlib as mpl
In [22]: import matplotlib.pyplot as plt
通過plt.style來選擇圖形的繪圖風格。
In [23]: plt.style.use('classic')
Matplotlib包含有3種實驗環境:分別是腳本、IPython shell 和 IPython Notebook。如果使用腳本,為了顯示圖片就必須使用plt.show()語句,plt.show() 會啟動一個事件循環(eventloop),并找到所有當前可用的圖形對象,然后打開一個或多個交互式窗口顯示圖形。需要注意,一個 Python 會話(session)中只能使用一次 plt.show(),因此通常都把它放在腳本的最后。
在IPython中啟動 Matplotlib 模式就可以使用,使用語句%matplotlib,此后的任何 plt 命令都會自動打開一個圖形窗口,增加新的命令,圖形就會更新(圖形是交互式變化的)。對于這些變化,可以使用 plt.draw() 強制更新。不需要使用plt.show()了。
使用IPython Notebook與IPython類似:
%matplotlib notebook 會在 Notebook 中啟動交互式圖形。
%matplotlib inline 會在 Notebook 中啟動靜態圖形。(經常使用)
In [3]: %matplotlib
Using matplotlib backend: MacOSX
In [4]: x = np.linspace(0,10,100)
In [6]: fig = plt.figure()
In [7]: plt.plot(x,np.sin(x),'-')
Out[7]: [<matplotlib.lines.Line2D at 0x12371a490>]
In [8]: plt.plot(x,np.cos(x),'--')
Out[8]: [<matplotlib.lines.Line2D at 0x10f82e110>]
In [16]: fig.savefig('first_pic.png') #保存為PNG格式的文件
In [17]: fig.canvas.get_supported_filetypes()
Out[17]:
{u'eps': u'Encapsulated Postscript',
u'jpeg': u'Joint Photographic Experts Group',
u'jpg': u'Joint Photographic Experts Group',
u'pdf': u'Portable Document Format',
u'pgf': u'PGF code for LaTeX',
u'png': u'Portable Network Graphics',
u'ps': u'Postscript',
u'raw': u'Raw RGBA bitmap',
u'rgba': u'Raw RGBA bitmap',
u'svg': u'Scalable Vector Graphics',
u'svgz': u'Scalable Vector Graphics',
u'tif': u'Tagged Image File Format',
u'tiff': u'Tagged Image File Format'}
得到下邊的圖形:
Matplotlib 的一個優點是能夠將圖形保存為各種不同的數據格式。你可以用 savefig() 命令將圖形保存為文件。不同格式的文件是以文件后綴名來區分的,上邊的命令列出了matplotlib支持的所有格式名。
2. 兩種畫圖接口
Matplotlib 有一個容易讓人混淆的特性,就是它的兩種畫圖接口:
(1) 便捷的 MATLAB 風格接口。
(2)功能更強大的面向對象接口。
2.1. MATLAB風格
MATLAB 風格的工具位于 pyplot(plt)接口中,如下邊一個例子:
x = np.linspace(0,10,100)
fig = plt.figure()
plt.subplot(2,1,1) # 行、列、編號 (圖形矩陣)
plt.plot(x,np.sin(x),'-')
plt.subplot(2,1,2)
plt.plot(x,np.cos(x),'--')
這種接口最重要的特性是有狀態的(stateful)。可以用plt.gcf()(獲取當前圖形)和 plt.gca()(獲取當前坐標軸)來查看。 這種圖畫起圖來又快又方便,但是也很容易出問題。例如,在畫第2個圖時,已經無法修改第1個圖了。
2.2. 面向對象風格
面向對象接口可以處理更加復雜的場景,畫圖函數不再受到當前“活動”圖形或坐標軸的限制,而變成了顯式的 Figure 和 Axes 的方法。
fig,ax = plt.subplots(2)
ax[0].plot(x,np.sin(x))
ax[1].plot(x,np.cos(x))
3. 線形圖
要畫 Matplotlib 圖形時,都需要先創建一個圖形 fig 和一個坐標軸ax。
最簡單地,通過下邊方法創建這2個對象:
fig = plt.figure()
ax = plt.axes()
在 Matplotlib 里面,figure(plt.Figure 類的一個實例)可以被看成是一個能夠容納各種坐標軸、圖形、文字和標簽的容器。axes(plt.Axes 類的一個實例)是一個帶有刻度和標簽的矩形,最終會包含所有可視化的圖形元素。
fig = plt.figure()
ax = plt.axes()
ax.plot(x,np.sin(x),color='blue',linestyle = '--',label='sin(x)')
ax.plot(x,np.sin(x-1),color='olive',linestyle ='-.',label='sin(x-1)')
ax.plot(x,np.sin(x-2),'-g',label='sin(x-2)') #線條風格+顏色簡寫形式
ax.set(xlim=(-2,11),ylim=(-2,2),xlabel='x',ylabel='y',title='A simple plot')
ax.legend() # 顯示圖例
如果需要在一個fig畫多個線,直接plot多次即可。ax對象可以通過set()方法來統一設置很多屬性。
matplotlib 可選的線條風格和顏色:
https://blog.csdn.net/detaswc/article/details/81086757
最后,通過一張表格來總結下本節介紹過的一些常用的屬性(更詳細可以參考官方文檔):
name | 說明 | 應用層級 |
---|---|---|
color | 配置線條顏色 | 曲線 |
linestyle | 線條風格(直線、虛線等) | 曲線 |
label | 曲線標簽(圖例) | 曲線 |
xlim | 圖形x軸取值范圍 | 整個圖表 |
ylim | 圖形y軸取值范圍 | 整個圖表 |
xlabel | 圖形x軸標簽名 | 整個圖表 |
ylabel | 圖形y軸標簽名 | 整個圖表 |
title | 圖形的標題 | 整個圖表 |
legend() | 生成圖例的方法 | 方法 |
4. 散點圖
散點圖(scatter plot)與線形圖類似,不再由線段連接,而是由獨立的點、圓圈或其他形狀構成。
4.1 使用plt.plot畫散點圖
第一種方式與上邊線形圖的構造方法十分類似,只修改下linestyle參數即可(之前是- 修改為o),執行效果如下:
plt.plot(x,np.sin(x),'o',color='black')
4.2 使用plt.scatter畫散點圖
plt.scatter 與 plt.plot 的主要差別在于,前者在創建散點圖時具有更高的靈活性,可以單獨控制每個散點與數據匹配,也可以讓每個散點具有不同的屬性(大小、表面顏色、邊框顏色等)。
當數據變大到幾千個散點時,plt.plot 的效率將大大高于plt.scatter。因此面對大型數據集時,plt.plot 方法比 plt.scatter 方法好。
下面以sklearn的鳶尾花數據為例制作一個散點圖:
['sepal length (cm)',
'sepal width (cm)',
'petal length (cm)',
'petal width (cm)']
了解特征數據的說明:
from sklearn.datasets import load_iris
iris = load_iris()
iris.keys()
iris.DESCR
iris.feature_names
作圖代碼:
from sklearn.datasets import load_iris
iris = load_iris()
features = iris.data.T
feature_names = iris.feature_names
fig = plt.plot()
ax = plt.axes()
ax.scatter(features[0],features[1],alpha=0.2,s=100*features[3], c=iris.target, cmap='viridis')
ax.set(xlabel=feature_names[0] , ylabel=feature_names[1],title='First Scatter Plot')
Scatter參數詳見:https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.scatter.html#matplotlib.axes.Axes.scatter
5. 誤差線圖
基本誤差線(errorbar)可以通過一個 Matplotlib 函數來創建。
其中yerr指定是在縱軸方向誤差,xerr是在橫軸方向誤差,當然可以兩方向同時誤差。
下面是個簡單的例子:
fig = plt.figure()
ax = plt.axes()
dy = 0.8
x_e = np.linspace(0,10,50)
y_e = np.sin(x_e) + dy * np.random.randn(50)
ax.errorbar(x_e,y_e,yerr=dy,fmt='o',color='black',ecolor='lightgray',
elinewidth=3,capsize=0)
errorbar參數詳見:https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.errorbar.html#matplotlib.axes.Axes.errorbar
6. 密度圖與等高線圖
在二維圖上用等高線圖或者彩色圖來表示三維數據是個不錯的方法。用 plt.contour 畫等高線圖、用 plt.contourf 畫帶有填充色的等高線圖(filled contourplot)的色彩、用 plt.imshow 顯示圖形。
等高線圖可以用 plt.contour 函數來創建。它需要三個參數:x 軸、y軸、z 軸三個坐標軸的網格數據。x 軸與 y 軸表示圖形中的位置,而 z 軸將通過等高線的等級來表示。
np.meshgrid 函數可以從一維數組構建二維網格數據。
理解np.meshgrid :
實際上就是 生成網格點坐標矩陣
我以一個簡單的例子展開說明:
t_ga = np.array([0,4,5,8,9])
t_gb = np.array([1,3,9,12])
XT,YT = np.meshgrid(t_ga,t_gb)
plt.plot(XT,YT,color='green',linestyle='',marker='o')
plt.show()
以上代碼得到圖形如下:
實際上,以提供的兩個數組,在X軸和Y軸做垂線,這些所有線的交點也成了所謂的網格矩陣了。
參考文章:
https://blog.csdn.net/lllxxq141592654/article/details/81532855
介紹完meshgrid概念后,看看最簡單的等高圖:
def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
x_f = np.linspace(0,5,50)
y_f = np.linspace(0,5,40)
X,Y = np.meshgrid(x_f,y_f)
Z = f(X,Y)
fig = plt.figure()
ax = plt.axes()
ax.contour(X,Y,Z,colors='black')
當圖形中只使用一種顏色時,默認使用虛線表示負數,使用實線表示正數。此圖確實比較難看,下邊提供了一系列改進方式。
改進1:
你可以用 cmap 參數設置一個線條配色方案來自定義顏色。下邊語句將數據范圍等分為 20 份,然后用不同的顏色表示。Matplotlib 有非常豐富的配色方案,你可以在IPython 里用 Tab 鍵瀏覽 plt.cm 模塊對應的信息。plt.cm.<TAB>
ax.contour(X,Y,Z,30,cmap='RdGy')
改進2:
上邊圖形的線條間的間隙很大,可以通過plt.contourf來改進,其語法與contour完全一致。另外,通過 plt.colorbar() 命令自動創建一個表示圖形各種顏色對應標簽信息的顏色條。
ax0 = ax.contourf(X,Y,Z,30,cmap='RdGy')
fig.colorbar(ax0,ax=ax)
改進3:
上邊的圖形的主要問題是顏色的改變是一個離散而非連續的過程,這導致了圖形看起來有點兒“污漬斑斑”。plt.imshow() 函數可以將二維數組渲染成漸變圖。
contours = plt.contour(X, Y, Z, 3, colors='black')
plt.clabel(contours, inline=True, fontsize=8)
plt.imshow(Z, extent=[0, 5, 0, 5], origin='lower',
cmap='RdGy', alpha=0.5)
plt.colorbar();
7. 頻次直方圖、數據區間劃分和分布密度
10.多子圖
當需要做對比分析時,有必要畫多個圖。
10.1 plt.axes()創建子圖
plt.axes()函數有4個參數:[bottom, left, width, height](底坐標<x軸>、左坐標<y軸>、寬度、高度),數值的取值范圍是左下角(原點)為 0,右上角為 1。以上參數為了生成“畫中畫”,幾個參數都是百分比。
ax_sub_3 = plt.axes()
ax_sub_4 = plt.axes([0.4,0.6,0.5,0.3])
plt.show()
面向對象畫圖接口中類似的命令是:fig.add_axes(),例如下邊的代碼可以生成上下兩個圖:
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.5, 0.8, 0.4],xticklabels=[],
ylim=(-1.2, 1.2)) # xticklabels=[],表示上邊的坐標軸無刻度。
ax2 = fig.add_axes([0.1, 0.1, 0.8, 0.4],ylim=(-1.2, 1.2))
x = np.linspace(0, 10)
ax1.plot(np.sin(x))
ax2.plot(np.cos(x))
10.2 plt.subplot()創建子圖
plt.subplot() 在一個網格中創建一個子圖。
plt.subplots_adjust 命令可以調整子圖之間的間隔。
使用MATLAB接口創建子圖使用:plt.subplot(2,3,i)
使用面向對象接口創建子圖使用:fig.add_subplot(2,3,i)
上邊例子中,subplot,前2個參數是子圖矩陣的行和列(2行3列子圖,共6個),最后一個i是子圖的編號(從1到6)。下邊是2個例子:
for i in range(1,7):
plt.subplot(2,3,i)
plt.text(0.5,0.5,str((2,3,i)),fontsize=18,ha='center')
plt.subplots_adjust(hspace=0.4,wspace=0.4) #間隙的高和寬分別是子圖的40%
fig = plt.figure()
fig.subplots_adjust(hspace=0.4,wspace=0.4)
for i in range(1,7):
axi=fig.add_subplot(2,3,i)
axi.text(0.5,0.5,str((2,3,i)),fontsize=16,ha='center')
10.3 plt.subplots()
本節介紹的方法跟上邊相比多個s,如果需要隱藏子圖的x軸和y軸,可以使用 plt.subplots()。該方法返回子圖的Numpy數組。
plt.subplots()的參數包括:子圖矩陣的行數和列數,可選參數sharex和sharey,參考下邊例子:
fig,ax = plt.subplots(3,2,sharex='col' , sharey='row') #列共享x軸(sharex),行共享y軸
for i in range(0,3):
for j in range(0,2):
ax[i][j].text(0.5,0.5,str((i,j)),fontsize=16,ha='center')
fig.subplots_adjust(hspace=0.4,wspace=0.4)
10.4 plt.GridSpec()實現更復雜的排列方式
上邊10.2和10.3節畫的都是規則的多行多列子圖網格,如果想畫不規則的,可以使用plt.GridSpec()。plt.GridSpec()并不是規則圖形,而是plt.subplot()的簡易接口。
例如,下邊的例子,生成了不規則的圖形:
grid = plt.GridSpec(3,3)
plt.subplot(grid[0,:2])
plt.subplot(grid[0,2])
plt.subplot(grid[1:,:2])
plt.subplot(grid[1:,2])
下面是plt.GridSpec()的一個應用,多軸頻次直方圖:
11.文字和注釋
在圖表中增加文字標簽,主要通過ax.text方法。
ax.text 方法需要一個 x 軸坐標、一個 y 軸坐標、一個字符串和一些可選參數,比如文字的顏色、字號、風格、對齊方式以及其他文字屬性。
下邊是一個例子:
births=pd.read_csv('python_science_handbook_data/births.csv')
quartiles = np.percentile(births['births'],[25,50,75]) #25,50,75分位數
mid,sig = quartiles[1],0.74*(quartiles[2]-quartiles[0])
# (mid - 5*sig,mid + 5*sig)
births = births.query('(births > @mid - 5*@sig) & (births < @mid + 5*@sig)')
births['day'] = births['day'].astype(int) #day列轉化為int
# births['unix_time'] = 10000*births.year + 100*births.month + births.day ##19690101
births.index = pd.to_datetime(10000*births.year + 100*births.month + births.day,format='%Y%m%d')
births_by_date = births.pivot_table('births',[births.index.month, births.index.day])
births_by_date.index = [pd.datetime(2012, month, day)for (month, day) in births_by_date.index]
fig, ax = plt.subplots(figsize=(12, 4))
# births_by_date.loc['2012-1-1':'2012-1-1']
births_by_date.plot(ax=ax);
# 在圖上增加文字標簽
style = dict(size=10, color='gray')
ax.text('2012-1-1', 3950, "New Year's Day", **style)
ax.text('2012-7-4', 4250, "Independence Day", ha='center', **style)
ax.text('2012-9-4', 4850, "Labor Day", ha='center', **style)
ax.text('2012-10-31', 4600, "Halloween", ha='right', **style)
ax.text('2012-11-25', 4450, "Thanksgiving", ha='center', **style)
ax.text('2012-12-25', 3850, "Christmas ", ha='right', **style)
ax.set(title='USA births by day of year (1969-1988)',ylabel='average daily births')
ax.xaxis.set_major_locator(mpl.dates.MonthLocator())
ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))
ax.xaxis.set_major_formatter(plt.NullFormatter())
ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter('%h'));