Pandas DateTime 超強總結

語言: CN / TW / HK

攜手創作,共同成長!這是我參與「掘金日新計劃 · 8 月更文挑戰」的第26天,點擊查看活動詳情

對於 Pandas 來説,可以處理眾多的數據類型,其中最有趣和最重要的數據類型之一就是時間序列數據。時間序列數據無處不在,它在各個行業都有很多應用。患者健康指標、股票價格變化、天氣記錄、經濟指標、服務器、網絡、傳感器和應用程序性能監控都是時間序列數據的應用方向

我們可以將時間序列數據定義為在不同時間間隔獲得並按時間順序排列的數據點的集合

Pandas 基本上是為分析金融時間序列數據而開發的,併為處理時間、日期和時間序列數據提供了一整套全面的框架

今天我們來討論在 Pandas 中處理日期和時間的多個方面,具體包含如下內容: - Timestamp 和 Period 對象的功能 - 如何使用時間序列 DataFrames - 如何對時間序列進行切片 - DateTimeIndex 對象及其方法 - 如何重新採樣時間序列數據

探索 Pandas 時間戳和週期對象

Pandas 庫提供了一個名為 Timestamp 的具有納秒精度的 DateTime 對象來處理日期和時間值。Timestamp 對象派生自 NumPy 的 datetime64 數據類型,使其比 Python 的 DateTime 對象更準確而且更快。下面讓我們使用 Timestamp 構造函數創建一些 Timestamp 對象。 ```Python import pandas as pd import numpy as np from IPython.display import display

print(pd.Timestamp(year=1982, month=9, day=4, hour=1, minute=35, second=10)) print(pd.Timestamp('1982-09-04 1:35.18')) print(pd.Timestamp('Sep 04, 1982 1:35.18')) Output:Text 1982-09-04 01:35:10 1982-09-04 01:35:10 1982-09-04 01:35:10 如果將單個整數或浮點值傳遞給 Timestamp 構造函數,它會返回一個時間戳,該時間戳等於 Unix 紀元(1970 年 1 月 1 日)之後的納秒數:Python print(pd.Timestamp(5000)) Output:Text 1970-01-01 00:00:00.000005 Timestamp 對象包含許多方法和屬性,可幫助我們訪問時間戳的不同功能。讓我們嘗試一下:Python time_stamp = pd.Timestamp('2022-02-09') print('{}, {} {}, {}'.format(time_stamp.day_name(), time_stamp.month_name(), time_stamp.day, time_stamp.year)) Output:Text Wednesday, February 9, 2022 ``` Timestamp 類的一個實例代表一個時間點,而 Period 對象的一個實例代表一個時期,例如一年、一個月等

例如,公司在一年的時間裏監控他們的收入。Pandas 庫提供了一個名為 Period 的對象來處理,如下所示: Python year = pd.Period('2021') display(year) Output: Text Period('2021', 'A-DEC') 我們可以看到它創建了一個代表 2021 年期間的 Period 對象,而“A-DEC”表示該期間是年度的,在 12 月結束

Period 對象提供了許多有用的方法和屬性。例如,如果要返回期間的開始和結束時間,可以使用以下屬性: Python print('Start Time:', year.start_time) print('End Time:', year.end_time) Output: Text Start Time: 2021-01-01 00:00:00 End Time: 2021-12-31 23:59:59.999999999 要創建每月期間,可以將特定月份傳遞給它,如下所示 Python month = pd.Period('2022-01') display(month) print('Start Time:', month.start_time) print('End Time:', month.end_time) Output: ```Text Period('2022-01', 'M')

Start Time: 2022-01-01 00:00:00 End Time: 2022-01-31 23:59:59.999999999 “M”表示週期的頻率是每月一次。還可以使用 freq 參數顯式指定週期的頻率。下面的代碼創建了一個代表 2022 年 1 月 1 日期間的期間對象:Python day = pd.Period('2022-01', freq='D') display(day) print('Start Time:', day.start_time) print('End Time:', day.end_time) Output:Text Period('2022-01-01', 'D')

Start Time: 2022-01-01 00:00:00 End Time: 2022-01-01 23:59:59.999999999 我們還可以對週期對象執行算術運算。讓我們創建一個每小時頻率的新 period 對象,看看我們如何進行計算:Python hour = pd.Period('2022-02-09 16:00:00', freq='H') display(hour) display(hour + 2) display(hour - 2) Output:Text Period('2022-02-09 16:00', 'H')

Period('2022-02-09 18:00', 'H')

Period('2022-02-09 14:00', 'H') 我們可以使用 Pandas 日期偏移量獲得相同的結果:Python display(hour + pd.offsets.Hour(+2)) display(hour + pd.offsets.Hour(-2)) Output:Text Period('2022-02-09 18:00', 'H')

Period('2022-02-09 14:00', 'H') 要創建日期序列,可以使用 pandas range_dates() 方法。讓我們在代碼片段中嘗試一下:Python week = pd.date_range('2022-2-7', periods=7) for day in week: print('{}-{}\t{}'.format(day.day_of_week, day.day_name(), day.date())) Output:Text 0-Monday 2022-02-07 1-Tuesday 2022-02-08 2-Wednesday 2022-02-09 3-Thursday 2022-02-10 4-Friday 2022-02-11 5-Saturday 2022-02-12 6-Sunday 2022-02-13 ``` week 的數據類型是 DatetimeIndex 對象,一週中的每個日期都是 Timestamp 的一個實例。所以我們可以使用所有適用於 Timestamp 對象的方法和屬性

創建時間序列數據框

首先,讓我們通過從 CSV 文件中讀取數據來創建一個 DataFrame,該文件包含與連續 34 天每小時記錄的 50 台服務器相關的關鍵信息: Python df = pd.read_csv('http://raw.githubusercontent.com/m-mehdi/pandas_tutorials/main/server_util.csv') display(df.head()) Output: Text datetime server_id cpu_utilization free_memory session_count 0 2019-03-06 00:00:00 100 0.40 0.54 52 1 2019-03-06 01:00:00 100 0.49 0.51 58 2 2019-03-06 02:00:00 100 0.49 0.54 53 3 2019-03-06 03:00:00 100 0.44 0.56 49 4 2019-03-06 04:00:00 100 0.42 0.52 54 讓我們看一下 DataFrame 的內容。每個 DataFrame 行代表服務器的基本性能指標,包括特定時間戳的 CPU 利用率、可用內存和會話計數。DataFrame 分解為一小時的片段。例如,從午夜到凌晨 4 點記錄的性能指標位於 DataFrame 的前五行

現在,讓我們詳細瞭解一下 DataFrame 的特性,例如它的大小和每列的數據類型: Python print(df.info()) Output: Text <class 'pandas.core.frame.DataFrame'> RangeIndex: 40800 entries, 0 to 40799 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 datetime 40800 non-null object 1 server_id 40800 non-null int64 2 cpu_utilization 40800 non-null float64 3 free_memory 40800 non-null float64 4 session_count 40800 non-null int64 dtypes: float64(2), int64(2), object(1) memory usage: 1.6+ MB None 運行上面的語句會返回行數和列數、總內存使用量、每列的數據類型等

根據上面的信息,datetime 列的數據類型是對象,這意味着時間戳存儲為字符串值。要將 datetime 列的數據類型從 string 對象轉換為 datetime64 對象,我們可以使用 pandas 的 to_datetime() 方法,如下: Python df['datetime'] = pd.to_datetime(df['datetime']) 當我們通過導入 CSV 文件創建 DataFrame 時,日期/時間值被視為字符串對象,而不是 DateTime 對象。pandas to_datetime() 方法將存儲在 DataFrame 列中的日期/時間值轉換為 DateTime 對象。將日期/時間值作為 DateTime 對象使操作它們變得更加容易。 運行以下語句並查看更改: Python print(df.info()) Output: Text <class 'pandas.core.frame.DataFrame'> RangeIndex: 40800 entries, 0 to 40799 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 datetime 40800 non-null datetime64[ns] 1 server_id 40800 non-null int64 2 cpu_utilization 40800 non-null float64 3 free_memory 40800 non-null float64 4 session_count 40800 non-null int64 dtypes: datetime64[ns](1), float64(2), int64(2) memory usage: 1.6 MB None 現在 datetime 列的數據類型是 datetime64[ns] 對象。 [ns] 表示基於納秒的時間格式,它指定 DateTime 對象的精度

此外,我們可以讓 pandas 的 read_csv() 方法將某些列解析為 DataTime 對象,這比使用 to_datetime() 方法更直接。讓我們嘗試一下: Python df = pd.read_csv('http://raw.githubusercontent.com/m-mehdi/pandas_tutorials/main/server_util.csv', parse_dates=['datetime']) print(df.head()) Output: Text datetime server_id cpu_utilization free_memory session_count 0 2019-03-06 00:00:00 100 0.40 0.54 52 1 2019-03-06 01:00:00 100 0.49 0.51 58 2 2019-03-06 02:00:00 100 0.49 0.54 53 3 2019-03-06 03:00:00 100 0.44 0.56 49 4 2019-03-06 04:00:00 100 0.42 0.52 54 運行上面的代碼會創建一個 DataFrame,其中 datetime 列的數據類型是 DateTime 對象

下面讓我們對 datetime 列應用一些基本方法

首先,讓我們看看如何在 DataFrame 中返回最早和最晚的日期。為此,我們可以簡單地在 datetime 列上應用 max() 和 min() 方法,如下所示: Python display(df.datetime.min()) display(df.datetime.max()) Output: ```Text Timestamp('2019-03-06 00:00:00')

Timestamp('2019-04-08 23:00:00') 要選擇兩個特定日期之間的 DataFrame 行,我們可以創建一個布爾掩碼並使用 .loc 方法過濾特定日期範圍內的行:Python mask = (df.datetime >= pd.Timestamp('2019-03-06')) & (df.datetime < pd.Timestamp('2019-03-07')) display(df.loc[mask]) Output:Text datetime server_id cpu_utilization free_memory session_count 0 2019-03-06 00:00:00 100 0.40 0.54 52 1 2019-03-06 01:00:00 100 0.49 0.51 58 2 2019-03-06 02:00:00 100 0.49 0.54 53 3 2019-03-06 03:00:00 100 0.44 0.56 49 4 2019-03-06 04:00:00 100 0.42 0.52 54 … … … … … … 40003 2019-03-06 19:00:00 149 0.74 0.24 81 40004 2019-03-06 20:00:00 149 0.73 0.23 81 40005 2019-03-06 21:00:00 149 0.79 0.29 83 40006 2019-03-06 22:00:00 149 0.73 0.29 82 40007 2019-03-06 23:00:00 149 0.75 0.24 84 1200 rows × 5 columns ```

切片時間序列

為了使時間戳切片成為可能,我們需要將 datetime 列設置為 DataFrame 的索引。要將列設置為 DataFrame 的索引,請使用 set_index 方法: Python df.set_index('datetime', inplace=True) print(df) Output: ```Text datetime server_id cpu_utilization free_memory session_count 2019-03-06 00:00:00 100 0.40 0.54 52 2019-03-06 01:00:00 100 0.49 0.51 58 2019-03-06 02:00:00 100 0.49 0.54 53 2019-03-06 03:00:00 100 0.44 0.56 49 2019-03-06 04:00:00 100 0.42 0.52 54 ... ... ... ... ... 2019-04-08 19:00:00 149 0.73 0.20 81 2019-04-08 20:00:00 149 0.75 0.25 83 2019-04-08 21:00:00 149 0.80 0.26 82 2019-04-08 22:00:00 149 0.75 0.29 82 2019-04-08 23:00:00 149 0.75 0.24 80

[40800 rows x 4 columns] 要使用 .loc 方法選擇等於單個索引的所有行:Python print(df.loc['2019-03-07 02:00:00'].head(5)) Output:Text datetime server_id cpu_utilization free_memory session_count 2019-03-07 02:00:00 100 0.44 0.50 56 2019-03-07 02:00:00 101 0.78 0.21 87 2019-03-07 02:00:00 102 0.75 0.27 80 2019-03-07 02:00:00 103 0.76 0.28 85 2019-03-07 02:00:00 104 0.74 0.24 77 可以選擇與索引列中的特定時間戳部分匹配的行。讓我們嘗試一下:Python print(df.loc['2019-03-07'].head(5)) Output:Text datetime server_id cpu_utilization free_memory session_count 2019-03-07 00:00:00 100 0.51 0.52 55 2019-03-07 01:00:00 100 0.46 0.50 49 2019-03-07 02:00:00 100 0.44 0.50 56 2019-03-07 03:00:00 100 0.45 0.52 51 2019-03-07 04:00:00 100 0.42 0.50 53 選擇字符串可以是任何標準的日期格式,我們來看一些例子:Python df.loc['Apr 2019'] df.loc['8th April 2019'] df.loc['April 05, 2019 5pm'] 我們還可以使用 .loc 方法對日期範圍內的行進行切片。以下語句將返回從 2019 年 4 月 3 日到 2019 年 4 月 4 日結束的所有行; 開始日期和結束日期都包括在內:Python display(df.loc['03-04-2019':'04-04-2019']) 但是運行它會引發一個令人討厭的未來警告。為了擺脱警告,我們可以在切片行之前對索引進行排序:Python display(df.sort_index().loc['03-04-2019':'04-04-2019']) Output:Text datetime server_id cpu_utilization free_memory session_count 2019-03-06 00:00:00 100 0.40 0.54 52 2019-03-06 00:00:00 135 0.50 0.55 55 2019-03-06 00:00:00 110 0.54 0.40 61 2019-03-06 00:00:00 136 0.58 0.40 64 2019-03-06 00:00:00 109 0.57 0.41 61 … … … … … 2019-04-04 23:00:00 143 0.43 0.52 50 2019-04-04 23:00:00 111 0.53 0.52 59 2019-04-04 23:00:00 149 0.75 0.24 85 2019-04-04 23:00:00 138 0.40 0.56 47 2019-04-04 23:00:00 107 0.63 0.33 73 36000 rows × 4 columns ```

DateTimeIndex 方法

某些 pandas DataFrame 方法僅適用於 DateTimeIndex。下面我們來具體看一下,首先讓我們確保我們的 DataFrame 有一個 DateTimeIndex: Python print(type(df.index)) Output: Text <class 'pandas.core.indexes.datetimes.DatetimeIndex'> 要返回在特定時間收集的服務器監控數據,無論日期如何,請使用 at_time() 方法: Python display(df.at_time('09:00')) Output: Text datetime server_id cpu_utilization free_memory session_count 2019-03-06 09:00:00 100 0.48 0.51 51 2019-03-07 09:00:00 100 0.45 0.49 56 2019-03-08 09:00:00 100 0.45 0.53 53 2019-03-09 09:00:00 100 0.45 0.51 53 2019-03-10 09:00:00 100 0.49 0.55 55 … … … … … 2019-04-04 09:00:00 149 0.75 0.21 80 2019-04-05 09:00:00 149 0.71 0.26 83 2019-04-06 09:00:00 149 0.75 0.30 83 2019-04-07 09:00:00 149 0.81 0.28 77 2019-04-08 09:00:00 149 0.82 0.24 86 1700 rows × 4 columns 此外,要選擇所有日期午夜和凌晨 2 點之間的所有服務器數據,可以使用 between_time() 方法。 讓我們嘗試一下: Python display(df.between_time('00:00','02:00')) Output: Text datetime server_id cpu_utilization free_memory session_count 2019-03-06 00:00:00 100 0.40 0.54 52 2019-03-06 01:00:00 100 0.49 0.51 58 2019-03-06 02:00:00 100 0.49 0.54 53 2019-03-07 00:00:00 100 0.51 0.52 55 2019-03-07 01:00:00 100 0.46 0.50 49 … … … … … 2019-04-07 01:00:00 149 0.74 0.21 78 2019-04-07 02:00:00 149 0.76 0.26 74 2019-04-08 00:00:00 149 0.75 0.28 75 2019-04-08 01:00:00 149 0.69 0.27 79 2019-04-08 02:00:00 149 0.78 0.20 85 5100 rows × 4 columns 我們可以使用 first() 方法根據特定的日期偏移量選擇第一個 DataFrame 行。例如,將 5B 作為日期偏移量傳遞給該方法會返回前五個工作日內具有索引的所有行。同樣,將 1W 傳遞給 last() 方法會返回上週內所有帶有索引的 DataFrame 行。需要注意的是,必須按其索引對 DataFrame 進行排序,以確保這些方法有效。讓我們試試這兩個例子: Python display(df.sort_index().first('5B')) Output: Text datetime server_id cpu_utilization free_memory session_count 2019-03-06 100 0.40 0.54 52 2019-03-06 135 0.50 0.55 55 2019-03-06 110 0.54 0.40 61 2019-03-06 136 0.58 0.40 64 2019-03-06 109 0.57 0.41 61 … … … … … 2019-03-12 134 0.53 0.45 61 2019-03-12 144 0.68 0.31 73 2019-03-12 113 0.76 0.24 83 2019-03-12 114 0.58 0.48 67 2019-03-12 131 0.58 0.42 67 7250 rows × 4 columns Python display(df.sort_index().last('1W')) Output: Text datetime server_id cpu_utilization free_memory session_count 2019-04-08 00:00:00 106 0.44 0.62 49 2019-04-08 00:00:00 112 0.72 0.29 81 2019-04-08 00:00:00 100 0.43 0.54 51 2019-04-08 00:00:00 137 0.75 0.28 83 2019-04-08 00:00:00 110 0.61 0.40 62 … … … … … 2019-04-08 23:00:00 128 0.64 0.41 64 2019-04-08 23:00:00 127 0.67 0.33 78 2019-04-08 23:00:00 126 0.71 0.33 73 2019-04-08 23:00:00 123 0.71 0.22 83 2019-04-08 23:00:00 149 0.75 0.24 80 1200 rows × 4 columns Python df.sort_index().last('2W') Output: Text datetime server_id cpu_utilization free_memory session_count 2019-04-01 00:00:00 120 0.54 0.48 63 2019-04-01 00:00:00 104 0.73 0.31 83 2019-04-01 00:00:00 103 0.77 0.22 82 2019-04-01 00:00:00 124 0.39 0.55 49 2019-04-01 00:00:00 127 0.68 0.37 73 … … … … … 2019-04-08 23:00:00 128 0.64 0.41 64 2019-04-08 23:00:00 127 0.67 0.33 78 2019-04-08 23:00:00 126 0.71 0.33 73 2019-04-08 23:00:00 123 0.71 0.22 83 2019-04-08 23:00:00 149 0.75 0.24 80 9600 rows × 4 columns

重新採樣時間序列數據

resample() 方法背後的邏輯類似於 groupby() 方法。它在任何可能的時間段內對數據進行分組。雖然我們可以使用 resample() 方法進行上採樣和下采樣,但我們將重點介紹如何使用它來執行下采樣,這會降低時間序列數據的頻率——例如,將每小時的時間序列數據轉換為每日或 每日時間序列數據到每月

以下示例返回服務器 ID 100 每天的平均 CPU 利用率、可用內存和活動會話計數。 為此,我們首先需要過濾 DataFrame 中服務器 ID 為 100 的行,然後將每小時數據重新採樣為每日數據。 最後,對結果應用 mean() 方法,得到三個指標的每日平均值: Python df[df.server_id == 100].resample('D')['cpu_utilization', 'free_memory', 'session_count'].mean() Output: Text datetime cpu_utilization free_memory session_count 2019-03-06 0.470417 0.535417 53.000000 2019-03-07 0.455417 0.525417 53.666667 2019-03-08 0.478333 0.532917 54.541667 2019-03-09 0.472917 0.523333 54.166667 2019-03-10 0.465000 0.527500 54.041667 2019-03-11 0.469583 0.528750 53.916667 2019-03-12 0.475000 0.533333 53.750000 2019-03-13 0.462917 0.521667 52.541667 2019-03-14 0.472083 0.532500 54.875000 2019-03-15 0.470417 0.530417 53.500000 2019-03-16 0.463750 0.530833 54.416667 2019-03-17 0.472917 0.532917 52.041667 2019-03-18 0.475417 0.535000 53.333333 2019-03-19 0.460833 0.546667 54.791667 ... 我們還可以通過鏈接 groupby() 和 resample() 方法來查看每個服務器 ID 的相同結果。以下語句返回每個服務器每月的最大 CPU 利用率和可用內存。讓我們嘗試一下: Python df.groupby(df.server_id).resample('M')['cpu_utilization', 'free_memory'].max() Output: Text server_id datetime cpu_utilization free_memory 100 2019-03-31 0.56 0.62 2019-04-30 0.55 0.61 101 2019-03-31 0.91 0.32 2019-04-30 0.89 0.30 102 2019-03-31 0.85 0.36 … … … … 147 2019-04-30 0.61 0.57 148 2019-03-31 0.84 0.35 2019-04-30 0.83 0.34 149 2019-03-31 0.85 0.36 2019-04-30 0.83 0.34 100 rows × 2 columns 下面讓我們繪製每個服務器每月的平均 CPU 利用率,結果使我們能夠充分了解每個服務器的平均 CPU 利用率在幾個月內的變化 Python import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(24, 8)) df.groupby(df.server_id).resample('M')['cpu_utilization'].mean()\ .plot.bar(color=['green', 'gray'], ax=ax, title='The Average Monthly CPU Utilization Comparison') Output: Text <AxesSubplot:title={'center':'The Average Monthly CPU Utilization Comparison'}, xlabel='server_id,datetime'>

寫在最後

Pandas 是一種出色的分析工具,尤其是在處理時間序列數據時。該庫提供了廣泛的工具來處理時間索引的 DataFrame

好了,這就是今天分享的內容,如果喜歡就點個贊吧~