1. 程式人生 > >matplotlib做圖中常用的輔助函式

matplotlib做圖中常用的輔助函式

matplotlib可能是Python語言中最常用的繪相簿了,使用它可以較為容易的做出印刷品質的專業圖形,此外,matplotlib的定製程度也很高,可以滿足各式各樣的繪圖要求,能夠限制你做圖能力只是你的想像力而已。然而,也正因為matplotlib的定製性,相關API較為底層,所以為了做出複雜的圖形,常常需要寫很多行程式碼,顯然不夠經濟。為了節省畫圖的時間,我們可以把日常工作中經常碰到的某些繪圖需求加以定製化抽象化,編寫一些輔助函式,這些函式將某些繪圖需求打包封裝了。這樣,下次我們繪圖裡只需要呼叫相關函式就可以很快的做出需要的圖形了,少寫很多程式碼,也更加符合DRY(Don’t Repeat Yourself)的原則。

在這篇文章裡,我將對我在使用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$就可以顯示為y=exy=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函式,現在我們試試用它來繪製一個函式y=esin(x)y=e^{sin(x)}:

from math import exp,sin

f = lambda x: exp(sin(x))
fplot(f,-10,10,title='y=e^{sin(x)}')

png

可以看到通過一行簡單的程式碼就可以繪製出想要的函式圖形,繪製效果也較為滿意。你還可以在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)

png

從以上示例我們可以看到,我們可以繪製函式後再加上其它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)

png

顯然,第二張圖形和第一張圖形反映出的資料的趨勢或走向是一樣的,只是加了一些平滑性,所以看上去更加美觀一些。第三張圖形和第二張圖形是一致的,只是換成了面積圖,有時候可以通過這種方式變換一下風格。

五、結語

以上就是我現在主要用到的一些輔助函式,如果之後有新增,我還會繼續更新這篇文章。

函式作為一種抽象與封裝的方式,可以大大提高我們編寫程式碼的效率,提高程式的健壯性與可維護性,建議大家可以把日常工作中重複使用的一些功能編寫成函式,然後通過函式組合來實現更復雜的功能。雖然python並不是一個函式式語言,但這種函數語言程式設計的思想還是可以應用的,這篇短文就是一個簡單的示例。