Matplotlib是一個Python 2D繪相簿,能夠以多種硬拷貝格式和跨平臺的互動式環境生成出版物質量的圖形,用來繪製各種靜態,動態,互動式的圖表
matplotlib的影象都是畫在對應的figure上,可以認為是一個繪圖區域。而一個figure又可以包含一個或者多個axes,可以認為是子區域,這個子區域可以指定屬於自己的座標系。下面通過簡單的範例進行展示:
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
fig, ax = plt.subplots() # 該函數建立一個包含1個axes的figure,並將兩者進行返回
ax.plot([1,2,3,4],[1,4,2,3])
那麼也可以用更為簡單的方式來進行建立:
line = plt.plot([1,2,3,4],[1,4,2,3])
這是因為如果未指定axes,那麼會自動建立一個,因此可以簡化。
通常,一個完成的matplotlib影象會包括四個層級(容器):
matplotlib提供了兩種最常用的繪圖介面:
就像是上小節所展示的那樣兩種建立圖的方法。
Datawhale提供了一個通常的繪圖模板,可以根據實際需要對該模板進行修改了補充:
# 先準備好資料
x = np.linspace(0, 2, 100)
y = x**2
# 設定繪圖樣式(非必須)
mpl.rc('lines', linewidth=4, linestyle='-.')
# 定義佈局
fig, ax = plt.subplots()
# 繪製影象
ax.plot(x, y, label='linear')
# 新增標籤,文字和圖例
ax.set_xlabel('x label')
ax.set_ylabel('y label')
ax.set_title("Simple Plot")
ax.legend() ;
思考題
請思考兩種繪圖模式的優缺點和各自適合的使用場景
在第五節繪圖模板中我們是以OO模式作為例子展示的,請思考並寫一個pyplot繪圖模式的簡單模板
plt.plot(x,y,label='linear')
plt.xlabel("x label")
plt.ylabel("y label")
plt.title("simple plot")
plt.legend()
先準備待會兒要用到的庫
import numpy as np
import pandas as pd
import re
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from matplotlib.patches import Circle, Wedge
from matplotlib.collections import PatchCollection
matplotlib的原理或者說基礎邏輯是,用Artist物件在畫布(canvas)上繪製(Render)圖形。因此跟人作畫類似,需要三個步驟:
因此可以認為matplotlib有三層的API:
matplotlib.backend_bases.FigureCanvas
代表了繪圖區,所有的影象都是在繪圖區完成的matplotlib.backend_bases.Renderer
代表了渲染器,可以近似理解為畫筆,控制如何在 FigureCanvas 上畫圖。matplotlib.artist.Artist
代表了具體的圖表元件,即呼叫了Renderer的介面在Canvas上作圖。因此我們大部分是利用Artist類來進行繪圖。
Artist有兩種型別:primitives
和containers
:
primitives主要有以下幾種型別,我們按照順序介紹。
其中常見的引數主要有:
對於上面提到的各個引數有三種修改方法:
在plot函數裡面進行設定
x = range(0,5)
y = [2,5,7,9,11]
plt.plot(x,y,linewidth = 10)
獲取線物件,對線物件進行設定
x = range(0,5)
y = [2,5,7,8,10]
line, = plt.plot(x, y, '-') # 這裡等號座標的line,是一個列表解包的操作,目的是獲取plt.plot返回列表中的Line2D物件,返回是一個列表型別
line.set_antialiased(False); # 關閉抗鋸齒功能,呼叫線物件的函數
獲取線屬性,使用setp函數設定
x = range(0,5)
y = [2,5,7,8,10]
lines = plt.plot(x, y)
plt.setp(lines, color='r', linewidth=10);
那我們常見的功能是繪製直線line,以及繪製errorbar誤差折線圖,下面對這兩種分別進行介紹。
可以採用兩種方法來繪製直線:
1、plot方法
x = range(0,5)
y1 = [2,5,7,8,10]
y2= [3,6,8,9,11]
fig,ax= plt.subplots()
ax.plot(x,y1)
ax.plot(x,y2)
print(ax.lines);
列印為:
<Axes.ArtistList of 2 lines>
可以看到建立了2個lines物件。
2、Line2D物件繪製
x = range(0,5)
y1 = [2,5,7,8,10]
y2= [3,6,8,9,11]
fig,ax= plt.subplots()
lines = [Line2D(x, y1), Line2D(x, y2,color='orange')] # 顯式建立Line2D物件,但是現在還沒有在哪裡展示
for line in lines:
ax.add_line(line) # 使用add_line方法將建立的Line2D新增到子圖中,才會展示
ax.set_xlim(0,4)
ax.set_ylim(2, 11);
是利用pyplot中的errorbar類來實現,其引數為:
那麼具體的繪製方法就是將plot更改為errorbar即可:
fig = plt.figure()
x = np.arange(10)
y = 2.5 * np.sin(x / 20 * np.pi)
yerr = np.linspace(0.05, 0.2, 10)
plt.errorbar(x,y+3,yerr=yerr,fmt='o-',ecolor='r',elinewidth=2);
這個類是二維圖形類,它最常見的可以用來繪製矩形、多邊形、楔形。
Rectangle矩形類比較簡單,主要是通過xy來控制錨點,然後控制矩形的高寬即可。
最常見的矩形圖是hist直方圖和bar條形圖
其函數為plt.hist(),那麼引數為:
x=np.random.randint(0,100,100) #生成[0-100)之間的100個資料,即 資料集
bins=np.arange(0,101,10) #設定連續的邊界值,即直方圖的分佈區間[0,10),[10,20)...
fig = plt.figure(figsize = (6,12))
plt.subplot(311)
plt.hist(x,bins,color='fuchsia',alpha=0.5, density = True, histtype="step",
align = "left")#alpha設定透明度,0為完全透明
plt.xlabel('scores')
plt.ylabel('count')
plt.xlim(0,100); #設定x軸分佈範圍 plt.show()
plt.subplot(312)
plt.hist(x,bins,color='fuchsia',alpha=0.5, density = True, histtype="step",
align = "mid")
plt.subplot(313)
plt.hist(x,bins,color='fuchsia',alpha=0.5, density = True, histtype="step",
align = "right")
這裡對比了一下引數align的區別:
同樣,也是採用plt.bar()函數,其引數為:
y = range(1,17)
plt.bar(np.arange(16), y, alpha=0.5, width=0.5, color='yellow', edgecolor='red', label='The First Bar', lw=2);
# lw是柱形描邊的線寬度
Polygon類是多邊形類,其引數主要是繪製的多邊形的頂點座標。
那麼這個類中最常用的是fill類,它是基於頂點座標繪製一個填充的多邊形,例如:
x = np.linspace(0, 5 * np.pi, 1000)
y1 = np.sin(x)
y2 = np.sin(2 * x)
plt.fill(x, y1, color = "g", alpha = 0.3);
一個楔型是以座標xy為中心,半徑r,從角度1掃到角度2。最常用是繪製餅狀圖plt.pie()
其引數為:
labels = ['Frogs', 'Hogs', 'Dogs', 'Logs']
sizes = [15, 30, 45, 10]
explode = (0, 0.1, 0, 0)
fig1, ax1 = plt.subplots()
ax1.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%', shadow=True, startangle=90)
ax1.axis('equal'); # 設定axes為等高寬比,這樣才能夠確保畫出來為圓形
這個類是用來繪製一組物件的集合,那麼最常見的是用來繪製散點圖,即scatter方法,根據xy繪製不同大小或者顏色標記的散點圖。
其主要的引數如下:
x = [0,2,4,6,8,10]
y = [10]*len(x)
s = [20*2**n for n in range(len(x))]
plt.scatter(x,y,s=s) ;
這是繪製影象的類,最常用的imshow可以根據陣列繪製成影象(數值是各個畫素值)。
使用imshow畫圖時首先需要傳入一個陣列,陣列對應的是空間內的畫素位置和畫素點的值,interpolation引數可以設定不同的差值方法,可以理解為不同畫素之間的處理手段:
methods = [None, 'none', 'nearest', 'bilinear', 'bicubic', 'spline16',
'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric',
'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos']
grid = np.random.rand(4, 4)
fig, axs = plt.subplots(nrows=3, ncols=6, figsize=(9, 6),
subplot_kw={'xticks': [], 'yticks': []})
for ax, interp_method in zip(axs.flat, methods):
ax.imshow(grid, interpolation=interp_method, cmap='viridis')
ax.set_title(str(interp_method))
plt.tight_layout() # 自動調整子圖使其填充整個影象
前面我們介紹的primitives基礎元素,是包含在容器裡面的,當然容器還會包含它自身的屬性。
figure是最頂層的一個容器,它包含了圖中的所有元素,而一個圖表的背景可以認為就是在figure中新增的一個矩形。
當我們向圖表中新增add_subplot或者add_axes時,這些元素會被新增到figure.axes列表中:
fig = plt.figure()
ax1 = fig.add_subplot(211) # 作一幅2*1的圖,選擇第1個子圖
ax2 = fig.add_axes([0.1, 0.1, 0.7, 0.3]) # 再新增一個子圖位置引數,四個數分別代表了(left,bottom,width,height)
ax3 = fig.add_axes([0.2,0.1,0.3,0.4]) # 新增第三個子圖
print(ax1)
print(fig.axes) # fig.axes 中包含了subplot和axes兩個範例, 剛剛新增的
可以看到如果新增的子圖位置重疊的可能存在的情況。而輸出結果為:
AxesSubplot(0.125,0.53;0.775x0.35)
[<AxesSubplot:>, <Axes:>, <Axes:>]
figure.axes的列表中當前有三個元素,代表三個子圖。
而我們可以通過figure.delaxes()來刪除其中的圖表,或者可以通過迭代存取列表中的元素獲取子圖表,再在其上做修改:
fig = plt.figure()
ax1 = fig.add_subplot(211) # 作一幅2*1的圖,選擇第1個子圖
ax2 = fig.add_axes([0.1, 0.1, 0.7, 0.3]) # 再新增一個子圖位置引數,四個數分別代表了(left,bottom,width,height)
ax3 = fig.add_axes([0.2,0.1,0.3,0.4])
print(ax1)
print(fig.axes) # fig.axes 中包含了subplot和axes兩個範例, 剛剛新增的
for ax in fig.axes:
ax.grid(True)
Axes
是matplotlib的核心。大量的用於繪圖的Artist
存放在它內部,並且它有許多輔助方法來建立和新增Artist
給它自己,而且它也有許多賦值方法來存取和修改這些Artist
。
和figure類似,axes包含一個patch屬性,這個可以認為就是它的繪圖區域:
fig = plt.figure()
ax = fig.add_subplot(111)
rect = ax.patch # 獲取範例
rect.set_facecolor("blue")
Axes
有許多方法用於繪圖,如.plot()、.text()、.hist()、.imshow()
等方法用於建立大多數常見的primitive
(如Line2D,Rectangle,Text,Image
等等)。
可以在任意區域建立Axes,通過Figure.add_axes([left,bottom,width,height])來建立一個任意區域的Axes,其中left,bottom,width,height都是[0—1]之間的浮點數,他們代表了相對於Figure的座標。
而我們往axes裡面新增圖表是通過add_line和add_patch來進行新增。
另外Axes還包含兩個最重要的Artist container:
ax.xaxis
:XAxis物件的範例,用於處理x軸tick以及label的繪製ax.yaxis
:YAxis物件的範例,用於處理y軸tick以及label的繪製該容器用來處理跟座標軸相關的屬性,它包括座標軸上的刻度線、刻度label、座標網格、座標軸標題等,而且可以獨立對上下左右四個座標軸進行處理。
可以通過下面的方法獲取座標軸的各個屬性範例:
fig, ax = plt.subplots()
x = range(0,5)
y = [2,5,7,8,10]
plt.plot(x, y, '-')
axis = ax.xaxis # axis為X軸物件
axis.get_ticklocs() # 獲取刻度線位置
axis.get_ticklabels() # 獲取刻度label列表(一個Text範例的列表)
axis.get_ticklines() # 獲取刻度線列表(一個Line2D範例的列表)
axis.get_data_interval()# 獲取軸刻度間隔
axis.get_view_interval()# 獲取軸視角(位置)的間隔
也可以對獲取的屬性進行修改,例如:
fig = plt.figure() # 建立一個新圖表
rect = fig.patch # 矩形範例並將其設為黃色
rect.set_facecolor('lightgoldenrodyellow')
ax1 = fig.add_axes([0.1, 0.3, 0.4, 0.4]) # 創一個axes物件,從(0.1,0.3)的位置開始,寬和高都為0.4,
rect = ax1.patch # ax1的矩形設為灰色
rect.set_facecolor('lightslategray')
for label in ax1.xaxis.get_ticklabels():
# 呼叫x軸刻度標籤範例,是一個text範例
label.set_color('blue') # 顏色
label.set_rotation(45) # 旋轉角度
label.set_fontsize(14) # 字型大小
for line in ax1.yaxis.get_ticklines():
# 呼叫y軸刻度線條範例, 是一個Line2D範例
line.set_markeredgecolor('green') # 顏色
line.set_markersize(25) # marker大小
line.set_markeredgewidth(2)# marker粗細
它是axis下方的一個容器物件,包含了tick、grid、line範例以及對應的label。我們可以存取它的屬性來獲取這些範例:
Tick.tick1line
:Line2D範例Tick.tick2line
:Line2D範例Tick.gridline
:Line2D範例Tick.label1
:Text範例Tick.label2
:Text範例y軸分為左右兩個,因此tick1對應左側的軸;tick2對應右側的軸。
x軸分為上下兩個,因此tick1對應下側的軸;tick2對應上側的軸。
例如我們做如下修改:
fig, ax = plt.subplots()
ax.plot(100*np.random.rand(20))
ax.yaxis.set_tick_params(which='major', labelcolor='blue',
labelleft=False, labelright=True);
將主軸設在右邊且修改其顏色。
思考題
import pandas as pd
df = pd.read_csv("Drugs.csv")
df.head(5)
new_df = df.groupby(["YYYY","State"]).sum()
new_df
data = new_df.reset_index().pivot(index='YYYY', columns='State', values='DrugReports')
data
data = data.reset_index()
data
因此就可以開始繪圖了:
fig,ax = plt.subplots(figsize = (12,12))
ax.grid(True, color='white')
rect = ax.patch
rect.set_facecolor('#efefef')
ax.plot(data["YYYY"], data["KY"],color='#afafaf')
ax.plot(data["YYYY"], data["OH"],color='#afafaf')
ax.plot(data["YYYY"], data["PA"],color='yellow',linewidth='8')
ax.plot(data["YYYY"], data["VA"],color='#afafaf')
ax.plot(data["YYYY"], data["WV"],color='#afafaf')
ax.set_title('Evolution of PA vs other states', color='yellow', loc='left')
ax.set_xlabel('Year')
ax.set_ylabel('DrugReports')
import numpy as np
x = np.linspace(0,10)
y = -1 * (x - 2) * (x - 8) + 10
fig,ax = plt.subplots(2,1,figsize = (8,12))
x_bar = np.linspace(2,9)
y_bar = -1 * (x_bar - 2) * (x_bar - 8) + 10
y_bar_button = y_bar * 0
ax[0].plot(x,y,color="red")
ax[1].plot(x,y,color="red")
ax[0].bar(x_bar, y_bar,width=0.1, color='lightgray')
ax[1].bar(x_bar, y_bar, width = 0.1, color='lightgray')
ax[0].set_ylim((0,20))
ax[1].set_ylim((0,20))
ax[1].fill_between(x_bar, y_bar, y_bar_button, color="lightgray")
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] #用來正常顯示中文標籤
plt.rcParams['axes.unicode_minus'] = False #用來正常顯示負號
該函數的返回分別是畫布和子圖構成的列表,傳入的引數為行、列、第幾個子圖,figsize用來指定畫布的大小,sharex和sharey用來表示是否共用橫軸和縱軸刻度,tight_layout用來調整子圖的相對大小使字元不重疊:
fig, axs = plt.subplots(2,5, figsize = (10,4), sharex = True, sharey = True)
fig.suptitle("樣例1",size = 20)
for i in range(2):
for j in range(5):
axs[i][j].scatter(np.random.randn(10), np.random.randn(10))
axs[i][j].set_title('第%d行,第%d列'%(i+1,j+1))
axs[i][j].set_xlim(-5,5)
axs[i][j].set_ylim(-5,5)
if i==1: axs[i][j].set_xlabel('橫座標')
if j==0: axs[i][j].set_ylabel('縱座標')
fig.tight_layout()
前面是利用subplots(注意加了s)顯式的建立多個物件,然後一一進行畫圖;我們還可以通過plt和subplot(注意沒加s),每次在指定位置建立子圖,建立後當前的繪製都會指向該子圖:
plt.figure()
# 子圖1
plt.subplot(2,2,1)
plt.plot([1,2], 'r')
# 子圖2
plt.subplot(2,2,2)
plt.plot([1,2], 'b')
#子圖3
plt.subplot(224) # 當三位數都小於10時,可以省略中間的逗號,這行命令等價於plt.subplot(2,2,4)
plt.plot([1,2], 'g');
除了常規的直角座標系,還可以用projection方法建立極座標系下的圖表:
N = 300
r = 2 * np.random.rand(N)
theta = 2 * np.pi * np.random.rand(N)
area = 50 * r**2
colors = theta
plt.subplot(projection='polar')
plt.scatter(theta, r, c=colors, s=area, cmap='hsv', alpha=0.75);
練一練
請思考如何用極座標系畫出類似的玫瑰圖
fig = plt.figure(figsize = (8,12))
ax = plt.subplot(projection = "polar")
x = np.arange(100,1000, 20) # 間隔為20
y = np.linspace(0,np.pi*2, len(x))
ax.set_theta_direction(-1) # 設定極座標的方向為順時針,1為逆時針
ax.set_theta_zero_location('N') # 設定開始畫的方位,有8個方位
ax.bar(y, x, width = 0.15,color=np.random.random((len(r), 3)))
plt.tight_layout()
主要就是set_theta_direction和set_theta_zero_location兩個函數調整影象。
所謂非均勻包含兩層含義,第一是指圖的比例大小不同但沒有跨行或跨列,第二是指圖為跨列或跨行狀態
利用 add_gridspec
可以指定相對寬度比例 width_ratios
和相對高度比例引數 height_ratios
fig = plt.figure(figsize=(10, 4))
spec = fig.add_gridspec(nrows=2, ncols=5, width_ratios=[1,2,3,4,5], height_ratios=[1,3])
fig.suptitle('樣例2', size=20)
for i in range(2):
for j in range(5):
ax = fig.add_subplot(spec[i, j]) # 注意此處的呼叫方式
ax.scatter(np.random.randn(10), np.random.randn(10))
ax.set_title('第%d行,第%d列'%(i+1,j+1))
if i==1: ax.set_xlabel('橫座標')
if j==0: ax.set_ylabel('縱座標')
fig.tight_layout()
上述建立子圖時用到了spec[i,j]的方法,說明它是一個可索引的列表,那麼同樣也可以對其採用切片:
fig = plt.figure(figsize=(10, 4))
spec = fig.add_gridspec(nrows=2, ncols=6, width_ratios=[2,2.5,3,1,1.5,2], height_ratios=[1,2])
fig.suptitle('樣例3', size=20)
# sub1
ax = fig.add_subplot(spec[0, :3]) # 高度取第一個,寬度前三個都要了,就是1,7.5
ax.scatter(np.random.randn(10), np.random.randn(10))
# sub2
ax = fig.add_subplot(spec[0, 3:5]) # 1,1+1.5
ax.scatter(np.random.randn(10), np.random.randn(10))
# sub3
ax = fig.add_subplot(spec[:, 5])
ax.scatter(np.random.randn(10), np.random.randn(10))
# sub4
ax = fig.add_subplot(spec[1, 0])
ax.scatter(np.random.randn(10), np.random.randn(10))
# sub5
ax = fig.add_subplot(spec[1, 1:5])
ax.scatter(np.random.randn(10), np.random.randn(10))
fig.tight_layout()
補充一些子圖上的常用方法。
常用來畫直線的方法為axhline, axvline, axline
(水平、垂直、任意方向)
fig, ax = plt.subplots(figsize=(4,3))
ax.axhline(0.5,0.1,0.8, color = 'red')
# 第一個引數為水平y等於多少,第二個為xmin,第三個為xmax,都是浮點數代表座標軸佔百分比
ax.axvline(0.5,0.2,0.8, color = "blue")
ax.axline([0.3,0.3],[0.7,0.7], color = "green");
利用grid可以新增灰色網格:
fig, ax = plt.subplots(figsize=(4,3))
ax.grid(True)
使用set_xscale或者set_yscale可以設定座標軸的刻度:
fig, axs = plt.subplots(1, 2, figsize=(10, 4))
for j in range(2):
axs[j].plot(list('abcd'), [10**i for i in range(4)])
if j==0:
axs[j].set_yscale('log')
else:
pass
fig.tight_layout()
思考題
data = pd.read_csv("layout_ex1.csv")
data["Time"] = pd.to_datetime(data["Time"])
data["year_num"] = data["Time"].apply(lambda x: x.year)
fig, ax = plt.subplots(2, 5, figsize = (20,4))
fig.suptitle('墨爾本1981年至1990年月溫度曲線',size=20,y=1.1)
for i in range(2):
for j in range(5):
tem = data[data["year_num"] == j+1981+i*5]["Temperature"]
x = np.arange(0,12)
ax[i][j].plot(x,tem,marker = "o",color='b')
ax[i][j].set_title(str(j+1981 + i*5 ) + "年")
if( j == 0):
ax[i][j].set_ylabel("氣溫")
plt.tight_layout()
np.random.randn(2, 150)
生成一組二維資料,使用兩種非均勻子圖的分割方法,做出該資料對應的散點圖和邊際分佈圖data = np.random.randn(2,150)
fig = plt.figure(figsize = (12,12))
spec = fig.add_gridspec(nrows = 2, ncols = 2,width_ratios = [3,1],height_ratios=[1,3])
ax = fig.add_subplot(spec[0,0])
ax.hist(data[0,:],color = "blue",width = 0.4)
ax.axis("off")
ax2 = fig.add_subplot(spec[1,1])
ax2.hist(data[1,:], orientation='horizontal',color = "blue",rwidth = 0.8)
# 第二個引數設定為在y上面
ax2.axis("off")
ax3 = fig.add_subplot(spec[1,0])
ax3.scatter(data[0,:],data[1,:],color = "blue")
ax3.grid(True)
ax3.set_ylabel("my_data_y")
ax3.set_xlabel("my_data_x")
plt.tight_layout()
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as mdates
import datetime
下面這些命令是通過pyplot API和ooAPI分別建立文字的方式:
fig = plt.figure()
ax = fig.add_subplot()
# 設定x和y軸標籤
ax.set_xlabel('xlabel')
ax.set_ylabel('ylabel')
# 設定x和y軸顯示範圍均為0到10
ax.axis([0, 10, 0, 10])
ax.text(3, 8, 'boxed italics text in data coords', style='italic',
bbox={'facecolor': 'red', 'alpha': 0.5, 'pad': 10})
# 在畫布上新增文字,一般在子圖上新增文字是更常見的操作,這種方法很少用
fig.text(0.4,0.8,'This is text for figure')
ax.plot([2], [1], 'o')
# 新增註解
ax.annotate('annotate', xy=(2, 1), xytext=(3, 4),
arrowprops=dict(facecolor='black', shrink=0.05));
其呼叫方法為axes.text()。那麼其引數為:
重點解釋下fontdict和**kwargs引數,這兩種方式都可以用於調整呈現的文字樣式,最終效果是一樣的,不僅text方法,其他文字方法如set_xlabel,set_title等同樣適用這兩種方式修改樣式。通過一個例子演示這兩種方法是如何使用的。
fig = plt.figure(figsize = (10,3))
axes = fig.subplots(1,2)
axes[0].text(0.3,0.8, "modift by **kwargs", style="italic",
bbox = {"facecolor":"red", "alpha":0.5, "pad": 10})
font = {"bbox": {"facecolor":"red", "alpha":0.5, "pad": 10},
"style":"italic"}
axes[1].text(0.3,0.8, "modify by fontdict", fontdict = font)
那麼這些樣式常用的引數如下:
其呼叫方法為axes.set_xlabel和axes.set_ylabel
其引數為:
在**kwargs中有另外的引數可以調整標籤的位置等資訊,下面來觀察他們的區別:
fig = plt.figure(figsize=(10,3))
axes = fig.subplots(1,2)
axes[0].set_xlabel('xlabel',labelpad=20,loc='left')
# loc引數僅能提供粗略的位置調整,如果想要更精確的設定標籤的位置,可以使用position引數+horizontalalignment引數來定位
# position由一個元組過程,第一個元素0.2表示x軸標籤在x軸的位置,第二個元素對於xlabel其實是無意義的,隨便填一個數都可以
# horizontalalignment='left'表示左對齊,這樣設定後x軸標籤就能精確定位在x=0.2的位置處
axes[1].set_xlabel('xlabel', position=(0.2, _), horizontalalignment='left');
title呼叫方法為axes.set_title(),其引數為:
suptitle的呼叫為figure.suptitle()。
下面檢視pad和y的影響:
fig = plt.figure(figsize=(10,3))
fig.suptitle('This is figure title',y=1.2) # 通過引數y設定高度
axes = fig.subplots(1,2)
axes[0].set_title('This is title,pad = 15',pad=15)
axes[1].set_title('This is title,pad = 6',pad=6);
fig = plt.figure(figsize=(10,3))
fig.suptitle('This is figure title2',y=1)
axes = fig.subplots(1,2)
axes[0].set_title('This is title,y = 1',y = 1)
axes[1].set_title('This is title,y = 1.2',y = 1.2);
可以看到兩者其實就是控制標題與圖的距離而已。
呼叫方式為axes.annotate(),其引數為:
其引數特別多樣化,這裡只是舉個例子:
fig = plt.figure()
ax = fig.add_subplot()
ax.annotate("annotate1",
xy=(0.2, 0.2), xycoords='data',
xytext=(0.8, 0.8), textcoords='data',
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=0.2")
);
字型設定一般有全域性字型設定和自定義區域性字型設定兩種方法。
為了方便在圖中加入合適的字型,可以嘗試瞭解中文字型的英文名稱,此連結中就有常用的中文字型的英文名
#該block講述如何在matplotlib裡面,修改字型預設屬性,完成全域性字型的更改。
plt.rcParams['font.sans-serif'] = ['SimSun'] # 指定預設字型為新宋體。
plt.rcParams['axes.unicode_minus'] = False # 解決儲存影象時 負號'-' 顯示為方塊和報錯的問題。
#區域性字型的修改方法1
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
plt.plot(x, label='小范例圖示籤')
# 直接用字型的名字
plt.xlabel('x 軸名稱引數', fontproperties='Microsoft YaHei', fontsize=16) # 設定x軸名稱,採用微軟雅黑字型
plt.ylabel('y 軸名稱引數', fontproperties='Microsoft YaHei', fontsize=14) # 設定Y軸名稱
plt.title('座標系的標題', fontproperties='Microsoft YaHei', fontsize=20) # 設定座標系標題的字型
plt.legend(loc='lower right', prop={"family": 'Microsoft YaHei'}, fontsize=10) ; # 小范例圖的字型設定
設定tick(刻度)和ticklabel(刻度標籤)也是視覺化中經常需要操作的步驟,matplotlib既提供了自動生成刻度和刻度標籤的模式(預設狀態),同時也提供了許多靈活設定的方式。
直接使用axis.set_ticks設定標籤位置,使用axis.set_ticklabels設定標籤格式:
x1 = np.linspace(0.0, 5.0, 100)
y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True)
axs[0].plot(x1, y1)
axs[1].plot(x1, y1)
axs[1].xaxis.set_ticks(np.arange(0., 10.1, 2.));
可以自動設定相對來說會好一點(上圖)
fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True)
axs[0].plot(x1, y1)
axs[1].plot(x1, y1)
ticks = np.arange(0., 8.1, 2.)
tickla = [f'{tick:1.2f}' for tick in ticks]
axs[1].xaxis.set_ticks(ticks)
axs[1].xaxis.set_ticklabels(tickla);
我們通常設定tick都是要與數值的範圍匹配, 然後再設定ticklabel為我們想要的型別,如下:
fig, axs = plt.subplots(2, 1, figsize=(6, 4), tight_layout=True)
x1 = np.linspace(0.0, 6.0, 100)
y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
axs[0].plot(x1, y1)
axs[0].set_xticks([0,1,2,3,4,5,6])
axs[1].plot(x1, y1)
axs[1].set_xticks([0,1,2,3,4,5,6])#要將x軸的刻度放在資料範圍中的哪些位置
axs[1].set_xticklabels(['zero','one', 'two', 'three', 'four', 'five','six'],#設定刻度對應的標籤
rotation=30, fontsize='small')#rotation選項設定x刻度標籤傾斜30度。
axs[1].xaxis.set_ticks_position('top')
#set_ticks_position()方法是用來設定刻度所在的位置,常用的引數有bottom、top、both、none
print(axs[1].xaxis.get_ticklines());
上方的例子就是位置在bottom,下方就是在top,both就是上下都有,none就是都沒有。
除了上述的簡單模式以外,還可以通過Axis.set_major_locator和Axis.set_minor_locator方法用來設定標籤的位置,Axis.set_major_formatter和Axis.set_minor_formatter方法用來設定標籤的格式。這種方式的好處是不用顯式地列舉出刻度值列表。
set_major_formatter和set_minor_formatter這兩個formatter格式命令可以接收字串格式(matplotlib.ticker.StrMethodFormatter)或函數引數(matplotlib.ticker.FuncFormatter)來設定刻度值的格式 。
這部分的內容比較推薦用到的時候再去查。
接受字串:
fig, axs = plt.subplots(2, 2, figsize=(12, 5), tight_layout=True)
for n, ax in enumerate(axs.flat):
ax.plot(x1*10., y1)
formatter = matplotlib.ticker.FormatStrFormatter('%1.1f')
axs[0, 1].xaxis.set_major_formatter(formatter)
formatter = matplotlib.ticker.FormatStrFormatter('-%1.1f')
axs[1, 0].xaxis.set_major_formatter(formatter)
formatter = matplotlib.ticker.FormatStrFormatter('%1.5f')
axs[1, 1].xaxis.set_major_formatter(formatter);
接受函數:
def formatoddticks(x, pos):
if x % 2:
return f'{x:1.2f}'
else:
return ''
fig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)
ax.plot(x1, y1)
ax.xaxis.set_major_formatter(formatoddticks);
這個實現更復雜的操作:
fig, axs = plt.subplots(2, 2, figsize=(8, 5), tight_layout=True)
for n, ax in enumerate(axs.flat):
ax.plot(x1*10., y1)
locator = matplotlib.ticker.AutoLocator()
axs[0, 0].xaxis.set_major_locator(locator)
locator = matplotlib.ticker.MaxNLocator(nbins=3)
axs[0, 1].xaxis.set_major_locator(locator)
locator = matplotlib.ticker.MultipleLocator(5)
axs[1, 0].xaxis.set_major_locator(locator)
locator = matplotlib.ticker.FixedLocator([0,7,14,21,28])
axs[1, 1].xaxis.set_major_locator(locator);
# 特殊的日期型locator和formatter
locator = mdates.DayLocator(bymonthday=[1,15,25])
formatter = mdates.DateFormatter('%b %d')
fig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)
base = datetime.datetime(2017, 1, 1, 0, 0, 1)
time = [base + datetime.timedelta(days=x) for x in range(len(x1))]
ax.plot(time, y1)
ax.tick_params(axis='x', rotation=70);
在學習legend之前需要先學習幾個術語:
以下圖為例,右側的方框中的共有兩個legend entry;兩個legend key,分別是一個藍色和一個黃色的legend key;兩個legend label,一個名為‘Line up’和一個名為‘Line Down’的legend label
圖例的繪製同樣有OO模式和pyplot模式兩種方式,寫法都是一樣的,使用legend()即可呼叫。
fig, ax = plt.subplots()
line_up, = ax.plot([1, 2, 3], label='Line 2')
line_down, = ax.plot([3, 2, 1], label='Line 1')
ax.legend(handles = [line_up, line_down], labels = ['Line Up', 'Line Down']);
fig, ax = plt.subplots()
line_up, = ax.plot([1, 2, 3], label='Line 2')
line_down, = ax.plot([3, 2, 1], label='Line 1')
ax.legend()
而設定圖例的位置,可以通過設定loc引數的值來設定,其有10個位置可以選擇,每個都有字串的形式和對應的數位形式:
Location String | Location Code |
---|---|
best | 0 |
upper right | 1 |
upper left | 2 |
lower left | 3 |
lower right | 4 |
right | 5 |
center left | 6 |
center right | 7 |
lower center | 8 |
upper center | 9 |
center | 10 |
fig,axes = plt.subplots(2,5,figsize=(15,5))
for i in range(2):
for j in range(5):
axes[i][j].plot([0.5],[0.5])
axes[i][j].legend(labels='a',loc=i*5+j) # 觀察loc引數傳入不同值時圖例的位置
fig.tight_layout()
還可以設定圖例的邊框和背景:
fig = plt.figure(figsize=(10,3))
axes = fig.subplots(1,3)
for i, ax in enumerate(axes):
ax.plot([1,2,3],label=f'ax {i}')
axes[0].legend(frameon=False) #去掉圖例邊框
axes[1].legend(edgecolor='blue') #設定圖例邊框顏色
axes[2].legend(facecolor='gray'); #設定圖例背景顏色,若無邊框,引數無效
也可以為圖例加上標題:
fig,ax =plt.subplots()
ax.plot([1,2,3],label='label')
ax.legend(title='legend title');
思考題
嘗試使用兩種方式模仿畫出下面的圖表(重點是柱狀圖上的標籤),本文學習的text方法和matplotlib自帶的柱狀圖示籤方法bar_label
第一種:
label = ["Jim","Slim","Harry","Dick","Tom"]
y = [4,7,6,8,10]
error = np.random.rand(len(y)).round(2) #誤差
fig,ax = plt.subplots()
ax.set_title("How fast do you want to go today?")
ax.set_xlim(0,15)
for i in range(0, len(y)):
ax.text(y[i] + error[i]+1, label[i], '±' + str(error[i]), fontsize=10,horizontalalignment='center',color='blue')
ax.set_xlabel('performance')
ax.barh(label, y, color = 'blue',xerr = error)
# barh有一個引數為xerr就是來畫誤差線的
label = ["Jim","Slim","Harry","Dick","Tom"]
y = [4,7,6,8,10]
error = np.random.rand(len(y)).round(2) #誤差
fig,ax = plt.subplots()
ax.set_title("How fast do you want to go today?")
ax.set_xlim(0,15)
ax.set_xlabel('performance')
b = ax.barh(label, y, color = 'blue',xerr = error)
plt.bar_label(b, ["±"+str(i) for i in error])
第五回詳細介紹matplotlib中樣式和顏色的使用
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
設定樣式最簡單就是在繪製每一個元素時在引數中設定對應的樣式,不過也可以用方法來批次修改全域性的樣式。
只需要在python腳步最開始時輸入想使用的style的名稱就可以呼叫,那麼我們可以檢視有哪些方式方便使用:
print(plt.style.available)
['Solarize_Light2', '_classic_test_patch', '_mpl-gallery', '_mpl-gallery-nogrid', 'bmh', 'classic', 'dark_background', 'fast', 'fivethirtyeight', 'ggplot', 'grayscale', 'seaborn', 'seaborn-bright', 'seaborn-colorblind', 'seaborn-dark', 'seaborn-dark-palette', 'seaborn-darkgrid', 'seaborn-deep', 'seaborn-muted', 'seaborn-notebook', 'seaborn-paper', 'seaborn-pastel', 'seaborn-poster', 'seaborn-talk', 'seaborn-ticks', 'seaborn-white', 'seaborn-whitegrid', 'tableau-colorblind10']
那麼使用方法例如:
plt.style.use('ggplot')
plt.plot([1,2,3,4],[2,3,4,5]);
在任意路徑下建立一個字尾名為mplstyle的樣式清單,編輯檔案新增以下樣式內容:
axes.titlesize : 24
axes.labelsize : 20
lines.linewidth : 3
lines.markersize : 10
xtick.labelsize : 16
ytick.labelsize : 16
參照自定義stylesheet後觀察圖表變化:
plt.style.use('style1.mplstyle')
plt.plot([1,2,3,4],[2,3,4,5]);
值得特別注意的是,matplotlib支援混合樣式的參照,只需在參照時輸入一個樣式列表,若是幾個樣式中涉及到同一個引數,右邊的樣式表會覆蓋左邊的值:
plt.style.use(['dark_background', 'style1.mplstyle'])
plt.plot([1,2,3,4],[2,3,4,5]);
還可以通過修改預設rc設定的方式改變樣式,所有rc設定都儲存在一個叫做 matplotlib.rcParams的變數中。修改過後再繪圖,可以看到繪圖樣式發生了變化。
plt.style.use('default') # 恢復到預設樣式
mpl.rcParams['lines.linewidth'] = 2
mpl.rcParams['lines.linestyle'] = '--'
plt.plot([1,2,3,4],[2,3,4,5]);
另外matplotlib也還提供了一種更便捷的修改樣式方式,可以一次性修改多個樣式。
mpl.rc('lines', linewidth=4, linestyle='-.')
在matplotlib中,設定顏色有以下幾種方式
plt.plot([1,2,3],[4,5,6],color=(0.1, 0.2, 0.5))
plt.plot([4,5,6],[1,2,3],color=(0.1, 0.2, 0.5, 0.5));
顏色用[0,1]之間的浮點數表示,四個分量按順序分別為(red, green, blue, alpha),其中alpha透明度可省略。
# 用十六進位制顏色碼錶示,同樣最後兩位表示透明度,可省略
plt.plot([1,2,3],[4,5,6],color='#0f0f0f')
plt.plot([4,5,6],[1,2,3],color='#0f0f0f80');
# 當只有一個位於[0,1]的值時,表示灰度色階
plt.plot([1,2,3],[4,5,6],color='0.5');
八個基本顏色可以用單個字元來表示,分別是'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w',對應的是blue, green, red, cyan, magenta, yellow, black, and white的英文縮寫,設定color='m'即可。
matplotlib提供了顏色對照表,可供查詢顏色對應的名稱
具體可以閱讀這篇文章。
x = np.random.randn(50)
y = np.random.randn(50)
plt.scatter(x,y,c=x,cmap='RdYlBu');