一.背景
通常app打点数据是有时间周期规律的时序数据,当打点数据出现异常时,需要有一种及时发现问题的方法。
二.发现规则变化的方法
2.1 传统方法
通过人工观察,当某段时间打点数据趋势突然异常的时候(明显增加或者减少),此时可以推测,增加的原因可能是某个规则欠拟合了;而减少的原因,可能是app升级版本后修改了api,导致某个规则失效了等等。
这样的弊端是,当打点规则非常多时,对每个规则都进行比对是非常低效的。
2.2 机器学习方法
https://blog.csdn.net/weixin_35834894/article/details/95637081
上文中提到了关于时间序列异常检测算法的一些讨论。
另外两篇文章:时间序列异常检测算法S-H-ESD
https://www.cnblogs.com/en-heng/p/9202654.html
时间序列分解算法:STL
https://www.cnblogs.com/en-heng/p/7390310.html
facebook开源的prophet时间序列预测工具---识别多种周期性、趋势性(线性,logistic)、节假日效应,以及部分异常值
https://www.cnblogs.com/bonelee/p/9577432.html
另外,ElasticSearch也提供了基于日志数据的machine learning算法。
三.使用prophet进行趋势预测及异常值检测
项目源码地址
https://github.com/facebook/prophet
参考官方快速开始文档。
https://facebook.github.io/prophet/docs/quick_start.html
3.1 安装prophet
参考如下文章
https://blog.csdn.net/qq_33873431/article/details/90064945
3.1.1 安装anaconda
https://conda.io/miniconda.html
需要勾选将anaconda添加到path中。安装完成后cmd中设置清华镜像。
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --set show_channel_urls yes
conda config --show
3.1.2 安装pyprophet
conda install -c conda-forge fbprophet
问题1:python版本过高
使用anaconda安装python3.7,并绑定到conda的虚拟环境python37下面。
#不指定3.7.x时默认会找3.7下最新的版本。
conda create --name python37 python=3.7
切换到python3.7环境以及切换回默认环境的命令
conda activate python37
conda deactivate
#删除已有环境
conda remove --name python37 --all
切换到python3.7环境后重新安装成功,这里需要下载一些安装包。
3.2 安装jupyter notebook
为了能够更方便原型验证的开发和调试,这里安装了jupyter notebook。
安装命令:
conda install -c conda-forge jupyter notebook
启动命令:
jupyter notebook
3.3 趋势预测
3.3.1 数据格式
导入10天的csv数据,格式如下
appId,actId,ruleId,day,hour,cnt
421,-1,61610,20200728,22,1217421
7873,-1,78166,20200728,22,17520
41,-1,61796,20200728,22,219648
1510,135,64553,20200728,22,174251
1786,-1,69972,20200728,22,36171
6813,-1,76046,20200728,22,82855
3453,-1,73536,20200728,22,6819
1647,-1,67366,20200728,22,49859
2237,-1,70287,20200728,22,1583
7913,-1,78540,20200728,22,41
....
问题2:无法导入plotly
原因是plotly没有安装,用如下命令安装后解决。
conda install -c conda-forge plotly
3.3.2 导入相关包
import pandas as pd
from fbprophet import Prophet
from pandas.plotting import register_matplotlib_converters
3.3.3 读取csv文件
df=pd.read_csv("./data/appId_actId_ruleId_day_hour_cnt.csv")
print(df.head())
3.3.4 将csv文件处理为Prophet的输入
Prophet的输入始终是两列数据,ds和y。ds是时间戳,格式为(YYYY-MM-DD或者YYYY-MM-DD HH:MM:SS),y是数字,代表预测的度量。
先筛选某一个规则的命中趋势来进行预测
df=df[df.appId==0]
df=df[df.actId==1843]
df[df.ruleId==73444]
df['ds']=df['day'].astype('str').str.cat(df['hour'].astype('str'),sep='-')
import datetime
df['ds']=df['ds'].map(lambda x:datetime.datetime.strptime(x, '%Y%m%d-%H').strftime('%Y-%m-%d %H:00:00'))
df['y']=df['cnt']
df=df[['ds','y']]
# 重置dataframe的行号从0开始
df.reset_index(drop=True, inplace=True)
3.3.5 使用fit方法对数据做预处理
m=Prophet(weekly_seasonality=True)
m.fit(df)
Prophet的参数有weekly_seasonality和yearly_seasonality,表示是否是按年或者是按星期拥有季节性的。
3.3.6 使用predict方法进行预测
future = m.make_future_dataframe(periods=24*7,freq='H')
future.tail()
forecast = m.predict(future)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()
m.make_future_dataframe()方法中,periods表示要预测的未来的时期,freq表示当前数据的频率,'H'表示小时。
其中yhat代表预测值,yhat_lower代表预测范围的最小值,yhat_upper代表预测范围的最大值。
3.3.7 使用plot方法画出预测图
fig1 = m.plot(forecast)
3.3.8 使用plot_components画出趋势的各个分量
trend表示y总体的趋势。
weekly表示y按周的变化趋势。
daily表示y按天的变化趋势。
3.4 趋势改变点发现
3.4.1 数据预处理
import pandas as pd
from fbprophet import Prophet
from pandas.plotting import register_matplotlib_converters
df=pd.read_csv("./data/appId_actId_ruleId_day_hour_cnt.csv")
#指定要分析的ruleId
df=df[df.ruleId==59999.0]
print(df.head())
df['ds']=df['day'].astype('str').str.cat(df['hour'].astype('str'),sep='-')
import datetime
df['ds']=df['ds'].map(lambda x:datetime.datetime.strptime(x, '%Y%m%d-%H').strftime('%Y-%m-%d %H:00:00'))
df['y']=df['cnt']
df=df[['ds','y']]
# 重置dataframe的行号从0开始
df.reset_index(drop=True, inplace=True)
3.4.2 趋势变更点发现
m=Prophet()
m.fit(df)
future = m.make_future_dataframe(periods=3,freq='H')
forecast = m.predict(future)
from fbprophet.plot import add_changepoints_to_plot
fig = m.plot(forecast)
a = add_changepoints_to_plot(fig.gca(), m, forecast)
下图中黑点是ruleId 59999实际的pv,蓝线是根据实际的pv拟合的曲线。红色的虚线是趋势变更点,红色的实线是趋势。
对比正常的ruleId 73444,不存在红色的虚线,即无趋势变更点。
可以通过是否存在趋势变更点来判断规则是否正常。
3.4.3 趋势变更点计算源码
上一节中将趋势变更点通过plot方法展示在了图上,如果需要获取趋势变更点的数组,可以参考add_changepoints_to_plot的源码。
源码地址如下:
关键代码为:
import numpy as np
threshold = 0.01
signif_changepoints = m.changepoints[np.abs(np.nanmean(m.params['delta'], axis=0)) >= threshold]