關(guān)于python處理氣象數(shù)據(jù)過程中常遇到的關(guān)于時(shí)間處理的問題

雖然近些年來氣象數(shù)據(jù)基本都以netcdf (.nc)格式為主隧熙,但是不同機(jī)構(gòu)婉弹,不同模式生成的數(shù)據(jù)仍存在差異睬魂,比如說有的經(jīng)度坐標(biāo)是0°-360°,有的是-180°-180°镀赌,有的緯度坐標(biāo)是從南到北氯哮,有的緯度坐標(biāo)是從北到南,然而這些處理起來都還算很簡單的商佛。唯獨(dú)時(shí)間模塊喉钢,真的是讓人麻爪。尤其是在python中良姆,時(shí)間格式的數(shù)據(jù)有可能是string肠虽,有可能是datetime64,有可能是object等等玛追,每種都需要用不同的方法去調(diào)整税课,就算是同一種格式,也可能在處理的過程中遇到各種各樣的問題痊剖。那么我就把自己親身經(jīng)歷過的各種崩潰瞬間分享一下韩玩,提供的解決辦法不一定是最優(yōu)解,僅供參考陆馁,大家有更好的辦法也可以評論留言找颓。

一、多模式結(jié)果處理中的時(shí)間格式不統(tǒng)一

這是之前在處理CMIP6模式中遇到的問題叮贩,我一共是處理了24個(gè)CMIP6模式(SSP245击狮,以及historical兩個(gè)情景),里邊就有那么幾個(gè)模式獨(dú)立特行妇汗,跟別人格格不入帘不。大部分的模式很乖,很好處理杨箭,時(shí)間格式默認(rèn)就是datetime64格式的寞焙,比如以ACCESS-CM2模式的tas變量為例(僅利用CDO通過mergetime和線性插值預(yù)處理了原始文件):

import xarray as xr
f = xr.open_dataset('./ACCESS-CM2/tas/t2m.nc')
print(f)

文件信息如下:

<pre style="box-sizing: border-box; overflow: auto; font-family: monospace; font-size: 14px; display: block; padding: 1px 0px; margin: 0px; line-height: inherit; color: rgb(0, 0, 0); word-break: break-all; overflow-wrap: break-word; background-color: rgb(255, 255, 255); border: 0px; border-radius: 0px; white-space: pre-wrap; vertical-align: baseline; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><xarray.Dataset>
Dimensions:    (lat: 73, lon: 144, nb2: 2, time: 31390)
Coordinates:
  * lon        (lon) float64 0.0 2.5 5.0 7.5 10.0 ... 350.0 352.5 355.0 357.5
  * lat        (lat) float64 -90.0 -87.5 -85.0 -82.5 ... 82.5 85.0 87.5 90.0
  * time       (time) datetime64[ns] 2015-01-01T12:00:00 ... 2100-12-31T12:00:00
Dimensions without coordinates: nb2
Data variables:
    time_bnds  (time, nb2) datetime64[ns] ...
    tas        (time, lat, lon) float32 ...

那我們可以看到,其時(shí)間格式為datetime64[ns]互婿,這是最方便的格式捣郊,可以直接索引。比如說我需要選取2015年到2099年冬季的數(shù)據(jù)慈参,那么直接通過以下指令就可以實(shí)現(xiàn):

tas = f['tas'].loc[f.time.dt.month.isin([12,1,2])].loc['2015-12-01':'2100-02-28']
print(tas)

輸出信息如下:

<xarray.DataArray 'tas' (time: 7650, lat: 73, lon: 144)>
[80416800 values with dtype=float32]
Coordinates:
  * lon      (lon) float64 0.0 2.5 5.0 7.5 10.0 ... 350.0 352.5 355.0 357.5
  * lat      (lat) float64 -90.0 -87.5 -85.0 -82.5 -80.0 ... 82.5 85.0 87.5 90.0
  * time     (time) datetime64[ns] 2015-12-01T12:00:00 ... 2100-02-28T12:00:00
Attributes:
    standard_name:  air_temperature
    long_name:      Near-Surface Air Temperature
    units:          K
    comment:        near-surface (usually, 2 meter) air temperature
    cell_methods:   area: time: mean
    history:        2019-11-08T10:42:10Z altered by CMOR: Treated scalar dime...

那么呛牲,當(dāng)遇到另一種時(shí)間格式時(shí),就不能直接這樣索引了驮配,比如說接下來的這個(gè):

f = xr.open_dataset('/data/home/zenggang/yxy/CMIP6/SSP245/CanESM5/tas/t2m.nc')    
print(f)

其文件信息如下:

<xarray.Dataset>
Dimensions:    (lat: 73, lon: 144, nb2: 2, time: 31390)
Coordinates:
  * lon        (lon) float64 0.0 2.5 5.0 7.5 10.0 ... 350.0 352.5 355.0 357.5
  * lat        (lat) float64 -90.0 -87.5 -85.0 -82.5 ... 82.5 85.0 87.5 90.0
  * time       (time) object 2015-01-01 12:00:00 ... 2100-12-31 12:00:00
Dimensions without coordinates: nb2
Data variables:
    time_bnds  (time, nb2) object ...
    tas        (time, lat, lon) float32 ...

注意咯娘扩,這里是object着茸,那么我們還以之前的方法索引會發(fā)生什么呢?

tas = f['tas'].loc[f.time.dt.month.isin([12,1,2])].loc['2015-12-01':'2100-02-28']
image.png

哎琐旁?格式不支持涮阔,好氣哦!
不過Xarray庫提供了一個(gè)函數(shù)來進(jìn)行轉(zhuǎn)換灰殴,因此這個(gè)格式也還算友好敬特。

import xarray as xr
f = xr.open_dataset('/data/home/zenggang/yxy/CMIP6/SSP245/CanESM5/tas/t2m.nc')    
f= f.assign_coords(time = f.indexes['time'].to_datetimeindex())
tas = f['tas'].loc[f.time.dt.month.isin([12,1,2])].loc['2015-12-01':'2100-02-28']

后來,我又遇到了另一種情況牺陶,就是有的模式的時(shí)間是0時(shí)(比如說 1979-01-01 00:00:00)伟阔,而有的模式的時(shí)間是12時(shí)(比如說 1979-01-01 12:00:00),如果需要統(tǒng)一的話掰伸,只需要搭配datetime庫進(jìn)行調(diào)整即可皱炉,比如說將所有的12時(shí)變?yōu)?時(shí):

from datetime import datetime, timedelta
f = f.assign_coords(time = (f.indexes['time'].to_datetimeindex()- timedelta(hours=12)))        

利用assign_coords重新聲明坐標(biāo)時(shí),將所有的時(shí)間減去了12小時(shí)碱工。(利用assign_coords也可以重新聲明經(jīng)緯度坐標(biāo)等等)

還有一類模式有點(diǎn)反人類(不點(diǎn)名了)娃承,我目前也沒想到太好的辦法來處理,它所使用的不是標(biāo)準(zhǔn)日歷怕篷,他每個(gè)月都有30天,2月份也有30天酗昼,很離譜廊谓,這在xarray庫中無法被識別(轉(zhuǎn)換),簡單粗暴點(diǎn)就直接用CDO刪掉了每年的這一天麻削,或者只能將全年的數(shù)據(jù)索引出來蒸痹,通過reshape成(年,月呛哟,日叠荠,經(jīng)度,緯度)的方法通過下標(biāo)序號索引扫责。

二榛鼎、非標(biāo)準(zhǔn)日歷數(shù)據(jù)

這個(gè)是我處理CAM模式數(shù)據(jù)時(shí)遇到的問題(run了30年的敏感性試驗(yàn),生成的數(shù)據(jù)的時(shí)間是從0001-01-01到0032-01-01)鳖孤,后面處理時(shí)讓我瞬間崩潰了者娱,生成的數(shù)據(jù)是cftime庫中的時(shí)間格式,但是表面還是寫著object類型苏揣,通過前邊介紹的assign_coords的方法根本行不通黄鳍,報(bào)錯(cuò)理由是


image.png

深層的原因就不在這里解釋了,只提供一個(gè)解決的方法平匈,思路還是將時(shí)間轉(zhuǎn)為datetime64格式:

import xarray as xr
import pandas as pd
f = xr.open_dataset('./TS.nc')
time_tmp1 = f.indexes['time']
time_tmp2 = []
j = 0
for i in range(time_tmp1.shape[0]):
    if ((i+1)%365==0):
        j = j+1
    else:
        j = j 
    tmp = time_tmp1[i].replace(year = 2000+j)
    a = tmp.year
    b = tmp.month
    c = tmp.day
    time_tmp2.append(pd.to_datetime('{}/{}/{}'.format(a,b,c),format='%Y/%m/%d')) 
f = f.assign_coords(time = time_tmp2)

中間那一大段的循環(huán)的目的框沟,是將每個(gè)時(shí)間坐標(biāo)統(tǒng)統(tǒng)加上2000年藏古,使其從0001-01-01到0030-12-31變?yōu)?000-01-01到2032-01-01,這樣就又可以通過正常的時(shí)間索引方法進(jìn)行索引了忍燥。
該文件默認(rèn)格式的時(shí)間是cftime拧晕,其存在一個(gè)方法,.replace可以替換年灾前,月防症,日,然后通過.year, .month,.day獲取新的年月日哎甲,再通過pd.to_datetime函數(shù)變?yōu)閐atetime64格式的日期蔫敲,最后重新賦值給f即可。
原始f信息:

<xarray.Dataset>
Dimensions:    (lat: 96, lon: 144, nb2: 2, time: 11681)
Coordinates:
  * lon        (lon) float64 0.0 2.5 5.0 7.5 10.0 ... 350.0 352.5 355.0 357.5
  * lat        (lat) float64 -90.0 -88.11 -86.21 -84.32 ... 86.21 88.11 90.0
  * time       (time) object 0001-01-01 00:00:00 ... 0033-01-01 00:00:00
Dimensions without coordinates: nb2
Data variables:
    time_bnds  (time, nb2) object ...
    TREFHT     (time, lat, lon) float32 ...
    TS         (time, lat, lon) float32 ...
    TSMN       (time, lat, lon) float32 ...
    TSMX       (time, lat, lon) float32 ...

修改后f信息:

<xarray.Dataset>
Dimensions:    (lat: 96, lon: 144, nb2: 2, time: 11681)
Coordinates:
  * lon        (lon) float64 0.0 2.5 5.0 7.5 10.0 ... 350.0 352.5 355.0 357.5
  * lat        (lat) float64 -90.0 -88.11 -86.21 -84.32 ... 86.21 88.11 90.0
  * time       (time) datetime64[ns] 2000-01-01 2000-01-02 ... 2032-01-01
Dimensions without coordinates: nb2
Data variables:
    time_bnds  (time, nb2) object ...
    TREFHT     (time, lat, lon) float32 ...
    TS         (time, lat, lon) float32 ...
    TSMN       (time, lat, lon) float32 ...
    TSMX       (time, lat, lon) float32 ...

三炭玫、批量索引特定時(shí)間的數(shù)據(jù)

我主要的方向是極端天氣氣候事件奈嘿,因此嘗嘗需要批量索引特定時(shí)間的數(shù)據(jù),比如說先統(tǒng)計(jì)40年里發(fā)生的極端(高溫吞加,低溫)事件的日期裙犹,然后從40年的逐日位勢高度數(shù)據(jù)里提取出發(fā)生事件當(dāng)天的位勢高度異常,有沒有不寫一大長串的判斷循環(huán)就能實(shí)現(xiàn)的方法呢衔憨?

方法1:

在統(tǒng)計(jì)事件時(shí)叶圃,順便統(tǒng)計(jì)好每次事件發(fā)生時(shí)的時(shí)間坐標(biāo)索引序號(是這40年里的第幾天發(fā)生的)
然后通過:

z = np.array([z_all[i]  for i in number])

實(shí)現(xiàn)索引,其中践图,z為取出的發(fā)生極端事件當(dāng)天的位勢高度掺冠,z_all為這40年的逐日位勢高度,number是每次事件發(fā)生時(shí)的時(shí)間坐標(biāo)索引序號码党,比如說事件是這40年里的第3,6,8...天發(fā)生的德崭,那么number就是[2,5,7,...],這里z和z_all都是np.array
實(shí)際上這里的本質(zhì)還是通過循環(huán)解決的揖盘。

方法2:通過xarray直接索引特定時(shí)間

首先在統(tǒng)計(jì)事件時(shí)眉厨,統(tǒng)計(jì)好發(fā)生每次事件的時(shí)間,datetime64格式的兽狭!憾股,索引時(shí)通過

z = z_all.loc[date]

直接完成。
其中z和z_all是xr.DataArray格式椭符,date是統(tǒng)計(jì)好的時(shí)間列表荔燎。
并且,對于分析事件個(gè)例來說销钝,通常還會提取爆發(fā)前后的一段時(shí)間進(jìn)行逐日的分析有咨,那么通過如下方法實(shí)現(xiàn):

z_1day_before = z_all.loc[date - np.timedelta64(1,'D')]

這里以發(fā)生前一天的數(shù)據(jù)提取為例。D表示day

小結(jié)

看到這里大家應(yīng)該可以發(fā)現(xiàn)蒸健,能否用python處理好數(shù)據(jù)的關(guān)鍵在于編程者能否將不同的庫的方法和函數(shù)靈活的組合起來座享,單純依靠基礎(chǔ)語言或者單一的庫婉商,很難實(shí)現(xiàn)一些靈活的操作,也無法體現(xiàn)出py的方便和強(qiáng)大之處渣叛。有時(shí)候我看到曾經(jīng)寫過的一些代碼丈秩,都想不起來當(dāng)時(shí)是怎么能想到這樣處理的,因?yàn)殪`活就意味著多種解決方法淳衙。實(shí)在不行的話蘑秽,遇事不決就簡單粗暴的使用循環(huán)判斷吧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末箫攀,一起剝皮案震驚了整個(gè)濱河市肠牲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌靴跛,老刑警劉巖缀雳,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異梢睛,居然都是意外死亡肥印,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門绝葡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來深碱,“玉大人,你說我怎么就攤上這事藏畅∮。” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵墓赴,是天一觀的道長。 經(jīng)常有香客問我航瞭,道長诫硕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任刊侯,我火速辦了婚禮章办,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘滨彻。我一直安慰自己藕届,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布亭饵。 她就那樣靜靜地躺著休偶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辜羊。 梳的紋絲不亂的頭發(fā)上踏兜,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天词顾,我揣著相機(jī)與錄音,去河邊找鬼碱妆。 笑死肉盹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的疹尾。 我是一名探鬼主播上忍,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼纳本!你這毒婦竟也來了窍蓝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤饮醇,失蹤者是張志新(化名)和其女友劉穎它抱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體朴艰,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡观蓄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祠墅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侮穿。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖毁嗦,靈堂內(nèi)的尸體忽然破棺而出亲茅,到底是詐尸還是另有隱情,我是刑警寧澤狗准,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布克锣,位于F島的核電站,受9級特大地震影響腔长,放射性物質(zhì)發(fā)生泄漏袭祟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一捞附、第九天 我趴在偏房一處隱蔽的房頂上張望巾乳。 院中可真熱鬧,春花似錦鸟召、人聲如沸胆绊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽压状。三九已至,卻和暖如春槽片,著一層夾襖步出監(jiān)牢的瞬間何缓,已是汗流浹背肢础。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碌廓,地道東北人传轰。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像谷婆,于是被迫代替她去往敵國和親慨蛙。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容