{ "cells": [ { "cell_type": "markdown", "id": "b1eefe41", "metadata": {}, "source": [ "# 4. 時間條件控制" ] }, { "cell_type": "markdown", "id": "ae5b58b4", "metadata": {}, "source": [ "## `xarray`時間條件控制\n", "\n", "在先前的範例中,已經示範如何用`slice`來選擇特定時間區間的資料,但若選擇的時間並不連續,用`slice`就不太方便。因此若能在時間加入一些條件控制,則可以只選擇滿足條件的資料。xarray轉譯出的時間屬於datetime物件,且存在年、月、日等時間單位的attribute,因此我們就可以針對它們進行條件控制。\n", "\n", "**Example 1: 選擇JAS(七至九月)季節的資料** \n", "\n", "首先準備資料:" ] }, { "cell_type": "code", "execution_count": 1, "id": "0721be78", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<xarray.DataArray 'olr' (time: 2208, lat: 90, lon: 360)>\n",
       "[71539200 values with dtype=float32]\n",
       "Coordinates:\n",
       "  * time     (time) datetime64[ns] 1998-07-01 1998-07-02 ... 2021-09-30\n",
       "  * lon      (lon) float32 0.5 1.5 2.5 3.5 4.5 ... 355.5 356.5 357.5 358.5 359.5\n",
       "  * lat      (lat) float32 -44.5 -43.5 -42.5 -41.5 -40.5 ... 41.5 42.5 43.5 44.5\n",
       "Attributes:\n",
       "    standard_name:  toa_outgoing_longwave_flux\n",
       "    long_name:      NOAA Climate Data Record of Daily Mean Upward Longwave Fl...\n",
       "    units:          W m-2\n",
       "    cell_methods:   time: mean area: mean
" ], "text/plain": [ "\n", "[71539200 values with dtype=float32]\n", "Coordinates:\n", " * time (time) datetime64[ns] 1998-07-01 1998-07-02 ... 2021-09-30\n", " * lon (lon) float32 0.5 1.5 2.5 3.5 4.5 ... 355.5 356.5 357.5 358.5 359.5\n", " * lat (lat) float32 -44.5 -43.5 -42.5 -41.5 -40.5 ... 41.5 42.5 43.5 44.5\n", "Attributes:\n", " standard_name: toa_outgoing_longwave_flux\n", " long_name: NOAA Climate Data Record of Daily Mean Upward Longwave Fl...\n", " units: W m-2\n", " cell_methods: time: mean area: mean" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import xarray as xr \n", "\n", "olr_ds = xr.open_dataset(\"data/olr.nc\")\n", "olr_da = olr_ds.olr\n", "olr_jas = olr_da.sel(time=(olr_da.time.dt.month.isin([7,8,9]))) \n", "olr_jas" ] }, { "cell_type": "markdown", "id": "38da4c51", "metadata": {}, "source": [ "**Example 2: 移除閏日 (leap day)**\n", "利用類似Example 1的方法,也可以只選取2/29之外的其他日期 (反向選擇),而達到移除閏日的目的。" ] }, { "cell_type": "code", "execution_count": 2, "id": "b01a127f", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<xarray.DataArray 'olr' (time: 8760, lat: 90, lon: 360)>\n",
       "[283824000 values with dtype=float32]\n",
       "Coordinates:\n",
       "  * time     (time) datetime64[ns] 1998-01-01 1998-01-02 ... 2021-12-31\n",
       "  * lon      (lon) float32 0.5 1.5 2.5 3.5 4.5 ... 355.5 356.5 357.5 358.5 359.5\n",
       "  * lat      (lat) float32 -44.5 -43.5 -42.5 -41.5 -40.5 ... 41.5 42.5 43.5 44.5\n",
       "Attributes:\n",
       "    standard_name:  toa_outgoing_longwave_flux\n",
       "    long_name:      NOAA Climate Data Record of Daily Mean Upward Longwave Fl...\n",
       "    units:          W m-2\n",
       "    cell_methods:   time: mean area: mean
" ], "text/plain": [ "\n", "[283824000 values with dtype=float32]\n", "Coordinates:\n", " * time (time) datetime64[ns] 1998-01-01 1998-01-02 ... 2021-12-31\n", " * lon (lon) float32 0.5 1.5 2.5 3.5 4.5 ... 355.5 356.5 357.5 358.5 359.5\n", " * lat (lat) float32 -44.5 -43.5 -42.5 -41.5 -40.5 ... 41.5 42.5 43.5 44.5\n", "Attributes:\n", " standard_name: toa_outgoing_longwave_flux\n", " long_name: NOAA Climate Data Record of Daily Mean Upward Longwave Fl...\n", " units: W m-2\n", " cell_methods: time: mean area: mean" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "olr_noleap = olr_da.sel(time=~((olr_da.time.dt.month == 2) & (olr_da.time.dt.day == 29))) # ~() 代表反向選擇\n", "olr_noleap" ] }, { "cell_type": "markdown", "id": "6d5e65d1", "metadata": {}, "source": [ "沒有2/29的資料。" ] }, { "cell_type": "markdown", "id": "4d959a2f", "metadata": {}, "source": [ "## `pandas`時間序列\n", "\n", "結合`pandas`和`datetime`,我們可以很輕易地製造`datetime`物件的時間序列。" ] }, { "cell_type": "code", "execution_count": 3, "id": "44ef1bbc", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "DatetimeIndex(['2000-01-01', '2000-02-02'], dtype='datetime64[ns]', freq=None)" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "\n", "# 直接將指定的時間轉換成datetime格式。\n", "pd.to_datetime([\"2000-01-01\", \"2000-02-02\"])" ] }, { "cell_type": "code", "execution_count": 4, "id": "045d63c6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "DatetimeIndex(['2000-01-01', '2000-01-02', '2000-01-03', '2000-01-04',\n", " '2000-01-05', '2000-01-06', '2000-01-07', '2000-01-08',\n", " '2000-01-09', '2000-01-10',\n", " ...\n", " '2000-12-21', '2000-12-22', '2000-12-23', '2000-12-24',\n", " '2000-12-25', '2000-12-26', '2000-12-27', '2000-12-28',\n", " '2000-12-29', '2000-12-30'],\n", " dtype='datetime64[ns]', length=365, freq='D')" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 給定初始時間、長度,製造一串時間序列\n", "ts = pd.date_range(\"2000-01-01\", periods=365)\n", "ts" ] }, { "cell_type": "markdown", "id": "431ce71f", "metadata": {}, "source": [ "或是給定初始值、結束值、時間序列取樣頻率:" ] }, { "cell_type": "code", "execution_count": 5, "id": "2384bbb5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "DatetimeIndex(['2000-01-01', '2000-01-02', '2000-01-03', '2000-01-04',\n", " '2000-01-05', '2000-01-06', '2000-01-07', '2000-01-08',\n", " '2000-01-09', '2000-01-10',\n", " ...\n", " '2000-12-21', '2000-12-22', '2000-12-23', '2000-12-24',\n", " '2000-12-25', '2000-12-26', '2000-12-27', '2000-12-28',\n", " '2000-12-29', '2000-12-30'],\n", " dtype='datetime64[ns]', length=365, freq='D')" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ts = pd.date_range(start='2000-01-01',end='2000-12-30',freq='1D')\n", "ts" ] }, { "cell_type": "markdown", "id": "14056d83", "metadata": {}, "source": [ "目前這串時間序列的格式是YYYY-MM-DD,我們也可以利用`strftime`的函數,將時間改寫成任意格式。以下範例是改寫成'Jan 01 00'等," ] }, { "cell_type": "code", "execution_count": 6, "id": "81b68128", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Index(['Jan 01 00', 'Jan 02 00', 'Jan 03 00', 'Jan 04 00', 'Jan 05 00',\n", " 'Jan 06 00', 'Jan 07 00', 'Jan 08 00', 'Jan 09 00', 'Jan 10 00',\n", " ...\n", " 'Dec 21 00', 'Dec 22 00', 'Dec 23 00', 'Dec 24 00', 'Dec 25 00',\n", " 'Dec 26 00', 'Dec 27 00', 'Dec 28 00', 'Dec 29 00', 'Dec 30 00'],\n", " dtype='object', length=365)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ts.strftime(\"%b %d %y\")" ] }, { "cell_type": "markdown", "id": "d247478a", "metadata": {}, "source": [ "其中,文字的月份以`%b`表示,日以`%d`表示,二位數的年份以`%y`表示,四位數的年份以`%Y`表示。更詳細的用法可以在 [`datetime`套件官方網站 - `strftime`-`strptime` Behavior](https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior) 中找到。\n", "\n", "如果是改寫`olrlt`的時間格式," ] }, { "cell_type": "code", "execution_count": 7, "id": "f9d46c71", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<xarray.DataArray 'strftime' (time: 8760)>\n",
       "array(['Jan 01 98', 'Jan 02 98', 'Jan 03 98', ..., 'Dec 29 21',\n",
       "       'Dec 30 21', 'Dec 31 21'], dtype=object)\n",
       "Coordinates:\n",
       "  * time     (time) datetime64[ns] 1998-01-01 1998-01-02 ... 2021-12-31
" ], "text/plain": [ "\n", "array(['Jan 01 98', 'Jan 02 98', 'Jan 03 98', ..., 'Dec 29 21',\n", " 'Dec 30 21', 'Dec 31 21'], dtype=object)\n", "Coordinates:\n", " * time (time) datetime64[ns] 1998-01-01 1998-01-02 ... 2021-12-31" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "olr_da.time.dt.strftime(\"%b %d %y\")" ] }, { "cell_type": "markdown", "id": "e9d0e720", "metadata": {}, "source": [ "以上的例子可以看到,`xarray.DataArray.time.dt` (稱為DatetimeAccessor) 的功能相當於`pandas.DatetimeIndex`,如此一來,之後繪製時間序列圖、Hovmöller diagrams,需要改變座標軸上的時間格式時,以上的方法就很實用了。" ] }, { "cell_type": "markdown", "id": "fa7f2b28", "metadata": {}, "source": [ "## `datetime`和`timedelta`\n", "\n", "不管是Datetime Accessor,還是`pandas.DatetimeIndex`,都屬於datetime的物件格式。\n", "> `datetime.datetime`: A combination of a date and a time. Attributes: year, month, day, hour, minute, second, microsecond, and tzinfo. [(datetime官方網站)](https://docs.python.org/3/library/datetime.html)\n", "\n", "而datetime也可以進行算數運算。例如要計算時間A往後15天的日期,時間A就是`datetime.datetime`,15天則就是 `datetime.timedelta`,表示一段時間區間。\n", "> A timedelta object represents a duration, the difference between two dates or times. [(datetime官方網站)](https://docs.python.org/3/library/datetime.html)\n", "\n", "`datetime.datetime`和`datetime.timedelta`兩者的組合可以針對時間進行算數,一些簡單的規則如下:\n", "\n", "```\n", "datetime2 = datetime1 + timedelta \n", "datetime2 = datetime1 - timedelta\n", "timedelta = datetime1 - datetime2\n", "datetime1 < datetime2 \n", "```" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.9" } }, "nbformat": 4, "nbformat_minor": 5 }