时间序列异常检测实战:酒店价格

先前的文章讲到的一些基于时间序列异常检测方法。今天找到了一篇实战文章,在此翻译下,供研究。

数据准备

这里使用的是公开的Expedia 个性化酒店搜索中的部分数据。数据介绍:

列名数据类型描述
srch_idInteger搜索ID
date_timeDate/time搜索时间
site_idIntegerExpedia不同的站点(例如:Expedia.com, Expedia.co.uk, Expedia.co.jp, ..)
visitor_location_country_idInteger浏览者所在国家
visitor_hist_starratingFloat用户历史预订的平均星级,null代表没有历史记录
visitor_hist_adr_usdFloat用户历史预订的平均房价,null代表没有历史记录
prop_country_idInteger酒店所在的国家
prop_idInteger酒店ID
prop_starratingInteger酒店星级(1-5),0代表酒店没有星级
prop_review_scoreFloat酒店点评分(0-5之间),0.5为梯度。0 代表没有点评
prop_brand_boolInteger1为品牌联锁,0为个体酒店
prop_location_score1Float酒店所在位置的得分
prop_location_score2Float酒店所在位置的得分
prop_log_historical_priceFloat酒店上次交易时的价格,如果为0,则代表近期没有交易
positionInteger酒店在搜索结果中的位置
price_usdFloat外显价格,不同国家显示的不同(有些包含税费有些不包含)
promotion_flagInteger如果有特价促销,显示为1,否则显示0
gross_booking_usdFloat每次交易的利润
srch_destination_idInteger酒店搜索的目的地ID
srch_length_of_stayInteger搜索的入住天数
srch_booking_windowInteger搜索的提前天数
srch_adults_countInteger搜索成人数量
srch_children_countInteger搜索儿童数量
srch_room_countInteger搜索房间数
srch_saturday_night_boolBoolean搜索的入住日期是否包含周六
srch_query_affinity_scoreFloat酒店平均点击率,0代表没有点击或没有展示
orig_destination_distanceFloat用户搜索时位置和酒店位置之间的距离,如果为NULL则表示距离无法被计算
random_boolBoolean1表示返回的结果是随机的,0 表示正常的结果返回
comp1_rateInteger1代表Expedia比竞争对手的价格低,0 表示和竞争对手的价格一样。-1表示Expedia的价格比竞争对手高。NULL代表数据缺失。
comp1_invInteger如果竞争对手此酒店不可定,则返回1,如果都可定,则返回0。NULL代表没有竞品数据
comp1_rate_percent_diffFloat与竞对价格差的百分比,NULL代表没有数据
comp2_rateInteger同上
comp2_invInteger同上
comp2_rate_percent_diffFloat同上
comp3_rateInteger同上
comp3_invInteger同上
comp3_rate_percent_diffFloat同上
comp4_rateInteger同上
comp4_invInteger同上
comp4_rate_percent_diffFloat同上
comp5_rateInteger同上
comp5_invInteger同上
comp5_rate_percent_diffFloat同上
comp6_rateInteger同上
comp6_invInteger同上
comp6_rate_percent_diffFloat同上
comp7_rateInteger同上
comp7_invInteger同上
comp7_rate_percent_diffFloat同上
comp8_rateInteger同上
comp8_invInteger同上
comp8_rate_percent_diffFloat同上

使用的数据:

  • 选择数据点最多的酒店 prop_id = 104517
  • 选择 visitor_location_country_id = 219(219 代表美国)来统一 price_usd 列
  • 选择 search_room_count = 1
  • 选择我们需要的其他特征:date_time、price_usd、srch_booking_window 和 srch_saturday_night_bool

数据观察

整体价格分布:

查看包含周六与不包含周六的价格分布:

包含周六的价格明显偏高。说明价格与是否包含周六强相关。

再来看看其他的字段:

Booking Window与价格的关系:

发现还是存在一定的相关性。

基于聚类的异常检测

这里使用K-means算法,首先通过肘部法选择合适的K值

从上图,我们选定k=7。然后绘制3D的簇:

前只是进行了聚类,如何确定聚类中的哪些点是异常点呢?先来看看各个特征的重要性:

我们可以看到,第一个成分解释了解释了几乎 50% 的方差,第二个成分解释了超过 30% 的方差。没有哪一个成分是可以忽略不计的。前两个成分包含了超过 80% 的信息,这里是用主成分分析PCA降维到二维后再进行聚类:

基于聚类的异常检测中强调的假设是我们对数据聚类,正常的数据归属于簇,而异常不属于任何簇或者属于很小的簇。下面我们找出异常并进行可视化。

  • 计算每个点和离它最近的聚类中心的距离。最大的那些距离就是异常
  • 我们用 outliers_fraction 给算法提供数据集中离群点比例的信息
  • 使用 outliers_fraction 计算 number_of_outliers
  • 将 threshold 设置为离群点间的最短距离
  • anomaly1 的异常结果包括上述方法的簇(0:正常,1:异常)
  • 使用集群视图可视化异常
  • 使用时序视图可视化异常

结果表明,k-平均聚类检测到的异常房费要么非常高,要么非常低。

基于孤立森林(Isolation Forest)进行异常检测

孤立森林纯粹基于异常值的数量少且取值有异这一情况来进行检测。异常隔离不用度量任何距离或者密度就可以实现。这与基于聚类或者基于距离的算法完全不同。

  • 使用IsolationForest模型,设置contamination = outliers_fraction,即这异常的比例
  • fit 和 predict(data) 在数据集上执行异常检测,对于正常值返回 1,对于异常值返回 -1

基于支持向量机的异常检测(OneClassSVM

SVM 和监督学习紧密相连,但是 OneClassSVM 可以将异常检测当作一个无监督的问题,学得一个决策函数:将新数据归类为与训练数据集相似或者与训练数据集不同:

  • 在拟合 OneClassSVM 模型时,设置 nu=outliers_fraction,设置离群值占比
  • 指定算法中的核函数类型:rbf。此时 SVM 使用非线性函数将超空间映射到更高维度中
  • predict(data) 执行数据分类。会返回 +1 或者 -1,-1 代表异常,1 代表正常

基于高斯分布进行异常检测

高斯分布又称为正态分布。假设数据服从正态分布。这个假设并不适用于所有数据集,一旦成立,就能高效地确定离群点。Scikit-Learn 的covariance.EllipticEnvelope函数假设全体数据是概率分布的外在表现形式,其背后服从一项多变量高斯分布,以此尝试计算数据数据总体分布的关键参数:

  • 创建两个不同的数据集:search_Sat_night、Search_Non_Sat_night。
  • 对每个类别使用 EllipticEnvelope(高斯分布)。
  • 我们设置 contamination 参数,它是数据集中出现的离群点的比例
  • 我们用 decision_function 来计算给定观测值的决策函数。
  • predict(X_train) 使用拟合好的模型预测 X_train 的标签(1 表示正常,-1 表示异常)

基于马尔可夫链(Markov Chain)的异常检测

马尔可夫链可以度量一系列事件发生的概率。我们可以建立一个马尔可夫链,当事件发生时,我们可以用马尔可夫链来度量该序列发生的概率,并用它来检测是否异常。在我们的价格异常检测项目中,我们需要离散化马尔可夫链定义状态下的数据点。我们将使用“price_usd”来定义这个示例的状态,并定义5个级别的值(非常低、低、平均、高、非常高)/(VL、L、A、H、VH)。然后马尔可夫链用状态VL,L,L,A,A,H,H,VH表示。然后,我们可以找到任何新序列发生的概率,然后将稀有序列标记为异常。

总结

目前使用5种方法进行了异常检测,但是由于是无监督学习,所以不能评估到底哪种方法更合适。需要结合具体的业务场景去分析。从上面的处理过程,个人觉得比较好的是基于高斯分布的异常检测。

参考链接:

微信支付标点符 wechat qrcode
支付宝标点符 alipay qrcode

Python检验数据是否正态分布

判断数据是否符合正态分布,比如使用3-sigma判断数据异常前,首先需要确定的是数据是否符合正态分布。今天一起

数据探索Pandas-Profiling与Dataprep.…

在使用数据前,我们首先要做的事观察数据,包括查看数据的类型、数据的范围、数据的分布等。Pandas-Profi

开源指标可视化工具Graphite

Graphite 是处理可视化和指标数据的优秀开源工具。它有强大的查询 API 和相当丰富的插件功能设置。事实

发表评论

电子邮件地址不会被公开。 必填项已用*标注