matplotlib做圖中常用的輔助函式
在這篇文章裡,我將對我在使用matplotlib繪圖中編寫的輔助函式做一個階段性的總結,如果日後寫了新的函式,我再進一步更新。
為了節省我們在繪圖時的時間,我一般會在開始做圖時,在jupyter notebook中做一些初始化設定,包括匯入必須要的庫,如numpy,matplotlib,seaborn等;設定圖形的大小、風格等。你可以根據自己的需求對這部分配置進行修改,作為自己的初始化配置,下次繪圖時可以直接使用。我的初始化配置如下:
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import warnings
warnings. filterwarnings("ignore")
%matplotlib inline
import pandas as pd
import seaborn as sns
sns.set(style="white",color_codes=True)
plt.rcParams['figure.figsize'] = (15,9.27)
# Set the font set of the latex code to computer modern
matplotlib.rcParams['mathtext.fontset'] = "cm"
一、繪圖時使用latex
matplotlib支援在繪圖時使用latex程式碼,這樣便可以更好的支援在圖形中加入數學公式。使用latex程式碼的方式也很簡單,只需要在相應的字串兩邊各加上一個$
$y=e^x$
就可以顯示為。但是每次都要輸入$
號比較麻煩,我編寫了一個輔助函式可以自動為字串兩端加上$
,這樣只需求呼叫函式,正常輸入latex程式碼或者普通文字,就可以用latex公式的形式呈現了。如下:
def latex(s=''):
n = len(s.split(' '))
if n == 1:
if s == '':
return '$'+'\ '+'$'
else:
return '$'+s+'$'
else:
return '$' +'\ '.join(s.split(' '))+'$'
二、繪製單個函式影象
在科學計算中,經常會碰到的一個情形是需要繪製函式影象,函式影象可以讓我們更加直觀地瞭解到我們關心的函式的性質,比如單調性或者凹凸性等,這為我們之後進一步分析函式性質提供了指導。在matplotlib中為了繪製函式影象,首先需求指定一個自變數的一個取值區間,然後需要呼叫numpy中的linspace
函式在區間內產生一系列離散點。使用numpy.arange
函式也可以產生類似的效果,只需要指定超始值、結束值與步長,如果步長越短,則取的離散點越多,則函式會越平滑。
在呼叫函式時,我們只需要指定想要繪製的函式(通常可以lambda
表示式去定義),繪製函式的取值區間。其它設定包括X軸,Y軸與圖片標題,字型大小以及線寬,採用預設設定或者自己修改均可。
def fplot(func,beg,end,lw=2,xlab='x',ylab='y',title='',fs=30):
x = np.arange(beg,end+0.001,0.001)
y = np.array(list(map(func,x)))
plt.plot(x,y,linewidth=lw)
plt.xlim(beg,end)
plt.xlabel(latex(xlab),fontsize=fs)
plt.ylabel(latex(ylab),fontsize=fs)
plt.title(latex(title),fontsize=fs)
注意fplot
函式呼叫了之前定義的latex
函式,現在我們試試用它來繪製一個函式:
from math import exp,sin
f = lambda x: exp(sin(x))
fplot(f,-10,10,title='y=e^{sin(x)}')
可以看到通過一行簡單的程式碼就可以繪製出想要的函式圖形,繪製效果也較為滿意。你還可以在fplot
函式之後再加上一些matplotlib中的定製命令,如X與Y軸的取值範圍等,從而可以進一步地優化圖形。
三、繪製多個函式影象
有時候我們也需要在同一個圖形中繪製多個函式影象,一個個的去繪製當然比較麻煩,所以我們也可能編寫一個同時繪製多個函式影象的函式。為了更好的區分多個函式,除了不同函式曲線使用不同顏色外,我們也可以使用不同的線型,從而可以看得更清楚,哪怕在黑白列印時也不會影響識別的效果。除此之外,我們還可以為每個函式加上標籤,這樣才能看出不同顏色與線型分別對應那個函式。
def fplots(funcList,beg,end,legendList,loc=2,legendsize=20,lw=2):
x = np.arange(beg,end+0.001,0.001)
style = ['solid','dashed','dashdot','dotted']
styleList = style[:len(funcList)]
ldlist = ['$'+x+'$' for x in legendList]
for func,label,linestyle in zip(funcList,ldlist,styleList):
y = [func(i) for i in x]
plt.plot(x,y,linewidth=lw,label=label,linestyle=linestyle)
plt.legend(loc=loc,frameon=True,fontsize=legendsize)
fplots
函式的引數有七個,前四個是必要引數,後三個是可選引數,分別表示的意思如下:
- funcList:需要繪製的函式的列表;
- beg:繪製函式的左端點
- end: 繪製函式的右端點
- legendList: 函式標籤的列表,順序應與函式列表的順序一致
- loc: 標籤顯示的位置,預設為2,即左上角;1,3,4分別代表右上角、左下角與右下角
- legendsize: 標籤中文字的大小,預設為20
- lw: 線形的寬度,預設為2
下面,我們看一個示例:
from math import sqrt
f = lambda x:x**2
g = lambda x:sqrt(x)
h = lambda x:x
fplots([f,g,h],0,1,['y=x^2','y=\sqrt{x}','y=x'],lw=3)
plt.xlim(0,1)
plt.ylim(0,1)
plt.xlabel(latex('x'),fontsize=30)
plt.ylabel(latex('y'),fontsize=30)
plt.title(latex('y=x^2 & y=\sqrt{x} & y=x'),fontsize=30)
從以上示例我們可以看到,我們可以繪製函式後再加上其它matplotlib中其它的繪圖命令來對函式圖形進行修改調整,比如新增標籤,定義X與Y軸的取值範圍等。
四、為圖形新增平滑
在使用matplotlib繪圖時有時會碰到影象不夠平滑的問題,這雖然不影響繪製的準確性,但有時顯得不夠美觀。因此,我們不影響繪製準確性的的情況下,為圖形新增一定的平滑。我本以為matplotlib庫中應該有這個選項,正如excel繪圖時可以新增平滑性一樣,但是怎麼找都沒有找到。經過搜尋網路才知道有很多人也問了類似的問題,但matplotlib庫中確實沒有這項功能,不過可以通過自己編寫函式來較為容易的實現,實現的方法參考了stackoverflow上的這個回答。
實現的原理大致是這樣的,使用三次樣條插值,它的基本思想是,在由兩相鄰節點所構成的每一個小區間內用低次多項式來逼近,並且在各結點的連線處又保證是光滑的(即導數連續)。scipy
庫有樣條插值的相關函式,直接呼叫即可,不需要自己去實現,具體見以下程式碼:
def smooth_plot(x,y,area=False,color='b',ls='solid',lw=1):
from scipy.interpolate import spline
x_np,y_np = np.array(x),np.array(y)
xnew = np.linspace(x_np.min(),x_np.max(),300)
y_smooth = spline(x_np,y,xnew)
if area == True:
plt.fill_between(xnew,y_smooth,color=color)
else:
plt.plot(xnew,y_smooth,color=color,linestyle=ls,linewidth=lw)
函式包含六個引數,其中兩個必選引數,四個可選引數,含義分別如下:
- x: 自變數;
- y: 因變數;
- area: 是否繪製為面積圖,預設為False,即不繪製;
- color: 函式影象的顏色,預設為藍色;
- ls: 線型,預設為實線;
- lw:線寬,預設為1
下面我們看一下示例,分別繪製沒有新增平滑和新增平滑後的圖形:
np.random.seed(1234)
x = np.arange(1,21)
y = np.random.randint(1,10,20)
plt.subplot(311)
plt.plot(x,y,linewidth=2)
plt.xlim(1,20)
plt.ylim(0,11)
plt.ylabel(latex('y'),fontsize=20)
plt.title(latex('Non-smooth Curve'),fontsize=20)
plt.subplot(312)
smooth_plot(x,y,lw=2)
plt.xlim(1,20)
plt.ylim(0,11)
plt.ylabel(latex('y'),fontsize=20)
plt.title(latex('Smooth Curve'),fontsize=20)
plt.subplot(313)
smooth_plot(x,y,area=True)
plt.xlim(1,20)
plt.ylim(0,11)
plt.xlabel(latex('x'),fontsize=20)
plt.ylabel(latex('y'),fontsize=20)
plt.title(latex('Smooth Curve with Area'),fontsize=20)
subplots_adjust(hspace=0.4)
顯然,第二張圖形和第一張圖形反映出的資料的趨勢或走向是一樣的,只是加了一些平滑性,所以看上去更加美觀一些。第三張圖形和第二張圖形是一致的,只是換成了面積圖,有時候可以通過這種方式變換一下風格。
五、結語
以上就是我現在主要用到的一些輔助函式,如果之後有新增,我還會繼續更新這篇文章。
函式作為一種抽象與封裝的方式,可以大大提高我們編寫程式碼的效率,提高程式的健壯性與可維護性,建議大家可以把日常工作中重複使用的一些功能編寫成函式,然後通過函式組合來實現更復雜的功能。雖然python並不是一個函式式語言,但這種函數語言程式設計的思想還是可以應用的,這篇短文就是一個簡單的示例。