計算時間序列周期的三種方法

deephub 發佈 2024-05-13T09:14:03.187943+00:00

周期是數據中出現重複模式所需的時間長度。更具體地說,它是模式的一個完整周期的持續時間。在這篇文章中,將介紹計算時間序列周期的三種不同方法。我們使用City of Ottawa 數據集,主要關注的是每天的服務呼叫數量。所以不需要對病房名稱進行初始數據處理。

周期是數據中出現重複模式所需的時間長度。更具體地說,它是模式的一個完整周期的持續時間。在這篇文章中,將介紹計算時間序列周期的三種不同方法。

我們使用City of Ottawa 數據集,主要關注的是每天的服務呼叫數量。所以不需要對病房名稱進行初始數據處理。Ottawa 數據集在渥太華市提供的數據門戶網站上免費提供。

讓我們加載2019-2022年的這些數據,並將它們連接起來得到一個df。

from google.colab import drive
drive.mount('/content/gdrive')
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
file_path = '/content/gdrive/My Drive/Colab Notebooks/Data/SR-2019.xlsx'
records2019 = pd.read_excel(file_path)#,encoding='utf16')
file_path = '/content/gdrive/My Drive/Colab Notebooks/Data/SR-2020.xlsx'
records2020 = pd.read_excel(file_path)#,encoding='utf16')
file_path = '/content/gdrive/My Drive/Colab Notebooks/Data/2021_Monthly_Service_Requests_EN.xlsx'
records2021 = pd.read_excel(file_path)#,encoding='utf16')
file_path = '/content/gdrive/My Drive/Colab Notebooks/Data/2022_Monthly_Service_Requests.csv'
records2022 =pd.read_csv(file_path)
records=pd.concat([records2019,records2020,records2021,records2022],axis=0)

讓我們根據服務調用日期聚合這些數據,並得到一個簡單的圖。

records["DATE_RAISED"]=pd.to_datetime(records.DATE_RAISED)
record_by_date=records.groupby("DATE_RAISED")["TYPE"].count().sort_index()
record_by_date.plot(figsize = (25, 10))
plt.ylabel('Number of requests')
plt.grid(visible=True,which='both')
plt.figure()
record_by_date.iloc[100:130].plot(figsize = (25, 10))
plt.ylabel('Number of requests')
plt.grid(visible=True,which='both')

填充缺失

讓我們檢查一下我們的數據是否包含了所有的日期。

start_date = record_by_date.index.min()
end_date = record_by_date.index.max()
# create a complete date range for the period of interest
date_range = pd.date_range(start=start_date, end=end_date, freq='D')
# compare the date range to the index of the time series
missing_dates = date_range[~date_range.isin(record_by_date.index)]
if len(missing_dates) > 0:
print("Missing dates:", missing_dates)
else:
print("No missing dates")

正如所預期的那樣,數據缺少一些日期的值。讓我們用相鄰日期的平均值填充這些值。

# Reindex to fill missing dates
idx = pd.date_range(start=record_by_date.index.min(), end=record_by_date.index.max(), freq='D')
record_by_date = record_by_date.reindex(idx, fill_value=0)
# Add missing dates with average of surrounding values
for date in missing_dates:
prev_date = date - pd.DateOffset(days=1)
next_date = date + pd.DateOffset(days=1)
prev_val = record_by_date.loc[prev_date] if prev_date in record_by_date.index else np.nan
next_val = record_by_date.loc[next_date] if next_date in record_by_date.index else np.nan
avg_val = np.nanmean([prev_val, next_val])
record_by_date.loc[date] = avg_val

這就是我們要做的所有預處理了,在所有這些步驟之後,我們嘗試檢測這個時間序列的周期。一般來說,基於假日模式和一般的人類習慣,我們希望在數據中看到七天的周期,我們來看看是不是有這樣的結果。

0、目測

最簡單的方法就是目測。這是一種主觀的方法,而不是一種正式的或統計的方法,所以我把它作為我們列表中的原始方法。

如果我們看一下這張圖的放大部分,我們可以看到7天的周期。最低值出現在5月14日、21日和28日。但最高點似乎不遵循這個模式。但在更大的範圍內,我們仍然可以說這個數據集的周期是7天。

下面我們來正式的進行分析:

1、自相關分析

我們將繪製時間序列的自相關值。查看acf圖中各種滯後值的峰值。與第一個顯著峰值對應的滯後可以給出周期的估計。

對於這種情況,我們看看50個滯後值,並使用statmodels包中的方法繪製acf。

from statsmodels.graphics.tsaplots import plot_acf
fig, ax = plt.subplots(figsize=(14,7))
plot_acf(record_by_date.values.squeeze(), lags=50,ax=ax,title='Autocorrelation', use_vlines=True);
lags = list(range(51))
ax.set_xticks(lags);
ax.set_xticklabels(lags);

從上圖可以看出,在7、1、21等處有峰值。這證實了我們的時間序列有7天的周期。

2、快速傅立葉變換

對時間序列進行傅立葉變換,尋找主頻分量。主頻率的倒數可以作為周期的估計值。

傅立葉變換是一種數學運算,它把一個複雜的信號分解成一組更簡單的正弦和餘弦波。傅立葉變換廣泛應用於信號處理、通信、圖像處理以及其他許多科學和工程領域。它允許我們在頻域中分析和操作信號,這通常是一種比在時域中更自然和直觀的理解和處理信號的方法。

from scipy.fft import fft
# Calculate the Fourier transform
yf = np.fft.fft(record_by_date)
xf = np.linspace(0.0, 1.0/(2.0), len(record_by_date)//2)
# Find the dominant frequency
# We have to drop the first element of the fft as it corresponds to the 
# DC component or the average value of the signal
idx = np.argmax(np.abs(yf[1:len(record_by_date)//2]))
freq = xf[idx]
period =(1/freq)
print(f"The period of the time series is {period}")

輸出為:The period of the time series is 7.030927835051545。這與我們使用acf和目視檢查發現的每周周期相似。

3、周期圖

周期圖 Periodogram 是一個信號或序列的功率譜密度(PSD)圖。換句話說它是一個顯示信號中每個頻率包含多少總功率的圖表。周期圖是通過計算信號的傅立葉變換的幅值平方得到的,常用於信號處理和頻譜分析。在某種意義上,只是前面給出的基於fft的方法的擴展。

from scipy.signal import periodogram
freq, power = periodogram(record_by_date)
period = 1/freq[np.argmax(power)]
print(f"The period of the time series is {period}")
plt.plot(freq, power)
plt.xlabel('Frequency (Hz)')
plt.ylabel('Power spectral density')
plt.show()

周期圖可以清楚地看出,信號的最高功率在0.14,對應於7天的周期。

總結

本文,我們介紹了尋找時間序列周期的三種不同方法,通過使用這三種方法,我們能夠識別信號的周期性,並使用常識進行確認。

作者:Shashindra Silva

關鍵字: