精华内容
下载资源
问答
  • 【Python实战】单变量异常值检测
    2020-11-22 18:45:45

    作者: eavea 发表日期: 2020年04月14日 分类: 后端技术

    标签: Python , 数据处理

    阅读次数: 6,076

    评论数: 0 条

    【Python实战】单变量异常值检测

    异常值检测是数据预处理阶段重要的环节,这篇文章介绍下对于单变量异常值检测的常用方法,通过Python代码实现。

    一、什么是异常值

    异常值是在数据集中与其他观察值有很大差距的数据点,它的存在,会对随后的计算结果产生不适当的影响,因此检测异常值并加以适当的处理是十分必要的。

    二、异常值的处理

    异常值并不都是坏的,了解这一点非常重要。只是简单地从数据中删除异常值,而不考虑它们如何影响结果的话,可能会导致灾难。

    “异常值不一定是坏事。这些只是与其他模式不一致的观察。但事实上异常值非常有趣。例如,如果在生物实验中,某只老鼠没有死亡而其他老鼠都死了,去了解为什么将会非常有趣。这可能会带来新的科学发现。因此,检测异常值非常重要。” —— Pierre Lafaye de Micheaux,统计师

    对于异常值,一般有如下几种处理:

    删除含有异常值的记录(是否删除根据实际情况考虑)

    将异常值视为缺失值,利用缺失值的处理方法进行处理

    平均值修正(前后两个观测值的平均值)

    不处理(直接在具有异常值的数据集上进行挖掘)

    三、异常值的类型

    异常值有两种类型:单变量和多变量(Univariate and Multivariate)。单变量异常值是仅由一个变量中的极值组成的数据点,而多变量异常值是至少两个变量的组合异常分数。假设您有三个不同的变量 – X,Y,Z。如果您在三维空间中绘制这些变量的图形,它们应该形成一种云。位于此云之外的所有数据点都将是多变量异常值。

    举个例子:做客户分析,发现客户的年平均收入是80万美元。但是,有两个客户的年收入是4美元和420万美元。这两个客户的年收入明显不同于其他人,那这两个观察结果将被视为异常值,并且是单变量异常值,当我们看到单变量的分布时,可以找到这些异常值。

    再举个例子:身高和体重之间的关系。我们对“身高”和“体重”有单变量和双变量分布,如下图所示。

    height_weight_outliers.png

    看箱线图(box plot后面会介绍)没有任何异常值,再看散点图(scatter plot),有两个值在一个特定的身高和体重的平均值以下。可见多变量异常值是n维空间中的异常值,必须通过多维度的分布才能体现出来。

    如果对异常值不太了解,可以阅读这篇《数据探索指南》,上述部分解释也是从中摘录的。

    下面,我主要记录下单变量异常值检测的Python实现。

    四、常用异常检测方法

    原则上模拟数据集需要样本量足够大,这里仅是演示算法,所以就手动写了有限的样本。

    异常值的测量标准有很多,比较常见的是描述性统计法、三西格玛法(3σ法)、箱线图等:

    1. 描述性统计

    基于常识或经验,假定我们认为大于10的数值是不符合常理的。

    下面用Python代码实现用描述性统计求异常值:

    # -*- coding: utf-8 -*-

    data = [2.78, 1.79, 4.73, 3.81, 2.78, 1.80, 4.81, 2.79, 1.78, 3.32, 10.8, 100.0]

    threshold = 10

    # 定义描述性统计识别异常值函数

    def descriptive_statistics(data):

    return list(filter(lambda x: x > threshold, data))

    outliers = descriptive_statistics(data)

    print('异常值共有:{0}个,分别是:{1}'.format(len(outliers), outliers))

    # 输出:异常值共有:2个,分别是:[10.8, 100.0]

    2. 三西格玛(3σ)

    当数据服从正态分布时,99%的数值应该位于距离均值3个标准差之内的距离,P(|x−μ|>3σ)≤0.003,当数值超出这个距离,可以认为它是异常值。

    正态分布状况下,数值分布表:

    数值分布

    在数据中的占比

    (μ-σ,μ+σ)

    0.6827

    (μ-2σ,μ+2σ)

    0.9545

    (μ-3σ,μ+3σ)

    0.9973

    注:在正态分布中σ代表标准差,μ代表均值,x=μ为图形的对称轴

    下面用Python代码实现用三西格玛求异常值:

    # -*- coding: utf-8 -*-

    import pandas as pd

    import numpy as np

    data = [2.78, 1.79, 4.73, 3.81, 2.78, 1.80, 4.81, 2.79, 1.78, 3.32, 10.8, 100.0]

    # 定义3σ法则识别异常值函数

    def three_sigma(data_series):

    rule = (data_series.mean() - 3 * data_series.std() > data_series) | (data_series.mean() + 3 * data_series.std() < data_series)

    index = np.arange(data_series.shape[0])[rule]

    outliers = data_series.iloc[index]

    return outliers.tolist()

    data_series = pd.Series(data)

    outliers = three_sigma(data_series)

    print('异常值共有:{0}个,分别是:{1}'.format(len(outliers), outliers))

    # 输出:异常值共有:1个,分别是:[100.0]

    3. 箱线图(box plot)

    和3σ原则相比,箱线图依据实际数据绘制,真实、直观地表现出了数据分布的本来面貌,且没有对数据作任何限制性要求(3σ原则要求数据服从正态分布或近似服从正态分布)。

    其判断异常值的标准以四分位数和四分位距为基础。四分位数给出了数据分布的中心、散布和形状的某种指示,具有一定的鲁棒性,即25%的数据可以变得任意远而不会很大地扰动四分位数,所以异常值通常不能对这个标准施加影响。鉴于此,箱线图识别异常值的结果比较客观,因此在识别异常值方面具有一定的优越性。

    箱线图提供了识别异常值的一个标准,即:

    上界 = Q3 + 1.5IQR

    下界 = Q1 – 1.5IQR

    小于下界或大于上界的值即为异常值。

    其中,

    Q3称为上四分位数(75%),表示全部观察值中只有四分之一的数据取值比它大;

    Q1称为下四分位数(25%),表示全部观察值中只有四分之一的数据取值比它小;

    IQR称为四分位数差,这里就是 Q3-Q1;

    1.5其实是个参数λ,这个参数通常取1.5(类似于正态分布中的μ±λ)

    文字描述可能比较绕,下面用图片来解释下。

    box_plot.png

    第一四分位数 (Q1),又称“较小四分位数”,等于该样本中所有数值由小到大排列后第25%的数字。

    第二四分位数 (Q2),又称“中位数”,等于该样本中所有数值由小到大排列后第50%的数字。

    第三四分位数 (Q3),又称“较大四分位数”,等于该样本中所有数值由小到大排列后第75%的数字。

    Q3与Q1的差距又称四分位距(InterQuartile Range,IQR)。

    下面用Python代码实现用箱线图求异常值:

    # -*- coding: utf-8 -*-

    import pandas as pd

    data = [2.78, 1.79, 4.73, 3.81, 2.78, 1.80, 4.81, 2.79, 1.78, 3.32, 10.8, 100.0]

    # 定义箱线图识别异常值函数

    def box_plot(data_series):

    q_abnormal_low = data_series.quantile(0.25) - 1.5 * (data_series.quantile(0.75) - data_series.quantile(0.25))

    q_abnormal_up = data_series.quantile(0.75) + 1.5 * (data_series.quantile(0.75) - data_series.quantile(0.25))

    index = (data_series < q_abnormal_low) | (data_series > q_abnormal_up)

    outliers = data_series.loc[index]

    return outliers.tolist()

    sorted_data = sorted(data)

    data_series = pd.Series(sorted_data)

    outliers = box_plot(data_series)

    print('异常值共有:{0}个,分别是:{1}'.format(len(outliers), outliers))

    # 输出:异常值共有:2个,分别是:[10.8, 100.0]

    五、总结

    更多相关内容
  • MATLAB源码集锦-RPCA异常值检测代码
  • 一个名为 DDOutlier [4] 的 R 包包含许多基于密度的异常值检测算法。 我在寻找复杂的异常值检测方法时偶然发现了这个包。 它与相关文件一起证明了代码,这正是我需要的。 然后,我开始在 MATLAB 中找到类似的包。 ...
  • 这是题为“文本数据的异常值检测”的论文的 matlab 实现 生成一个标记数据集,其中 0 为非异常值,1 为异常值。 使用稀疏词矩阵、低秩 k、参数 alpha 和 beta 调用 textoutliers 函数。 它将返回三个矩阵 Z,W,H。 ...
  • matlab开发-异常值检测和移动。在非线性时间序列中用适当的局部值检测和替换异常值。
  • PyOD - 用于异常值检测的Python工具包(也称为异常检测)
  • 今天小编就为大家分享一篇Python实现非正太分布的异常值检测方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • PyOD - 用于异常值检测的Python工具包(也称为异常检测) Python 异常值检测 (PyOD) 部署、文档和统计信息 构建状态、覆盖率、可维护性和许可证 PyOD 是一个全面且可扩展的 Python 工具包,用于检测多元数据中的外围...
  • 总结了异常值检测的相关课程视频、书籍、论文、数据集以及工具库
  • 异常值检测

    千次阅读 2021-04-30 06:03:16
    数据处理过程中,通常需要对数据进行预处理,包括缺失值填充,异常值检测等。异常值处理对于后续数据分析,建模具有非常很重要的影响。基于描述统计1.基于常识判断针对数据进行简单的描述统计,查看数据的极大值和极...

    数据处理过程中,通常需要对数据进行预处理,包括缺失值填充,异常值检测等。异常值处理对于后续数据分析,建模具有非常很重要的影响。

    基于描述统计

    1.基于常识判断

    针对数据进行简单的描述统计,查看数据的极大值和极小值。判断数据是否处于合理的范围。进行数据筛选,筛选掉明显不符范围的数据。

    2.基于3倍标准差进行筛选

    如果数据整体成正态分布,可以根据常识的3σ原则进行异常值筛选。对于标准正态分布来说,(μ-3σ,μ+3σ)之间的面积为99.730020%。统计学中小概率事件为5%,则可以初略的认为处于数据3σ之外的数据为异常数据。不过在具体场景中还需要具体的讨论。

    e2f77a220a8f

    正态分布.jpg(图片来源于网络,侵删)

    3.box-cox数据转化+3σ准则

    3σ准则假设是数据整体呈正态分布。但是如果数据是有偏数据,则需要转化后再进行筛选,box-cox数据转化在前文已有描述box-cox数据转化。不再赘述。

    4.基于四分位进行筛选

    我们在进行箱线图描述时,通常都会绘制出上下四分位线以及处于四分位线外的离群点。通常认为位于4分位线外的点为异常值。位于上下四分位外的为异常值。

    e2f77a220a8f

    boxplot.png

    基于机器学习的异常值检测方法

    1.孤立森林算法

    对于孤立森林的个人理解。孤立森林算法是基于决策树的算法。在我们的一堆数据中,如果以散点图的形式,可以将每个点进行分类。对于数据中大多数点来说,大多密集的数据区分要达到的树的深度基本都是相近的,而对于异常点,应该是处于密集点之外的离群点,则区分该数据的深度应该是小于平均的树的深度。以下为我个人数据处理的代码

    def Anomaly_detect(self):

    '''

    :param self:

    :param datamatrix:

    '''

    rng = np.random.RandomState(42)

    clf = IsolationForest(max_samples=len(self.data.columns), random_state=rng, behaviour="new",contamination='auto')

    X_train = np.array(self.data).T

    clf.fit(X_train)

    y_pred = clf.predict(X_train)

    x_left = X_train[y_pred == 1]

    x_sample = []

    for x in range(len(y_pred)):

    if y_pred[x] == 1:

    x_sample.append(self.data.columns[x])

    self.data = pd.DataFrame(x_left.T,columns = x_sample ,index = self.data.index)

    最终结果大致如图

    e2f77a220a8f

    IsolationForest.jpg(来源于网络,侵删)

    2.one-class svm

    未完待续

    展开全文
  • 异常值检测常用算法及案例

    千次阅读 2022-03-08 21:32:01
    异常值检测常用方法 对历史数据进行异常值检测,对突发情况或者异常情况进行识别,避免因为异常值导致预测性能降低,并对其进行调整便于后续预测。 一、3-sigma原则异常值检测 3-Sigma原则又称为拉依达准则,该准则...

    异常值检测常用方法

    对历史数据进行异常值检测,对突发情况或者异常情况进行识别,避免因为异常值导致预测性能降低,并对其进行调整便于后续预测。

    一、3-sigma原则异常值检测

    3-Sigma原则又称为拉依达准则,该准则定义如下:假设一组检测数据只含有随机误差,对原始数据进行计算处理得到标准差,然后按一定的概率确定一个区间,认为误差超过这个区间的就属于异常值。如果数据服从正态分布,异常值被定义为一组测定值中与平均值的偏差超过3倍的值 → p(|x - μ| > 3σ) ≤ 0.003。
    在这里插入图片描述
    样例如下:
    在这里插入图片描述

    对于波动较为规律性的部分数据,异常值往往即最大值、最小值,对这种情况只需取规则上限进行修匀。
    在这里插入图片描述

    对于长期为0,偶尔存在较高业务量的情形,3-sigma原则能够较好地识别较极端的部分,对于较小的部分会进行保留,不会将所有非0值剔除,比较符合实际场景。
    核心函数:
    sample_data中仅需要包含一个字段:总流量

    每行代表一个时间点,将会对总流量这一列进行异常值检测,并得到一个标签。

    def three_sigma(sample_data):
        """
        3-sigma法则异常值判定
        """
        data_mean = np.array(sample_data['总流量'].tolist()).mean()  # 计算均值
        data_std = np.array(sample_data['总流量'].tolist()).std()  # 计算标准差
        data_max = data_mean + 3 * data_std
        data_min = data_mean - 3 * data_std
        sample_data['three_sigma'] = 0
        sample_data.loc[(sample_data['总流量'] > data_max) | (sample_data['总流量'] < data_min), 'three_sigma'] = 1
        length = len(sample_data[sample_data['three_sigma']==1])  # 3-sigma方法的异常值数量
        if length!=0:
            print('3-sigma方法的异常值数量:{}'.format(length))
        return sample_data
    

    二、箱线法异常值检测

    箱形图可以用来观察数据整体的分布情况,利用中位数,25/%分位数,75/%分位数,上边界,下边界等统计量来来描述数据的整体分布情况。通过计算这些统计量,生成一个箱体图,箱体包含了大部分的正常数据,而在箱体上边界和下边界之外的,就是异常数据。

    在这里插入图片描述

    检测样例如下:

    在这里插入图片描述

    对于曲线波动较平稳且存在一定规律性的小区,检测效果与3-sigma相似,都集中在极值附近。
    在这里插入图片描述

    对于较极端情形,箱线法将所有值都判定为异常值,不符合常理。
    核心代码:

    def box_test(sample_data):
        """
        箱线法
        """
        Q1 = np.percentile(sample_data['总流量'], 25)  # 计算1/4分位数
        Q3 = np.percentile(sample_data['总流量'], 75)  # 计算3/4分位数
        IQR = Q3 - Q1
        outlier_step = 1.5 * IQR
        min_limit_box = Q1 - outlier_step
        max_limit_box = Q3 + outlier_step
        print('正常值范围是:'+str(min_limit_box)+'---'+str(max_limit_box))
        sample_data['box_test'] = 0
        sample_data.loc[(sample_data['总流量'] > max_limit_box) | (sample_data['总流量'] < min_limit_box), 'box_test'] = 1
        length = len(sample_data[sample_data['three_sigma']==1])  # 3-sigma方法的异常值数量
        if length!=0:
            print('箱线法得到异常值数量:{}'.format(length))
        return sample_data
    

    三、adtk异常值检测

    智能运维AIOps的数据基本上都是时间序列形式的,而异常检测告警是AIOps中重要组成部分,异常值检测在智能运维中应用十分广泛。
    同时对于电力负荷、业务量预测而言也可以借鉴相应方法,提升数据质量。

    adtk包主要包含:

    • 简单有效的异常检测算法(detector)
    • 异常特征加工(transformers)
    • 处理流程控制(Pipe)

    1. adtk数据要求

    时间序列的数据主要包括时间和相应的指标(如cpu,内存,数量,电力负荷、业务量等)。
    python中数据分析一般都是pandas的DataFrame,adtk要求输入数据的索引必须是DatetimeIndex

    pandas提供了时间序列的时间生成和处理方法。

    • pd.to_datetime

    也可以用处理脚本进行转换:

        time_data = sample_data['总流量']
        time_index = pd.to_datetime(sample_data['time'])
        time_data.index = time_index
    

    adtk提供是validate_series来验证时间序列数据的有效性,如是否按时间顺序

    time_data = validate_series(time_data)
    

    在这里插入图片描述

    2. 异常特征加工(transformers)

    adtk中transformers提供了许多时间序列特征加工的方法:

    • 一般我们获取时间序列的特征,通常会按照时间窗口在滑动,采集时间窗口上的统计特征;
    • 还有对于季节性趋势做分解,区分哪些是季节性的部分,哪些是趋势的部分
    • 时间序列降维映射:对于细粒度的时间序列数据,数据量大,对于检测算法来说效率不高。降维方法能保留时间序列的主要趋势等特征同时,降低维数,提供时间效率。这个对于用CNN的方式来进行时间序列分类特别有效,adtk主要提供基于pca的降维和重构方法,主要应用于多维时间序列。

    3. 异常检测算法(detector)

    adtk提供的主要是无监督或者基于规则的时间序列检测算法,可以用于常规的异常检测。

    • ThresholdAD 人为定阈值
      adtk.detector.ThresholdAD(low=None, high=None)
      参数:
      low:下限,小于此值,视为异常
      high:上限,大于此值,视为异常
      原理:通过认为设定上下限来识别异常
      总结:固定阈值算法
      
      from adtk.detector import ThresholdAD
      threshold_ad = ThresholdAD(high=30, low=15)
      anomalies = threshold_ad.detect(time_data)
      

    img

    • GeneralizedESDTestAD 正态检验法

    在现实数据集中,异常值往往是多个而非单个。为了将Grubbs’ Test扩展到k个异常值检测,

    则需要在数据集中逐步删除与均值偏离最大的值(为最大值或最小值),同步更新对应的t分布临界值,检验原假设是否成立。

    GESD是一种简单的统计方法,用于检测遵循近似正态分布的单变量数据集中的一个或多个异常值。

    统计方法假设常规数据遵循某种统计模型(或分布),而不遵循模型(或分布)的数据则是异常值。

    GeneralizedESDTestAD
    adtk.detector.GeneralizedESDTestAD(alpha=0.05)
    参数:
    alpha:显著性水平 (Significance level),alpha越小,表示识别出的异常越有把握是真异常
    原理:将样本点的值与样本的均值作差后除以样本标准差,取最大值,通过t分布计算阈值,对比阈值确定异常点
    计算步骤简述:
    设置显著水平alpha,通常取0.05
    指定离群比例h,若h=5%,则表示50各样本中存在离群点数为2
    计算数据集的均值mu与标准差sigma,将所有样本与均值作差,取绝对值,再除以标准差,找出最大值,得到esd_1
    在剩下的样本点中,重复步骤3,可以得到h个esd值
    为每个esd值计算critical value: lambda_i (采用t分布计算)
    统计每个esd是否大于lambda_i,大于的认为你是异常
    
    from adtk.detector import GeneralizedESDTestAD
    esd_ad = GeneralizedESDTestAD(alpha=0.3)
    anomalies = esd_ad.fit_detect(time_data)
    

    在这里插入图片描述

    • 检测突变的情形

    突变: Spike and Level Shift 异常的表现形式不是离群点,而是通过和临近点的比较,即突增或者突降。adtk提供adtk.detector.PersistADadtk.detector.LevelShiftAD检测方法
    在这里插入图片描述
    可以并排滑动两个时间窗口,并继续跟踪它们的平均值或中值之间的差异。这种随时间的差异是一个新的时间序列,由异常值检测器检查。每当左右窗口中的统计数据显着不同时,就表明在这个时间点附近发生了突变。时间窗口长度控制检测变化的时间尺度:对于尖峰,左侧窗口比右侧窗口长,以捕获近期的代表性信息;另一方面,对于电平转换,两个窗口都应该足够长以捕捉稳定状态。

    • PersistAD 突变检测

    PersistAD将每个时序值与其以前的值进行比较。在内部,它被实现为带有变压器DoubleRollingAggregate的管道网络。

    adtk.detector.PersistAD(window=1, c=3.0, side='both', min_periods=None, agg='median')
    

    参数:
    window:参考窗长度,可为int, str
    c:分位距倍数,用于确定上下限范围
    side:检测范围,为’positive’时检测突增,为’negative’时检测突降,为’both’时突增突降都检测
    min_periods:参考窗中最小个数,小于此个数将会报异常,默认为None,表示每个时间点都得有值
    agg:参考窗中的统计量计算方式,因为当前值是与参考窗中产生的统计量作比较,所以得将参考窗中的数据计算成统计量,默认’median’,表示去参考窗的中位值
    原理:
    用滑动窗口遍历历史数据,将窗口后的一位数据与参考窗中的统计量做差,得到一个新的时间序列s1;

    from adtk.detector import PersistAD
    persist_ad = PersistAD(c=3.0, side='positive')
    anomalies = persist_ad.fit_detect(s)
    

    img

    • LevelShiftAD 突变检测法

    LevelShiftAD通过跟踪两个相邻滑动时间窗口的中位数值之间的差异来检测值水平的偏移。

    它对瞬时峰值不敏感,如果经常发生嘈杂的异常值,它可能是一个不错的选择。在内部,它被实现为带有变压器DoubleRollingAggregate的管道网络。

    LevelShiftAD
    adtk.detector.LevelShiftAD(window, c=6.0, side='both', min_periods=None)
    参数:
    window:支持(10,5),表示使用两个相邻的滑动窗,左侧的窗中的中位值表示参考值,右侧窗中的中位值表示当前值
    c:越大,对于波动大的数据,正常范围放大较大,对于波动较小的数据,正常范围放大较小,默认6.0
    side:检测范围,为'positive'时检测突增,为'negative'时检测突降,为'both'时突增突降都检测
    min_periods:参考窗中最小个数,小于此个数将会报异常,默认为None,表示每个时间点都得有值
    原理:
    该模型用于检测突变情况,相比于PersistAD,其抗抖动能力较强,不容易出现误报
    from adtk.detector import LevelShiftAD
    level_shift_ad = LevelShiftAD(c=6.0, side='both', window=5)
    anomalies = level_shift_ad.fit_detect(time_data)
    

    img

    • VolatilityShiftAD

    VolatilityShiftAD通过跟踪两个相邻滑动时间窗口下标准偏差之间的差异来检测波动率水平的变化。在内部,它被实现为带有变压器DoubleRollingAggregate的管道网络。

    在下面的例子中,我们检测到地震振幅波动性的正向偏移,这表明地震的开始。

    
    from adtk.detector import VolatilityShiftAD
    volatility_shift_ad = VolatilityShiftAD(c=6.0, side='positive', window=30)
    anomalies = volatility_shift_ad.fit_detect(time_data)
    plot(time_data, anomaly=anomalies, anomaly_color='red');
    

    在这里插入图片描述

    四. 总结

    时间序列异常检测的无监督算法工具包ADTK提供了简单的异常检测算法和时间序列特征加工函数。

    • adtk要求输入数据为datetimeIndex,validate_series来验证数据有效性,使得时间有序
    • adtk单窗口和double窗口滑动,加工统计特征
    • adtk分解时间序列的季节部分,获得时间序列的残差部分,可根据这个判断异常点
    • adtk支持离群点、突变和季节性异常检测。通过fit_detect 获取异常点序列,也可以通过Pipeline联通多部异常检测算法
    展开全文
  • 具有统计参数的强大局部异常值检测可用于大数据
  • 仅依据瓦斯浓度阈值指标不能有效反映危险程度的演化过程,进行预警存在一定风险,为提升瓦斯浓度异常值检测的准确性,提出了一种基于逻辑回归模型的瓦斯浓度异常值检测与预警新方法。建立瓦斯数据检测逻辑回归模型后...
  • 基于深度学习的多波束海底地质数据异常值检测方法.pdf
  • 使用PyOD来进行异常值检测

    千次阅读 2021-07-25 11:29:42
    异常值检测主要是为了发现数据集中的一些"与众不同"的数据值,所谓“与众不同”的数据值是指这些数据与大多数数据存在较大的差异我们称之为“异常值”,并且在现实中这些“异常值”并没有被打上标签,因此我们必须...

    异常值检测主要是为了发现数据集中的一些"与众不同"的数据值,所谓“与众不同”的数据值是指这些数据与大多数数据存在较大的差异我们称之为“异常值”,并且在现实中这些“异常值”并没有被打上标签,因此我们必须通过某种算法来自动识别出这些异常值。对于异常值我们有如下的定义:

    1. 异常值所占整体数据的比例较少,产生异常值的概率非常低。
    2. 异常值本身的特征与其他正常值有明显的差异。

    数据

    在本篇博客中我们的数据来自于国外某连锁零售企业的客流量与订单数量统计数据,为了让数据更加清晰,我们只保留了如下3个字段:

    1. date:日期,
    2. num_people: 客流量,
    3. num_order: 订单数量量

    你可以在这里下载数据(点击下载这两个字)

    import pandas as pd
    import numpy as np
    from numpy import percentile
    import matplotlib
    import matplotlib.pyplot as plt
    import seaborn as sns
    sns.set_theme();
    sns.set_style("darkgrid",{"font.sans-serif":['simhei','Droid Sans Fallback']})
    from sklearn.ensemble import IsolationForest
    from sklearn.preprocessing import MinMaxScaler
    
    from pyod.models.abod import ABOD
    from pyod.models.cblof import CBLOF
    from pyod.models.feature_bagging import FeatureBagging
    from pyod.models.hbos import HBOS
    from pyod.models.iforest import IForest
    from pyod.models.knn import KNN
    from pyod.models.lof import LOF
    from pyod.models.mcd import MCD
    from pyod.models.ocsvm import OCSVM
    from pyod.models.pca import PCA
    from pyod.models.lscp import LSCP
    import warnings
    warnings.filterwarnings("ignore")
    df=pd.read_csv("order_num.csv")
    df

    过滤缺失值 

    除异常值以外,真实的数据中往往还会存在数据缺失的情况(即有些数据的值为NaN),对于缺失值一般会有2种处理方式比如:1.直接删除缺失值;2.对缺失值进行填充.对缺失值进行填充时一般会用均值或者使用某些插值算法插入一些符合数据历史趋势的数值。在这里我们使用最简单的方式即直接删除缺失值的方式来处理数据缺失,这样做的目的是让问题简单化。

    print("在 num_people 列中总共有 %d 个空值." % df['num_people'].isnull().sum())
    print("在 num_order 列中总共有 %d 个空值." % df['num_order'].isnull().sum())
    df=df[~df.num_people.isnull()==True]
    df=df[~df.num_order.isnull()==True]
    print("删除缺失值以后记录总数:",len(df))

     计算客流量、订单量数据分布

    print(df.num_people.describe())
    print()
    print(df.num_order.describe())
    plt.figure(figsize=(15,8), dpi=80)
    plt.subplot(221)
    sns.distplot(df['num_people']);
    
    plt.subplot(222)
    sns.distplot(df['num_order']);

     

     ​​​​​

     从分布上看客流量(num_people)明显严重右偏,右侧有长尾,并且我们看到客流量(num_people)的异常区域大致应该分布在上图的红圈范围内。而订单量数据呈现正太分布,异常值区域位于分布左右两侧。下面我们查看num_people的偏度(Skewness)与峰度(Kurtosis),偏度反应的是分布的偏斜程度,可能是左偏,右偏,长尾等形态,峰度反应分布的形状的胖瘦(宽窄),具体解释请参考这篇博客: https://blog.csdn.net/binbigdata/article/details/79897160

    print("num_people's Skewness: %f" % df['num_people'].skew())
    print("num_people's Kurtosis: %f " % df['num_people'].kurt())
    print()
    print("num_order's Skewness: %f" % df['num_order'].skew())
    print("num_order's Kurtosis: %f" % df['num_order'].kurt())

     

    IsolationForest(隔离森林)

    IsolationForest是一种简单有效的检测异常值的算法,它可以在数据的分布区域中找出异常值所在的区域,并对所有数据进行评分,那些落在异常区域的数据值会获得较低的分数,而那些不在异常区域中的数据将会获得较高的分数,大家可以参考这篇文章(https://dzone.com/articles/spotting-outliers-with-isolation-forest-using-skle )。在这篇文章中作者随机生成了两个正太分布N(-2,5)和N(2,5),同时通过隔离森林算法找到到了这两个分布中的异常区域,并且生成一条评分曲线,落在异常区域内的数据将会得到低分,落在异常区域内以外的数据将会获得高分:

    import numpy as np 
    import matplotlib.pyplot as plt 
    x = np.concatenate((np.random.normal(loc=-2, scale=.5,size=500), np.random.normal(loc=2, scale=.5, size=500)))
    
    isolation_forest = IsolationForest(n_estimators=100) 
    isolation_forest.fit(x.reshape(-1, 1)) 
    xx = np.linspace(-6, 6, 100).reshape(-1,1) 
    anomaly_score = isolation_forest.decision_function(xx) 
    outlier = isolation_forest.predict(xx)
    
    plt.figure(figsize=(10,8))
    plt.subplot(2,1,1)
    plt.hist(x) 
    plt.xlim([-5, 5]) 
    
    plt.subplot(2,1,2)
    plt.plot(xx, anomaly_score, label='异常值分数') 
    plt.fill_between(xx.T[0], np.min(anomaly_score), np.max(anomaly_score), where=outlier==-1, color='r', alpha=.4, label='异常值区域') 
    plt.legend() 
    plt.ylabel('异常值分数') 
    plt.xlabel('x') 
    plt.xlim([-5, 5]) 
    plt.show()

     

    使用隔离森林算法来监测客流量,订单量的异常值区域

    隔离森林是一种检测异常值的算法,使用IsolationForest算法返回每个样本的异常分数,该算法基于这样的思想:异常是少数和在特征上不同的数据点。隔离森林是一种基于树的模型。在这些树中,通过首先随机选择特征然后在所选特征的最小值和最大值之间选择随机分割值来创建分区。下面我们使用隔离森林算法来探测客流量与订单量的异常值区域,并生成评分曲线:

    #定义隔离森林
    IF1 = IsolationForest(n_estimators=100)
    #训练客流量数据
    IF1.fit(df['num_people'].values.reshape(-1, 1))
    #在客流量的最小值和最大值之间分割数据
    x1 = np.linspace(df['num_people'].min(), df['num_people'].max(), len(df)).reshape(-1,1)
    #生成所有数据的异常值分数
    anomaly_score1 = IF1.decision_function(x1)
    #预测异常值
    outlier1 = IF1.predict(x1)
    
    IF2 = IsolationForest(n_estimators=100)
    #训练订单量数据
    IF2.fit(df['num_order'].values.reshape(-1, 1))
    #在订单量的最小值和最大值之间分割数据
    x2 = np.linspace(df['num_order'].min(), df['num_order'].max(), len(df)).reshape(-1,1)
    #生成所有数据的异常值分数
    anomaly_score2 = IF2.decision_function(x2)
    #预测异常值
    outlier2 = IF2.predict(x2)
    
    plt.figure(figsize=(18,8))
    plt.subplot(2,2,1)
    sns.distplot(df['num_people'])
    
    plt.subplot(2,2,2)
    sns.distplot(df['num_order'])
    
    plt.subplot(2,2,3)
    plt.plot(x1, anomaly_score1, label='异常值分数')
    plt.fill_between(x1.T[0], np.min(anomaly_score1), np.max(anomaly_score1), 
                     where=outlier1==-1, color='r', 
                     alpha=.4, label='异常值区域')
    plt.legend()
    plt.ylabel('异常值分数')
    plt.xlabel('客流量(num_people)')
    
    plt.subplot(2,2,4)
    plt.plot(x2, anomaly_score2, label='异常值分数')
    plt.fill_between(x2.T[0], np.min(anomaly_score2), np.max(anomaly_score2), 
                     where=outlier2==-1, color='r', 
                     alpha=.4, label='异常值区域')
    plt.legend()
    plt.ylabel('异常值分数')
    plt.xlabel('订单量(num_order)')
    plt.show();

     

     

    在上图中隔离森林算法轻松的探测出来了客流量(num_people)与订单量(num_order)的异常值区域,并生成了评分曲线。当数据落在红色矩形区域内时将会得到较低的分数,而当数据落在红色矩形框的范围之外将会得到较高的分数。下面我们计算每个分布的异常值区域的边界值(上图红圈中的值)

    x1_outlier = x1[outlier1==-1]
    right_min=x1_outlier[x1_outlier>1000].min()
    left_max = x1_outlier[x1_outlier<1000].max()
    print('客流量异常值区域中左侧最大异常值为:',df[df.num_people<=left_max].num_people.max())
    print('客流量异常值区域中右侧最小异常值为:',df[df.num_people>=right_min].num_people.min())
    print()
    x2_outlier = x2[outlier2==-1]
    right_min=x2_outlier[x2_outlier>248].min()
    left_max = x2_outlier[x2_outlier<248].max()
    print('订单量异常值区域中左侧最大异常值为:',df[df.num_order<=left_max].num_order.max())
    print('订单量异常值区域中右侧最小异常值为:',df[df.num_order>=right_min].num_order.min())
    

     

    我们分别计算了客流量和订单量的异常值区域(红色区域)的边界值:
    客流量异常值所在区域:x<=459 和 x>=1508
    订单量异常值所在区域:x<=156 和 x>=357

    上述两个可视化结果显示了异常值分数并突出显示异常值所在的区域。从图中可见异常分数反映了基础分布的形状,异常区域对应于低概率区域。然而,到目前为止我们只是对客流量和订单量这两个单一变量分别进行了分析。如果我们仔细研究,可能会发现,由我们的模型确定的一些异常值只不过是数学统计上的异常,它可能并非是我们业务场景中的异常值,比如某些时候订单量很高可能是由于客流量很高所导致,它可能在统计分布上是异常值,但它在实际的业务场景中并不应该是异常值。下面我们同时观察客流量(num_people)和订单量(num_order)这两个变量的散点分布,并对客流量和订单量进行线性拟合,那些严重偏离拟合曲线的点,可以被认为是异常值,以这样的方式来判定异常值更符合实际的业务场景。

    plt.figure(figsize=(10,6), dpi=80)
    sns.regplot(data=df,x="num_people", y="num_order");

     

     当我们的数据不是单变量而是多维度变量时,异常检测的方法使得计算难道更高并且在数学上更复杂。

    关于PyOD

    PyOD 是一个全面且可扩展的Python 工具包,用于检测多维数据中的异常对象。这个令人兴奋但具有挑战性的领域通常被称为 异常值检测 或异常检测。

    PyOD 包括 30 多种检测算法,从经典的 LOF (SIGMOD 2000) 到最新的 COPOD (ICDM 2020)。自 2017 年以来,PyOD [ AZNL19 ]已成功应用于众多学术研究和商业产品[ AGSW19 , ALCJ+19 , AWDL+19 , AZNHL19 ]。它也得到了机器学习社区的广泛认可,有各种专门的帖子/教程,包括 Analytics Vidhya、 Towards Data Science、 KDnuggets、 Computer Vision News和 awesome-machine-learning。

    PyOD官方文档:https://pyod.readthedocs.io/en/latest/index.html

    本例中将会使用以下种类的异常探测模型:

    1.异常探测的线性模型

    PCA:主成分分析使用到特征向量超平面的加权投影距离之和作为异常值异常值)
    MCD:最小协方差行列式(使用马氏距离作为异常值)
    OCSVM:一类支持向量机

    2.基于邻近度的异常值检测模型

    LOF:局部异常因子
    CBLOF:基于聚类的局部异常值因子
    kNN:k Nearest Neighbors(使用到第k个最近邻居的距离作为异常值)
    Median kNN 异常值检测(使用到 k 个最近邻居的中值距离作为异常值分数)
    HBOS:基于直方图的异常值分数

    3.异常值检测的概率模型

    ABOD:基于角度的异常值检测

    4.使用集成分类(回归)器的异常检查模型

    Isolation Forest:隔离森林
    Feature Bagging:特征装袋
    LSCP

    使用PyOD 进行异常检测步骤

    • 数据缩放:将客流量和订单量标准化处理将其缩放到0到1之间。
    • 设置异常值比例: 根据经验将设置异常值比例设置为1%。
    • 初始化异常检查模型: 初始化12种异常检测模型。
    • 拟合数据:使用异常检测模型拟合数据并预测结果。
    • 判断异常值:使用阈值来判断数据点是正常值还是异常值。
    • 计算异常值分数:使用决策函数计算每个点的异常值分数。

    下面的代码参考了"比较所有已实现的离群值检测模型的例子"(https://github.com/yzhao062/pyod/blob/master/notebooks/Compare%20All%20Models.ipynb) 和"使用PyOD库在Python中学习异常检测的一个很棒的教程"(https://www.analyticsvidhya.com/blog/2019/02/outlier-detection-python-pyod/) 这两篇文章。

    对数据进行缩放

    将客流量和订单量标准化处理将其缩放到0到1之间

    #数据缩放
    cols = ['num_people', 'num_order']
    minmax = MinMaxScaler(feature_range=(0, 1))
    print(df[cols].head())
    print('--------------------------')
    df[['num_people','num_order']] = minmax.fit_transform(df[cols])
    print(df[cols].head())

    初始化异常值检测模型

    在这里我们将初始化常见的12种异常检测模型

    #设定异常值比例
    outliers_fraction = 0.01
    
    # 初始化 LSCP 探测集
    detector_list = [LOF(n_neighbors=5), LOF(n_neighbors=10), LOF(n_neighbors=15),
                     LOF(n_neighbors=20), LOF(n_neighbors=25), LOF(n_neighbors=30),
                     LOF(n_neighbors=35), LOF(n_neighbors=40), LOF(n_neighbors=45),
                     LOF(n_neighbors=50)]
    classifiers = {
        'Angle-based Outlier Detector (ABOD)':
            ABOD(contamination=outliers_fraction),
        'Cluster-based Local Outlier Factor (CBLOF)':
            CBLOF(contamination=outliers_fraction,
                  check_estimator=False, random_state=0),
        'Feature Bagging':
            FeatureBagging(LOF(n_neighbors=35),
                           contamination=outliers_fraction,
                           random_state=0),
        'Histogram-base Outlier Detection (HBOS)': HBOS(
            contamination=outliers_fraction),
        'Isolation Forest': IForest(contamination=outliers_fraction,
                                    random_state=0),
        'K Nearest Neighbors (KNN)': KNN(
            contamination=outliers_fraction),
        'Average KNN': KNN(method='mean',
                           contamination=outliers_fraction),
        'Local Outlier Factor (LOF)':
            LOF(n_neighbors=35, contamination=outliers_fraction),
        'Minimum Covariance Determinant (MCD)': MCD(
            contamination=outliers_fraction, random_state=0),
        'One-class SVM (OCSVM)': OCSVM(contamination=outliers_fraction),
        'Principal Component Analysis (PCA)': PCA(
            contamination=outliers_fraction, random_state=0),
        'Locally Selective Combination (LSCP)': LSCP(
            detector_list, contamination=outliers_fraction,
            random_state=0)
    }
    
    for i, clf in enumerate(classifiers.keys()):
        print('Model', i + 1, clf)

     

    计算异常值

    在这里我们要做的是拟合数据,预测异常和正常数据,计算异常值分数,最后进行可视化。

    #将num_people和num_order合并成一个两列的numpy数组
    X1= df['num_people'].values.reshape(-1,1)
    X2 = df['num_order'].values.reshape(-1,1)
    X = np.concatenate((X1,X2),axis=1)
    
    xx , yy = np.meshgrid(np.linspace(0, 1, 100), np.linspace(0, 1, 100))
    plt.figure(figsize=(20, 15))
    for i, (clf_name, clf) in enumerate(classifiers.items()):
        #训练数据
        clf.fit(X)
        # 预测异常值分数
        scores_pred = clf.decision_function(X) * -1
        # 预测异常值和正常值的数据
        y_pred = clf.predict(X)
        n_inliers = len(y_pred) - np.count_nonzero(y_pred)
        n_outliers = np.count_nonzero(y_pred == 1)
    
        df1 = df
        df1['outlier'] = y_pred.tolist()
        #过滤出num_people和num_order的正常值
        inliers_people = np.array(df1['num_people'][df1['outlier'] == 0]).reshape(-1,1)
        inliers_order = np.array(df1['num_order'][df1['outlier'] == 0]).reshape(-1,1)
    
        #过滤出num_people和num_order的异常值
        outliers_people = df1['num_people'][df1['outlier'] == 1].values.reshape(-1,1)
        outliers_order = df1['num_order'][df1['outlier'] == 1].values.reshape(-1,1)
            
        # 设定一个阈值用以识别正常值和异常值的标准
        threshold = np.percentile(scores_pred, 100 * outliers_fraction)
    
        #决策函数为每一个数据点计算异常值分数
        Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()]) * -1
        Z = Z.reshape(xx.shape)
        
        plt.subplot(3,4,i+1)
        #在图上对从最小的异常值分数到阈值的范围内进行分层着色
        plt.contourf(xx, yy, Z, levels=np.linspace(Z.min(), threshold, 7),cmap=plt.cm.Blues_r)
        #在异常值分数等于阈值处画红色线条
        a = plt.contour(xx, yy, Z, levels=[threshold],linewidths=2, colors='red')
        
        #填充橙色轮廓线,其中异常分数的范围是从阈值到最大异常分数
        plt.contourf(xx, yy, Z, levels=[threshold, Z.max()],colors='orange')
        b = plt.scatter(x=inliers_people, y=inliers_order, c='white',s=20, edgecolor='k')    
        c = plt.scatter(x=outliers_people, y=outliers_order, c='black',s=20, edgecolor='k')
           
        plt.axis('tight')   
        plt.legend([a.collections[0], b,c], ['决策函数', '正常值','异常值'],
                    prop=matplotlib.font_manager.FontProperties(size=12),loc='upper right')
    
        plt.xlim((0, 1))
        plt.ylim((0, 1))
        ss = '异常值数量: '+str(n_outliers)+' 正常值数量: '+str(n_inliers)
        plt.title(clf_name)
        plt.xlabel(ss)
    plt.show();

     

     

     参考资料

    CBLOF(Cluster-based Local Outlier Factor)

    CBLOF算法时基于聚类组的本地异常因子计算异常值分数。 CBLOF将数据集和由聚类算法生成的聚类模型作为输入。它使用参数alpha和beta将群集分为小群集和大群集。然后基于该点所属的聚类的大小以及到最近的大聚类的距离来计算异常分数。()

    HBOS(基于直方图的离群值检测)¶

    HBOS假设特征独立,并通过构建直方图来计算异常程度。 在多变量异常检测中,可以计算每个单个特征的直方图,单独评分并在最后组合。

    IsolationForest(隔离森林) 

    隔离森林其原理与随机森林类似,建立在决策树的基础上。 隔离林通过随机选择特征然后根据特征的最大值和最小值之间的分割值来隔离观察。 PyOD Isolation Forest模块() 是Scikit-learn Isolation Forest的wrapper,它具有更多功能。

    KNN(K - Nearest Neighbors)

    用于离群检测的pyod.models.knn.KNN,对于数据,它与第k个最近邻居的距离可以被视为异常值。

    关于异常值检测的博客文章

    Example of Comparing All Implemented Outlier Detection Models 

    An Awesome Tutorial to Learn Outlier Detection in Python using PyOD Library

     完整代码下载:

    https://github.com/tongzm/ml-python/blob/master/pyod%E5%BC%82%E5%B8%B8%E5%80%BC%E6%A3%80%E6%B5%8B.ipynb

     

    展开全文
  • 总结了异常值检测的相关课程视频、书籍、论文、数据集以及工具库 异常检测学习资源 异常检测(也称为异常检测)是一个令人兴奋但具有挑战性的领域,旨在识别偏离一般数据分布的异常对象。 异常值检测已被证明在许多...
  • 个推也非常重视大数据中的异常值检测,例如在运维部门的流量管理业务中,个推很早便展开了对异常值检测的实践,也因此积累了较为丰富的经验。本文将从以下几个方面介绍异常值检测。 1、异常值检测研究背景 2、异常...
  • AutoOut是一种自动化的异常值检测和处理工具,可让您无需编写任何代码即可获得更准确的更好模型。 凭借易于使用和简单的界面,您可以检测和处理数据集中的异常值,从而有助于改进最终模型。 如何使用: 步骤1:...
  • 异常值检测方法汇总

    万次阅读 多人点赞 2020-04-08 17:16:57
    异常检测项目流程前言一、项目流程1.目标确立2.数据准备3.数据分析处理4.模型算法(重点)4.1 传统统计方法4.1.1 3σ准则4.1.2 四分位(箱线图)4.2 机器学习方法(重点)4.2.1 监督学习算法4.2.2 无监督学习算法...
  • 异常值检测已被证明在许多领域都很重要,例如信用卡欺诈分析、网络入侵检测和机械单元缺陷检测。 该存储库收集: 书籍和学术论文 在线课程和视频 离群数据集 开源和商业图书馆/工具包 重要会议和期刊 更多项目将被...
  • 机器学习数据分析之异常值检测

    千次阅读 2021-07-20 11:34:14
    异常值检验可以分为单变量异常值检验和多变量异常值检验,对于时间序列数据而言还有趋势预测的时间序列异常值检验。 1.基于统计学的单变量异常值检验 可以先采用统计学方法查看数据的描述性统计(均值、标准差、...
  • 1.异常值定义 异常值是指样本中的个别值,其数值明显偏离它...2.异常值检测方法 (1)描述性统计和可视化寻找 就是简单的画图看数据,计算方差、标准差查看数据的波动程度,另外查看均值大小、众数、最大值最小值、分
  • ML之FE:特征工程中常用的一些处理手段(缺失值填充、异常值检测等)及其对应的底层代码的实现 目录 特征工程中常用的一些处理手段(缺失值填充、异常值检测等)及其对应的底层代码的实现 缺失值填充 fillna(self, value...
  • 异常值检测的常用方法简述

    万次阅读 2019-06-17 23:12:02
    常用的异常值检测方法如下 1. 简单的描述性统计分析方法 2. 3原则方法 3. 箱型图分析方法 4.Z-score方法 5.DBSCAN方法 6.孤立森林(Isolation Forest)方法 异常值定义 异常值是指样本中的个别值,其数值...
  • python异常值检测

    千次阅读 2020-04-01 12:56:55
    Anomaly Detection异常检测 What are Outliers ? Statistical Methods for Univariate Data Using Gaussian Mixture Models Fitting an elliptic envelope Isolation Forest Local Outlier Factor Using clustering...
  • python数据分析入门,异常值检测及预处理
  • 五种常用异常值检测方法

    万次阅读 2020-06-22 14:46:37
    Toggle navigation 首页 产业趋势 专家观察 CISO洞察 决策研究 登录 APP下载 数据挖掘最前线:五种常用异常值检测方法 安全运营 机器之心 2019-07-05 通过鉴别故障来检测异常对任何业务来说都很重要。本文作者总结了...
  • Python数据分析基础: 异常值检测和处理

    万次阅读 多人点赞 2020-10-31 22:05:31
    本篇继续分享数据清洗中的另一个常见问题:异常值检测和处理。 1 什么是异常值? 在机器学习中,异常检测和处理是一个比较小的分支,或者说,是机器学习的一个副产物,因为在一般的预测问题中,模型通常是对整体样本...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 288,918
精华内容 115,567
关键字:

异常值检测

友情链接: zhengyan.zip