Matplotlib初相識 認識matplotlib Matplotlib是一個Python 2D繪圖庫,能夠以多種硬拷貝格式和跨平臺的互動式環境生成出版物質量的圖形,用來繪製各種靜態,動態,互動式的圖表 一個最簡單的繪圖例子 matplotlib的圖像都是畫在對應的figure上,可以認為是一個繪 ...
Matplotlib初相識
認識matplotlib
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,那麼會自動創建一個,因此可以簡化。
figure的組成
通常,一個完成的matplotlib圖像會包括四個層級(容器):
- Figure:頂級層,用來容納所有繪圖元素
- Axes:matplotlib宇宙的核心,容納了大量元素用來構造一幅幅的子圖,一個figure可以由1個或者多個子圖構成
- Axis:axes的下層,用來處理所有與坐標軸、網格相關的元素
- Tick:axis的下層,用來處理所有和刻度相關的元素
兩種繪圖介面
matplotlib提供了兩種最常用的繪圖介面:
- 創建figure和axes,然後在此之上調用繪圖方法
- 依賴pyplot自動創建figure和axes來繪圖
就像是上小節所展示的那樣兩種創建圖的方法。
通用繪圖模板
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() ;
思考題
-
請思考兩種繪圖模式的優缺點和各自適合的使用場景
- 我覺得先創建figure和axes再進行繪圖的方式更適用於你對圖的規劃比較清晰,或者你想要畫多個子圖,這樣在同一個figure上作畫會簡潔方便;而pyplot模型更實用於你當前只需要畫一個圖,那麼把所有元素都加到當前這個圖上就可以了
-
在第五節繪圖模板中我們是以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的三層api
matplotlib的原理或者說基礎邏輯是,用Artist對象在畫布(canvas)上繪製(Render)圖形。因此跟人作畫類似,需要三個步驟:
- 準備一個畫圖
- 準備畫筆、顏料
- 作畫
因此可以認為matplotlib有三層的API:
matplotlib.backend_bases.FigureCanvas
代表了繪圖區,所有的圖像都是在繪圖區完成的matplotlib.backend_bases.Renderer
代表了渲染器,可以近似理解為畫筆,控制如何在 FigureCanvas 上畫圖。matplotlib.artist.Artist
代表了具體的圖表組件,即調用了Renderer的介面在Canvas上作圖。
因此我們大部分是利用Artist類來進行繪圖。
Artist的分類
Artist有兩種類型:primitives
和containers
:
- primitive是基本要素,包含一些我們要在繪圖區作圖用到的標準圖形對象,例如曲線、文字、矩形等等。
- container是容器,可以認為是用來放置基本要素的地方,包括圖形figure,坐標系axes和坐標系axis
基本元素primitives
primitives主要有以下幾種類型,我們按照順序介紹。
2DLines
其中常見的參數主要有:
- xdata:橫坐標的取值,預設就是range(1,len(data)+1)
- ydata:縱坐標取值
- linewidth:線條的寬度
- linestyle:線型
- color:線條的顏色
- marker:點的標註樣式
- markersize:標註的大小
如何設置參數屬性
對於上面提到的各個參數有三種修改方法:
-
在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);
如何繪製lines
那我們常見的功能是繪製直線line,以及繪製errorbar誤差折線圖,下麵對這兩種分別進行介紹。
繪製line
可以採用兩種方法來繪製直線:
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);
繪製errorbar誤差折線圖
是利用pyplot中的errorbar類來實現,其參數為:
- x:橫坐標
- y:縱坐標
- yerr:指定在y軸水平的誤差
- xerr:指定在x軸水平的誤差
- fmt:指定折線圖中某個點的顏色、形狀、線條風格等
- ecolor:指定errorbar的顏色
- elinewidth:指定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);
patches
這個類是二維圖形類,它最常見的可以用來繪製矩形、多邊形、楔形。
矩形
Rectangle矩形類比較簡單,主要是通過xy來控制錨點,然後控制矩形的高寬即可。
最常見的矩形圖是hist直方圖和bar條形圖
hist-直方圖
其函數為plt.hist(),那麼參數為:
- x:數據集,直方圖將會對這個數據集進行統計
- bins:統計的區間分佈,我們可以指定區間進行統計,例如按照([0,10],[11,20])區間進行統計
- range:tuplt,顯示的區間
- density:是否顯示頻數統計結果
- histtype:可選{'bar', 'barstacked', 'step', 'stepfilled'}之一,預設為bar,step使用的是梯狀,stepfilled則會對梯狀內部進行填充,效果與bar類似
- align:可選{'left', 'mid', 'right'}之一,預設為'mid',控制柱狀圖的水平分佈,left或者right,會有部分空白區域,推薦使用預設
- log:y軸是否採用指數刻度
- stacked:是否為堆積狀圖
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的區別:
bar-柱狀圖
同樣,也是採用plt.bar()函數,其參數為:
- left:x軸的位置序列,一般採用range函數產生一個序列,但是有時候可以是字元串
- height:y軸的數值序列,也就是柱形圖的高度,一般就是我們需要展示的數據
- alpha:透明度,值越小越透明
- width:柱形的寬度
- color或者facecolor:柱形填充的顏色
- edgecolor:柱形邊緣顏色
- label:標簽
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()
其參數為:
- x:楔型的形狀,一維數組,可以看成是掃過角度的大小
- explode:如果不是None,那麼就是一個len(x)的數組,用來指定每塊的偏移
- labels:指定每個塊的標簽,列表或者none
- colors:指定每個塊的顏色,列表或者none
- startangle:餅狀圖開始繪製的角度
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為等高寬比,這樣才能夠確保畫出來為圓形
collections
這個類是用來繪製一組對象的集合,那麼最常見的是用來繪製散點圖,即scatter方法,根據xy繪製不同大小或者顏色標記的散點圖。
其主要的參數如下:
- x和y
- s:散點的尺寸大小
- c:顏色
- marker:標記類型
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) ;
image
這是繪製圖像的類,最常用的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() # 自動調整子圖使其填充整個圖像
對象容器-Object container
前面我們介紹的primitives基礎元素,是包含在容器裡面的,當然容器還會包含它自身的屬性。
Figure容器
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容器
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的繪製
Axis容器
該容器用來處理跟坐標軸相關的屬性,它包括坐標軸上的刻度線、刻度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粗細
Tick容器
它是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);
將主軸設在右邊且修改其顏色。
思考題
- primitives 和 container的區別和聯繫是什麼,分別用於控制可視化圖表中的哪些要
素- 【答】:我認為container是一個容器,而primitives 是基本元素,可以理解為container是包容primitives的,例如figure,axes,axis等作為一個容器,它們可以包含很多primitives 的基礎元素在其上面進行展示
- 使用提供的drug數據集,對第一列yyyy和第二列state分組求和,畫出下麵折線圖。PA加粗標黃,其他為灰色。
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')
- 分別用一組長方形柱和填充面積的方式模仿畫出下圖,函數 y = -1 * (x - 2) * (x - 8) +10 在區間[2,9]的積分面積
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 #用來正常顯示負號
子圖
使用plt.subplots()繪製均勻狀態下的子圖
該函數的返回分別是畫布和子圖構成的列表,傳入的參數為行、列、第幾個子圖,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兩個函數調整圖像。
使用GridSpec繪製非均勻子圖
所謂非均勻包含兩層含義,第一是指圖的比例大小不同但沒有跨行或跨列,第二是指圖為跨列或跨行狀態
利用 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()
思考題
- 墨爾本1981年至1990年的每月溫度情況
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
Figure和Axes上的文本
文本API示例
下麵這些命令是通過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));
text-子圖上的文本
其調用方法為axes.text()。那麼其參數為:
- x,y:文本出現的位置
- s:文本的內容
- fontdict:可選參數,用來調整文本的屬性
重點解釋下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)
那麼這些樣式常用的參數如下:
xlabel和ylabel
其調用方法為axes.set_xlabel和axes.set_ylabel
其參數為:
- xlabel:標簽內容
- fontdict和之前一樣
- **kwargs也和之前一樣
- labelpad:標簽和坐標軸之間的距離
- loc:標簽位置,可選為"left","center","right"
在**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和suptitle-子圖和畫布的標題
title調用方法為axes.set_title(),其參數為:
- label:標簽內容
- fontdict,loc,**kwargs和之前一樣
- pad:標題偏離圖表頂部的位置
- y: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);
可以看到兩者其實就是控制標題與圖的距離而已。
annotate-子圖的註解
調用方式為axes.annotate(),其參數為:
- text:註解的內容
- xy:註解箭頭指向的位置
- xytext:註解文字的坐標
- xycoords:用來定義xy參數的坐標系
- textcoords:用來定義xytext參數的坐標系
- arrowprops:用來定義指向箭頭的樣式
其參數特別多樣化,這裡只是舉個例子:
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上的文本
設置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就是都沒有。
Tick Lacators and Formatters
除了上述的簡單模式以外,還可以通過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)來設置刻度值的格式 。
這部分的內容比較推薦用到的時候再去查。
Tick Formatters
接受字元串:
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);
Tick Locators
這個實現更複雜的操作:
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之前需要先學習幾個術語:
- legend entry(圖例條目):每個圖例都有一個或者多個條目組成,一個條目包含一個key和對應的label,例如圖中三條曲線需要標註,那麼就是3個條目
- legend key(圖例鍵):每個legend label左邊的標記,指明是哪條曲線
- legend label(圖例標簽):描述文本
- legend handle(圖例句柄):用於在圖例中生成適當圖例條目的原始對象
以下圖為例,右側的方框中的共有兩個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
matplotlib的繪圖樣式(style)
設置樣式最簡單就是在繪製每一個元素時在參數中設定對應的樣式,不過也可以用方法來批量修改全局的樣式。
matplotlib預先定義樣式
只需要在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]);
用戶自定義stylesheet
在任意路徑下創建一個尾碼名為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]);
設置rcparams
還可以通過修改預設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的色彩設置color
在matplotlib中,設置顏色有以下幾種方式
RGB或者RGBA
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透明度可省略。
HEX RGB或者RGBA
# 用十六進位顏色碼表示,同樣最後兩位表示透明度,可省略
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提供了顏色對照表,可供查詢顏色對應的名稱
用colormap設置一組顏色
具體可以閱讀這篇文章。
x = np.random.randn(50)
y = np.random.randn(50)
plt.scatter(x,y,c=x,cmap='RdYlBu');