Python氣象數(shù)據(jù)處理與繪圖(17):加快循環(huán)的運(yùn)算速度

最近比較忙好幾天沒有更新,今天就更新一個簡單且實用的數(shù)據(jù)處理方法吧。
在使用python時大家會發(fā)現(xiàn)有時候程序運(yùn)行的異常緩慢。數(shù)據(jù)讀取環(huán)節(jié)是我們不能控制的,那么只有在數(shù)據(jù)處理環(huán)節(jié),我們才能盡可能的簡化它。讓python計算加快的關(guān)鍵是向量化,就是對數(shù)據(jù)進(jìn)行矩陣運(yùn)算,而不是循環(huán)運(yùn)算。
python語言本身就決定了它不能像C或者Fortran一樣實行動態(tài)運(yùn)算,而在氣象科研中又常常遇到大量的循環(huán)和邏輯判斷,這就讓程序的運(yùn)行異常緩慢,我提供兩個解決辦法,第一個就是前面提到的向量化,舉一個簡單的例子,這個例子很蠢,但是比較容易理解。

#向量運(yùn)算:
a = np.array([[1,2,3],[4,5,6],[7,8,9]])
b = np.array([[3,2,1],[6,5,4],[9,8,7]])
c = a + b

#非向量運(yùn)算:
a = np.array([[1,2,3],[4,5,6],[7,8,9]])
b = np.array([[3,2,1],[6,5,4],[9,8,7]])
c = np.zeros((3,3))
for i in range(3):
    for j in renge(3):
        c[i,j] = a[i,j] + b[i,j]

相信大家都不會用第二種去運(yùn)算,但是我這里只是一個簡單的例子,其實大家翻翻以前寫過的循環(huán),應(yīng)該都會發(fā)現(xiàn)自己使用過非向量運(yùn)算,最簡單的比如說計算相關(guān)系數(shù)時:

from scipy.stats import pearsonr
r = np.zeros((a.shape[1],a.shape[2]))
p = np.zeros((a.shape[1],a.shape[2]))

for i in range(a.shape[1]):
    for j in range(a.shape[2]):
            r[i,j], p[i,j] = pearsonr(b,a[:,i,j])

其實這里遠(yuǎn)沒有NCL的相關(guān)系數(shù)計算方便,但是我目前了解的python計算相關(guān)系數(shù)的函數(shù)均沒有指定維度的語句,也就是說它們都是求兩個序列的相關(guān)系數(shù),當(dāng)我們計算一個場與一個序列的相關(guān)系數(shù)時,只能通過循環(huán)實現(xiàn)。
那么,在這種情況下,就可以用到我要講的第二種方法,使用Numba加速!
關(guān)于Numba是什么的問題,大家感興趣可以百度深入了解,通俗地講,就是一個可以提高python遍歷運(yùn)算性能的庫。安裝方法:

conda install numba

使用numba很簡單,就是自己定義一個python函數(shù),然后添加一個裝飾器來提高性能。還是同樣的計算相關(guān)系數(shù)的程序:

import numba as nb
#為相關(guān)系數(shù)計算函數(shù)塊提供@jit裝飾器
@nb.jit()
def r_caculate(b,a):
    r = np.zeros((a.shape[1],a.shape[2]))
    p = np.zeros((a.shape[1],a.shape[2]))
    for i in range(a.shape[1]):
        for j in range(a.shape[2]):
            r[i,j], p[i,j] = pearsonr(b, a[:,i,j])
    return r,p

f_hadsic =  xr.open_dataset('HadISST_ice.nc')
sic_had = np.array(f_hadsic['sic'])                     
r,p = r_caculate(pc, sic_had)

運(yùn)算結(jié)果是4.39秒,而不適用numba加速時:

f_hadsic =  xr.open_dataset('HadISST_ice.nc')
sic_had = np.array(f_hadsic['sic'])                     
r,p = r_caculate(pc, sic_had)
r = np.zeros((sic_had.shape[1],sic_had.shape[2]))
p = np.zeros((sic_had.shape[1],sic_had.shape[2]))
for i in range(sic_had.shape[1]):
    for j in range(sic_had.shape[2]):
        r[i,j], p[i,j] = pearsonr(pc, sic_had[:,i,j])

計算花費了12.3秒??梢姴罹嗍志薮蟆?br> 當(dāng)數(shù)據(jù)的循環(huán)和邏輯判斷很大時,使用numba加速可以說會為我們節(jié)省大量的等待時間。
但是numba并不是萬能的,據(jù)我目前使用的結(jié)果,它只支持numpy,scipy的一部分函數(shù)。具體還需要大家在使用過程中逐漸嘗試。
再舉一個例子(這個函數(shù)實現(xiàn)是在一個(時間,緯度,經(jīng)度)數(shù)組中判斷任意時刻任意一個點是否大于周圍八個點的):

@nb.jit()
def max_in_8surrounds(a):
    b = np.zeros((a.shape[0],a.shape[1],a.shape[2]))
    for t in range(a.shape[0]):
            for i in range(1,a.shape[1]-1):
                for j in range(1,a.shape[2]-1):
                    tmp = np.array([a[t,i-1,j-1],a[t,i+1,j+1],a[t,i-1,j+1],
                                   a[t,i+1,j],a[t,i-1,j],a[t,i,j+1],a[t,i,j-1],a[t,i+1,j-1]])
                    if(a[t,i,j]>np.max(tmp)):
                        b[t,i,j] = 1
    return b
point = max_in_8surrounds(a)

我計算的數(shù)組十分巨大,加入了修飾器后,比未使用numba加速可以說快了不止幾十倍。其實這個程序還涉及了一個小技巧,就是我在判斷一個點是否小于周圍八個點時,我將周圍八個點先同時放進(jìn)了一個數(shù)組,然后用中心點去與數(shù)組的最大值去判斷大小,這樣遠(yuǎn)比進(jìn)行八次邏輯判斷的速度要快的多。

if ((a>a1) & (a>a2) & (a>a2) & (a>a2) & .................):

如果numba仍不能滿足你的需要,那還是去學(xué)C或者Fortran吧QAQ

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • python由于它動態(tài)解釋性語言的特性,跑起代碼來相比java、c++要慢很多,尤其在做科學(xué)計算的時候,十億百億級...
    朱衛(wèi)軍AI_Python閱讀 1,721評論 2 27
  • “想什么呢?這么出神?!辈恢朗裁磿r候,邊伯賢已經(jīng)洗完了澡,來到她的身邊坐下,他的頭發(fā)上還掛著水,帶著剛剛沐浴完的...
    霸道毅閱讀 186,637評論 1 9
  • “江湖有個小浪子, 嘗遍天下金波子。 要說這還差啥子, 獨缺貌美小娘子~”妖風(fēng)坐在一家名為味風(fēng)的酒肆里,一邊喝著杯...
    棠玥閱讀 1,288評論 9 10
  • 1.你以為放手可以成全我的幸福,可你不知道,我最大的幸福就是能和你手牽手。 2.快樂,不過是給傷口找一個笑著流淚的...
    duodortlol閱讀 205評論 0 0
  • 時光荏苒,歲月如梭,轉(zhuǎn)眼到了和孩子說再見的時候,思緒萬千,心里不覺悵然,當(dāng)看到孩子們那一張張稚嫩的臉,聽到那一聲聲...
    白窯小學(xué)尹曉麗閱讀 137評論 0 0

友情鏈接更多精彩內(nèi)容