關于python處理氣象數據過程中常遇到的關于時間處理的問題

雖然近些年來氣象數據基本都以netcdf (.nc)格式為主,但是不同機構,不同模式生成的數據仍存在差異,比如說有的經度坐標是0°-360°,有的是-180°-180°,有的緯度坐標是從南到北,有的緯度坐標是從北到南,然而這些處理起來都還算很簡單的。唯獨時間模塊,真的是讓人麻爪。尤其是在python中,時間格式的數據有可能是string,有可能是datetime64,有可能是object等等,每種都需要用不同的方法去調整,就算是同一種格式,也可能在處理的過程中遇到各種各樣的問題。那么我就把自己親身經歷過的各種崩潰瞬間分享一下,提供的解決辦法不一定是最優解,僅供參考,大家有更好的辦法也可以評論留言。

一、多模式結果處理中的時間格式不統一

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

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 ...

那我們可以看到,其時間格式為datetime64[ns],這是最方便的格式,可以直接索引。比如說我需要選取2015年到2099年冬季的數據,那么直接通過以下指令就可以實現:

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...

那么,當遇到另一種時間格式時,就不能直接這樣索引了,比如說接下來的這個:

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,那么我們還以之前的方法索引會發生什么呢?

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

哎?格式不支持,好氣哦!
不過Xarray庫提供了一個函數來進行轉換,因此這個格式也還算友好。

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']

后來,我又遇到了另一種情況,就是有的模式的時間是0時(比如說 1979-01-01 00:00:00),而有的模式的時間是12時(比如說 1979-01-01 12:00:00),如果需要統一的話,只需要搭配datetime庫進行調整即可,比如說將所有的12時變為0時:

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

利用assign_coords重新聲明坐標時,將所有的時間減去了12小時。(利用assign_coords也可以重新聲明經緯度坐標等等)

還有一類模式有點反人類(不點名了),我目前也沒想到太好的辦法來處理,它所使用的不是標準日歷,他每個月都有30天,2月份也有30天,很離譜,這在xarray庫中無法被識別(轉換),簡單粗暴點就直接用CDO刪掉了每年的這一天,或者只能將全年的數據索引出來,通過reshape成(年,月,日,經度,緯度)的方法通過下標序號索引。

二、非標準日歷數據

這個是我處理CAM模式數據時遇到的問題(run了30年的敏感性試驗,生成的數據的時間是從0001-01-01到0032-01-01),后面處理時讓我瞬間崩潰了,生成的數據是cftime庫中的時間格式,但是表面還是寫著object類型,通過前邊介紹的assign_coords的方法根本行不通,報錯理由是


image.png

深層的原因就不在這里解釋了,只提供一個解決的方法,思路還是將時間轉為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)

中間那一大段的循環的目的,是將每個時間坐標統統加上2000年,使其從0001-01-01到0030-12-31變為2000-01-01到2032-01-01,這樣就又可以通過正常的時間索引方法進行索引了。
該文件默認格式的時間是cftime,其存在一個方法,.replace可以替換年,月,日,然后通過.year, .month,.day獲取新的年月日,再通過pd.to_datetime函數變為datetime64格式的日期,最后重新賦值給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 ...

三、批量索引特定時間的數據

我主要的方向是極端天氣氣候事件,因此嘗嘗需要批量索引特定時間的數據,比如說先統計40年里發生的極端(高溫,低溫)事件的日期,然后從40年的逐日位勢高度數據里提取出發生事件當天的位勢高度異常,有沒有不寫一大長串的判斷循環就能實現的方法呢?

方法1:

在統計事件時,順便統計好每次事件發生時的時間坐標索引序號(是這40年里的第幾天發生的)
然后通過:

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

實現索引,其中,z為取出的發生極端事件當天的位勢高度,z_all為這40年的逐日位勢高度,number是每次事件發生時的時間坐標索引序號,比如說事件是這40年里的第3,6,8...天發生的,那么number就是[2,5,7,...],這里z和z_all都是np.array
實際上這里的本質還是通過循環解決的。

方法2:通過xarray直接索引特定時間

首先在統計事件時,統計好發生每次事件的時間,datetime64格式的!,索引時通過

z = z_all.loc[date]

直接完成。
其中z和z_all是xr.DataArray格式,date是統計好的時間列表。
并且,對于分析事件個例來說,通常還會提取爆發前后的一段時間進行逐日的分析,那么通過如下方法實現:

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

這里以發生前一天的數據提取為例。D表示day

小結

看到這里大家應該可以發現,能否用python處理好數據的關鍵在于編程者能否將不同的庫的方法和函數靈活的組合起來,單純依靠基礎語言或者單一的庫,很難實現一些靈活的操作,也無法體現出py的方便和強大之處。有時候我看到曾經寫過的一些代碼,都想不起來當時是怎么能想到這樣處理的,因為靈活就意味著多種解決方法。實在不行的話,遇事不決就簡單粗暴的使用循環判斷吧!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容