?
今天我們介紹一下使用python做時間序列數(shù)據(jù)分析和預測中異常值檢測的方法,常用的異常值檢測方法有以下幾種:
- 3sigma: 基于正太分布,當數(shù)據(jù)值超過±3個標準差(3sigma)時為異常值。
- z-score : z標準分數(shù),它測量數(shù)據(jù)值到平均值的距離,當數(shù)據(jù)與平均值相差2個標準差時z-score為2,如果將z-score為3作為異常值判斷標準時,便相當于3sigma。
- 箱體法(box): 它基于數(shù)據(jù)的四分位值來判斷異常值。
- 多維度異常值判斷法,通過數(shù)據(jù)特征的多個維度綜合判斷數(shù)據(jù)是否為異常值。
注:3sigma,z-score,箱體法(box)都是從數(shù)據(jù)值本身的單一維度去分析和判斷異常值,從而有一定的局限性, 然而多維度異常值判斷法更注重從數(shù)據(jù)特征的各個維度去分析和判斷異常值,顯然多維度異常值判斷法更為科學和精準。
導入時間序列數(shù)據(jù)
我們的數(shù)據(jù)來自于某商業(yè)零售門店的每日客流量數(shù)據(jù),客流量數(shù)據(jù)直接關系到門店銷售業(yè)績,所以有必要對客流量數(shù)據(jù)進行分析。
數(shù)據(jù)中的 y 列代表了客流量,這里數(shù)據(jù)的時間范圍為2020.1至2023.1 ,接下來我們查看數(shù)據(jù)的趨勢圖。
plt.figure(figsize=(10,4),dpi=100)
plt.plot(df)
plt.title("客流量趨勢")
plt.show()
?
?下面我們查看客流量數(shù)據(jù)的熱力圖分布:
calplot.calplot(df.y,suptitle='客流量分布',cmap='YlGn');
?從熱力圖的顏色深淺變化,我們也能發(fā)現(xiàn)客流量逐年在減少,這可能和疫情持續(xù)有關。
3sigma
依據(jù)正太分布異常值分布在3個標準差以外的位置,如下圖所示:
下面我們來計算數(shù)據(jù)的±3個標準以外的位置,落在這兩個位置內的數(shù)據(jù)點即為異常值:
# 3sigma
def three_sigma(df):
mean=df.y.mean()
std=df.y.std()
upper_limit=mean+3*std
lower_limit=mean-3*std
df['anomaly']=df.y.apply(lambda x: 1 if (x>upper_limit )
or (x<lower_limit) else 0)
return df
df1 = three_sigma(df.copy())
df1[df1.anomaly==1]
fig, ax = plt.subplots(figsize=(10,4))
a = df1.loc[df1['anomaly'] == 1, ['y']] #anomaly
ax.plot(df.index, df['y'], color='blue', label='正常值')
ax.scatter(a.index,a['y'], color='red', label='異常值')
plt.title(f'3sigma')
plt.xlabel('date')
plt.ylabel('y')
plt.legend()
plt.show();
?z-score
z-score測量數(shù)據(jù)值到平均值的距離,異常值的判斷依據(jù)為給定的距離閾值,一般情況下閾值可以設置在大于2個標準差的任意位置(依據(jù)業(yè)務和經驗來確定閾值)。如果將z-score為3作為異常值判定的閾值時,便相當于3sigma。
?文章來源地址http://www.zghlxwxcb.cn/news/detail-423238.html
# Z-Score
def z_score(df,threshold):
mean=df.y.mean()
std=df.y.std()
df['z_score']=df.y.apply(lambda x:abs(x-mean)/std)
df['anomaly']=df.z_score.apply(lambda x: 1 if x>threshold else 0)
return df
#設置閾值為2或3,當閾值為3時便相當于3sigma
threshold=2
df2 = z_score(df.copy(),threshold)
df2[df2.anomaly==1]
fig, ax = plt.subplots(figsize=(10,4))
a = df2.loc[df2['anomaly'] == 1, ['y']]
ax.plot(df.index, df['y'], color='blue', label='正常值')
ax.scatter(a.index,a['y'], color='red', label='異常值')
plt.title(f'Z-score, {threshold=}')
plt.xlabel('date')
plt.ylabel('y')
plt.legend()
plt.show();
箱體法(box)
?箱體法(box)基于數(shù)據(jù)的四分位值來判斷異常值。異常值>Q3+1.5*IQR 或者?異常值<Q1-1.5*IQR
def box_plot(df):
q1=np.nanpercentile(df.y,25)
q3=np.nanpercentile(df.y,75)
iqr=q3-q1
lower_limit=q1-1.5*iqr
upper_limit=q3+1.5*iqr
df['anomaly']=df.y.apply(lambda x: 1 if x<lower_limit or x>upper_limit
else 0)
return df
df3 = box_plot(df.copy())
df3[df3.anomaly==1]
fig, ax = plt.subplots(figsize=(10,4))
a = df3.loc[df3['anomaly'] == 1, ['y']]
ax.plot(df.index, df['y'], color='blue', label='正常值')
ax.scatter(a.index,a['y'], color='red', label='異常值')
plt.title(f'Box-plot')
plt.xlabel('date')
plt.ylabel('y')
plt.legend()
plt.show();
?多維度異常檢測法PyOD
異常檢測算法工具庫(PyOD) 可以從數(shù)據(jù)的多個特征維度來檢測異常值,所以我們可以將時間序列數(shù)據(jù)的日期特征分解成多個和時間相關的其它特征,同時我們還需要設置一個異常值比例,一般情況下我們設置異常值比例在5%以下。這里我們使用的是Pycaret的異常值檢測模型,該模型是對PyOD進行了再次包裝,使之調用更為簡單,感興趣的朋友可以去查看Pycaret和PyOD的相關文檔。這里我們首先將日期字段進行分解,從原始的日期字段中我們可以拆分出年,月,日,星期,季度等和時間相關的特征:
from pycaret.anomaly import AnomalyExperiment
# 分解日期特征
def create_features(df):
df['year'] = df.index.year #年
df['month'] = df.index.month #月
df['dayofmonth'] = df.index.day #日
df['dayofweek'] = df.index.dayofweek #星期
df['quarter'] = df.index.quarter #季度
df['weekend'] = df.dayofweek.apply(lambda x: 1 if x > 5 else 0) #是否周末
df['dayofyear'] = df.index.dayofyear #年中第幾天
df['weekofyear'] = df.index.weekofyear #年中第幾月
df['is_month_start']=df.index.is_month_start
df['is_month_end']=df.index.is_month_end
return df
#創(chuàng)建特征
df4 = create_features(df.copy())
df4
?接下來我們使用Pycaret的anomaly模型對新數(shù)據(jù)集進行建模和預測,同時我們仍然需要設置一個異常值比例的閾值fraction:
#異常值算法:'knn','cluster','iforest','svm'等。
alg='knn'#異常值算法
fraction=0.02 #異常值比例 0.02,0.03,0.04,0.05
#創(chuàng)建異常值模型
exp = AnomalyExperiment()
r = exp.setup(df4.copy(), session_id = 123,verbose=False)
model = exp.create_model(alg, fraction=fraction,verbose=False)
model_results = exp.assign_model(model,verbose=False)
#獲取檢測結果
df5 = pd.merge(df.reset_index(),model_results[['Anomaly']],
left_index=True, right_index=True)
df5.set_index('date',inplace=True)
fig, ax = plt.subplots(figsize=(10,4))
a = df5.loc[df5['Anomaly'] == 1, ['y']]
ax.plot(df5.index, df5['y'], color='blue', label='正常值')
ax.scatter(a.index,a['y'], color='red', label='異常值')
plt.title(f'Pycaret.anomaly {fraction=}')
plt.xlabel('date')
plt.ylabel('y')
plt.legend()
plt.show();
?
?
?
?這里我們還有多種異常值算法可以選擇,有興趣的朋友可以自己去嘗試不同的異常值算法:
?總結
今天我們介紹幾種常用的異常值檢測方法,其中3sigma,z-score,箱體法(box)都是從數(shù)據(jù)值本身的單一維度去分析和判斷異常值,從而有一定的局限性, 而多維度異常值判斷法更注重從數(shù)據(jù)特征的各個維度去分析和判斷異常值,顯然多維度異常值判斷法更為科學和精準。
參考資料
Pycaret 文檔
PyOD文檔文章來源:http://www.zghlxwxcb.cn/news/detail-423238.html
?
到了這里,關于基于Python的時間序列異常值檢測的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!