4. 時間條件控制#

xarray時間條件控制#

在先前的範例中,已經示範如何用slice來選擇特定時間區間的資料,但若選擇的時間並不連續,用slice就不太方便。因此若能在時間加入一些條件控制,則可以只選擇滿足條件的資料。xarray轉譯出的時間屬於datetime物件,且存在年、月、日等時間單位的attribute,因此我們就可以針對它們進行條件控制。

Example 1: 選擇JAS(七至九月)季節的資料

首先準備資料:

import xarray as xr 

olr_ds = xr.open_dataset("data/olr.nc")
olr_da = olr_ds.olr
olr_jas = olr_da.sel(time=(olr_da.time.dt.month.isin([7,8,9]))) 
olr_jas
/Users/waynetsai/.local/lib/python3.10/site-packages/ecmwflibs/__init__.py:83: UserWarning: dlopen(/Users/waynetsai/.local/lib/python3.10/site-packages/ecmwflibs/_ecmwflibs.cpython-310-darwin.so, 0x0002): tried: '/Users/waynetsai/.local/lib/python3.10/site-packages/ecmwflibs/_ecmwflibs.cpython-310-darwin.so' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64')), '/System/Volumes/Preboot/Cryptexes/OS/Users/waynetsai/.local/lib/python3.10/site-packages/ecmwflibs/_ecmwflibs.cpython-310-darwin.so' (no such file), '/Users/waynetsai/.local/lib/python3.10/site-packages/ecmwflibs/_ecmwflibs.cpython-310-darwin.so' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64'))
  warnings.warn(str(e))
<xarray.DataArray 'olr' (time: 2208, lat: 90, lon: 360)>
[71539200 values with dtype=float32]
Coordinates:
  * time     (time) datetime64[ns] 1998-07-01 1998-07-02 ... 2021-09-30
  * lon      (lon) float32 0.5 1.5 2.5 3.5 4.5 ... 355.5 356.5 357.5 358.5 359.5
  * lat      (lat) float32 -44.5 -43.5 -42.5 -41.5 -40.5 ... 41.5 42.5 43.5 44.5
Attributes:
    standard_name:  toa_outgoing_longwave_flux
    long_name:      NOAA Climate Data Record of Daily Mean Upward Longwave Fl...
    units:          W m-2
    cell_methods:   time: mean area: mean

Example 2: 移除閏日 (leap day) 利用類似Example 1的方法,也可以只選取2/29之外的其他日期 (反向選擇),而達到移除閏日的目的。

olr_noleap = olr_da.sel(time=~((olr_da.time.dt.month == 2) & (olr_da.time.dt.day == 29)))  # ~() 代表反向選擇
olr_noleap
<xarray.DataArray 'olr' (time: 8760, lat: 90, lon: 360)>
[283824000 values with dtype=float32]
Coordinates:
  * time     (time) datetime64[ns] 1998-01-01 1998-01-02 ... 2021-12-31
  * lon      (lon) float32 0.5 1.5 2.5 3.5 4.5 ... 355.5 356.5 357.5 358.5 359.5
  * lat      (lat) float32 -44.5 -43.5 -42.5 -41.5 -40.5 ... 41.5 42.5 43.5 44.5
Attributes:
    standard_name:  toa_outgoing_longwave_flux
    long_name:      NOAA Climate Data Record of Daily Mean Upward Longwave Fl...
    units:          W m-2
    cell_methods:   time: mean area: mean

沒有2/29的資料。

pandas時間序列#

結合pandasdatetime,我們可以很輕易地製造datetime物件的時間序列。

import pandas as pd

# 直接將指定的時間轉換成datetime格式。
pd.to_datetime(["2000-01-01", "2000-02-02"])
DatetimeIndex(['2000-01-01', '2000-02-02'], dtype='datetime64[ns]', freq=None)
# 給定初始時間、長度,製造一串時間序列
ts = pd.date_range("2000-01-01", periods=365)
ts
DatetimeIndex(['2000-01-01', '2000-01-02', '2000-01-03', '2000-01-04',
               '2000-01-05', '2000-01-06', '2000-01-07', '2000-01-08',
               '2000-01-09', '2000-01-10',
               ...
               '2000-12-21', '2000-12-22', '2000-12-23', '2000-12-24',
               '2000-12-25', '2000-12-26', '2000-12-27', '2000-12-28',
               '2000-12-29', '2000-12-30'],
              dtype='datetime64[ns]', length=365, freq='D')

或是給定初始值、結束值、時間序列取樣頻率:

ts = pd.date_range(start='2000-01-01',end='2000-12-30',freq='1D')
ts
DatetimeIndex(['2000-01-01', '2000-01-02', '2000-01-03', '2000-01-04',
               '2000-01-05', '2000-01-06', '2000-01-07', '2000-01-08',
               '2000-01-09', '2000-01-10',
               ...
               '2000-12-21', '2000-12-22', '2000-12-23', '2000-12-24',
               '2000-12-25', '2000-12-26', '2000-12-27', '2000-12-28',
               '2000-12-29', '2000-12-30'],
              dtype='datetime64[ns]', length=365, freq='D')

目前這串時間序列的格式是YYYY-MM-DD,我們也可以利用strftime的函數,將時間改寫成任意格式。以下範例是改寫成’Jan 01 00’等,

ts.strftime("%b %d %y")
Index(['Jan 01 00', 'Jan 02 00', 'Jan 03 00', 'Jan 04 00', 'Jan 05 00',
       'Jan 06 00', 'Jan 07 00', 'Jan 08 00', 'Jan 09 00', 'Jan 10 00',
       ...
       'Dec 21 00', 'Dec 22 00', 'Dec 23 00', 'Dec 24 00', 'Dec 25 00',
       'Dec 26 00', 'Dec 27 00', 'Dec 28 00', 'Dec 29 00', 'Dec 30 00'],
      dtype='object', length=365)

其中,文字的月份以%b表示,日以%d表示,二位數的年份以%y表示,四位數的年份以%Y表示。更詳細的用法可以在 datetime套件官方網站 - strftime-strptime Behavior 中找到。

如果是改寫olrlt的時間格式,

olr_da.time.dt.strftime("%b %d %y")
<xarray.DataArray 'strftime' (time: 8760)>
array(['Jan 01 98', 'Jan 02 98', 'Jan 03 98', ..., 'Dec 29 21',
       'Dec 30 21', 'Dec 31 21'], dtype=object)
Coordinates:
  * time     (time) datetime64[ns] 1998-01-01 1998-01-02 ... 2021-12-31

以上的例子可以看到,xarray.DataArray.time.dt (稱為DatetimeAccessor) 的功能相當於pandas.DatetimeIndex,如此一來,之後繪製時間序列圖、Hovmöller diagrams,需要改變座標軸上的時間格式時,以上的方法就很實用了。

datetimetimedelta#

不管是Datetime Accessor,還是pandas.DatetimeIndex,都屬於datetime的物件格式。

datetime.datetime: A combination of a date and a time. Attributes: year, month, day, hour, minute, second, microsecond, and tzinfo. (datetime官方網站)

而datetime也可以進行算數運算。例如要計算時間A往後15天的日期,時間A就是datetime.datetime,15天則就是 datetime.timedelta,表示一段時間區間。

A timedelta object represents a duration, the difference between two dates or times. (datetime官方網站)

datetime.datetimedatetime.timedelta兩者的組合可以針對時間進行算數,一些簡單的規則如下:

datetime2 = datetime1 + timedelta 
datetime2 = datetime1 - timedelta
timedelta = datetime1 - datetime2
datetime1 < datetime2