美文网首页人工智能 大数据 计算机科学
异常值检测-滑动均值实现智能告警

异常值检测-滑动均值实现智能告警

作者: 万州客 | 来源:发表于2022-04-13 19:10 被阅读0次

    疯狂学习起来吧,metis里用了EWMA的。这篇讲得比较细致。

    URL:

    https://blog.csdn.net/xsdxs/article/details/71608157

    前言

    当前的分析对象是一段 timestamp-value 的时间序列,该时间序列可能是cpu使用率、磁盘使用率等数据。对于这些序列实现智能告警功能(也就是检测出一段序列中的异常值),因此需要我们在计算前,首先要定义什么样的值是异常值。
      基于移动平均的方法,其朴素思想是在直观上来看图形,认为相近一段时间内的数据值,有相似的走向趋势。因此判断一个值是否是异常值,可通过判断该值是否对数据趋势造成了破坏,以此来得出结论。
      如下图所示,其中某一点明显波动异常。所以直观上来看,就可以判断出该点是异常点。

    那么如何通过某些方法,对异常点进行更加科学地判断以及代码实现。即本文主要工作内容。

    移动平均方法介绍

    简单移动平均

    简单移动平均线(Simple Moving Average,SMA),是某变量之前 n 个数值的未作加权的算术平均。若计算收盘价的10日 SMA,是指“之前10日”收市价的平均值。假设,收市价为 p=(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10),则 SMA 公式为:

    推广到一般情况为:

    加权移动平均

    加权移动平均(Weighted Moving Average,WMA),是指计算平均值时将各个数据分别乘以不同权重,其基本思想是赋予近期数据更高的行为权重。WMA是线性的。
      若计算n日WMA,假设收市价为 p= (p1, p2, … , pn, pn+1, … , pm, …),则为若干天的收市价及依倒数顺序递减一的不同比例,来加权计算的移动平均线,公式如下:

    指数加权移动平均

    指数加权移动平均(EWMA:exponentially weighted moving average),公式如下:

    x是实际值,s是ewma计算出来的平均值。也就是下一点的平均值是由上一点的平均值,加上当前点的实际值修正而来。这个修正的比例,就取决月这个alpha的decay factor的大小。视觉上来说就是ewma曲线是否紧跟实际曲线,也就是平滑程度。
      要避免由于简单移动平均导致的缺陷,最简单的方法是对近期的数据赋予更高的权重。这也是指数加权移动平均法(EWMA)背后的基本思想

    Python 实现

    python pandas包中,对于ewma和sma有现成的实现方法。但是对于wma没有找到实现,在此,自己写了个简单地wma实现函数。

    # coding:utf-8
    from __future__ import division
    import pandas as pd
    import matplotlib.pyplot as plt
    
    
    class WMA(object):
        """
        加权移动平均实现
        """
        @staticmethod
        def get_wma_weights(span, flag=True):
            """
            计算每个数值点的wma权重值
            """
            paras = range(1, span + 1)
            count = sum(paras)
            if flag:
                return [float(para) / count for para in paras]
            else:
                return [float(para) / count for para in paras][::-1]
    
        def get_wma_values(self, datas):
            """
            计算wma数值
            """
            wma_values = []
            wma_keys = datas.index
            for length in range(1, len(datas) + 1):
                wma_value = 0
                weights = self.get_wma_weights(length)
                for index, weight in zip(datas.index, weights):
                    wma_value += datas[index] * weight
                wma_values.append(wma_value)
            return pd.Series(wma_values, wma_keys)
    
    '''
    1. 首先构造测试数据
    '''
    # TODO 以后改成读取数据,直接生成 pandas Series 对象
    data_dict_series = {
        '1490707920': 19.8219660272,
        '1490707980': 20.0681534509,
        '1490708040': 20.1842385903,
        '1490708100': 19.7650368611,
        '1490708160': 19.9269200861,
        '1490708220': 18.470530654,
        '1490708280': 18.2211077462,
        '1490708340': 19.3140179366,
        '1490708400': 20.5632611228,
        '1490708460': 20.6341463476,
        '1490708520 ': 8.9789650937,
        '1490708580': 19.3494873891,
        '1490708640': 20.0160623292,
        '1490708700': 19.6702364186,
        '1490708760': 19.624609674,
        '1490708820': 20.456564799,
        '1490708880': 17.6035744548,
        '1490708940': 17.1818237007,
        '1490709000': 20.600406309,
        '1490709060': 19.9717502842,
        '1490709120': 20.0490164061,
        '1490709180': 20.1280070096,
        '1490709240': 20.579920421,
        '1490709300': 19.0682847972,
        '1490709360': 20.6150588592,
        '1490709420': 19.8059894244,
        '1490709480': 19.7459333887,
        '1490709540': 20.1206678947,
        '1490709600': 21.0094336718,
        '1490709660': 19.8375952393
    }
    dps = pd.Series(data_dict_series)
    
    
    '''
    2. ewma进行拟合
    '''
    ewma_line = pd.ewma(dps, span=4)
    
    
    '''
    3. 简单移动平均
    '''
    sma_line = pd.rolling_mean(dps, window=4)
    
    
    '''
    4. wma加权移动平均
    '''
    wma_line = WMA().get_wma_values(dps)
    
    
    '''
    5. 对原始数据和拟合结果数据进行画图
    '''
    # plt.figure(figsize=(10, 4))
    
    dps.plot(label='sample data')
    ewma_line.plot(label='sample data EMA result')
    sma_line.plot(label='sample data SMA result')
    wma_line.plot(label='sample data EWMA result')
    plt.legend()
    plt.show()
    

    结果展示

    总体平滑效果对比


    20170511115002922.png

    异常点检测

    上面通过滑动平均算法,得到了滑动均值,通过均值可计算方差,方差乘以一定的倍数可以得出对于振幅的容忍范围。比较实际的值是否超出了这个范围就可以知道是否可以告警了。
      在本文的示例中,告警的正常的浮动范围设为一倍的方差。可以正常浮动范围可以根据实际业务进行调整。对于异常点监测通过如下代码实现。

    # coding:utf-8
    from __future__ import division
    import pandas as pd
    
    
    class WMA(object):
        """
        加权移动平均实现
        """
    
        @staticmethod
        def get_wma_weights(span, flag=True):
            """
            计算每个数值点的wma权重值
            """
            paras = range(1, span + 1)
            count = sum(paras)
            if flag:
                return [float(para) / count for para in paras]
            else:
                return [float(para) / count for para in paras][::-1]
    
        def get_wma_values(self, datas):
            """
            计算wma数值
            """
            wma_values = []
            wma_keys = datas.index
            for length in range(1, len(datas) + 1):
                wma_value = 0
                weights = self.get_wma_weights(length)
                for index, weight in zip(datas.index, weights):
                    wma_value += datas[index] * weight
                wma_values.append(wma_value)
            return pd.Series(wma_values, wma_keys)
    
    
    def calculate_variance(dps, moving_average):
        variance = 0
        flag_list = moving_average.isnull()
        count = 0
        for index in range(len(dps)):
            if flag_list[index]:
                count += 1
                continue
            variance += (dps[index] - moving_average[index]) ** 2
        variance /= (len(dps) - count)
        return variance
    
    
    '''
    1. 首先构造测试数据
    '''
    # TODO 以后改成读取数据,直接生成 pandas Series 对象
    data_dict_series = {
        '1490707920': 19.8219660272,
        '1490707980': 20.0681534509,
        '1490708040': 20.1842385903,
        '1490708100': 19.7650368611,
        '1490708160': 19.9269200861,
        '1490708220': 18.470530654,
        '1490708280': 18.2211077462,
        '1490708340': 19.3140179366,
        '1490708400': 20.5632611228,
        '1490708460': 20.6341463476,
        '1490708520 ': 8.9789650937,
        '1490708580': 19.3494873891,
        '1490708640': 20.0160623292,
        '1490708700': 19.6702364186,
        '1490708760': 19.624609674,
        '1490708820': 20.456564799,
        '1490708880': 17.6035744548,
        '1490708940': 17.1818237007,
        '1490709000': 20.600406309,
        '1490709060': 19.9717502842,
        '1490709120': 20.0490164061,
        '1490709180': 20.1280070096,
        '1490709240': 20.579920421,
        '1490709300': 19.0682847972,
        '1490709360': 20.6150588592,
        '1490709420': 19.8059894244,
        '1490709480': 19.7459333887,
        '1490709540': 20.1206678947,
        '1490709600': 21.0094336718,
        '1490709660': 19.8375952393
    }
    dps = pd.Series(data_dict_series)
    
    '''
    2. ewma进行拟合
    '''
    ewma_line = pd.ewma(dps, span=4)
    
    '''
    3. 简单移动平均
    '''
    sma_line = pd.rolling_mean(dps, window=4)
    
    '''
    4. wma加权移动平均
    '''
    wma_line = WMA().get_wma_values(dps)
    
    '''
    5. 计算告警
    '''
    # 计算每种平滑下原始数据的标准差
    
    sma_var = calculate_variance(dps, sma_line)
    wma_var = calculate_variance(dps, wma_line)
    ewma_var = calculate_variance(dps, ewma_line)
    
    flag_list = sma_line.isnull()
    
    for index in sma_line.index:
        if not (sma_line[index] - sma_var <= dps[index] <= sma_line[index] + sma_var):
            if not flag_list[index]:
                print "异常点", dps[index]
    print "=================================="
    for index in wma_line.index:
        if not (wma_line[index] - wma_var <= dps[index] <= wma_line[index] + wma_var):
            print "异常点", dps[index]
    print "=================================="
    for index in ewma_line.index:
        if not (ewma_line[index] - ewma_var <= dps[index] <= ewma_line[index] + ewma_var):
            print "异常点", dps[index]
    

    输出结果如下图所示:


    20170511175808099.png

    小结和参考

    小结

    本文使用了各种平滑算法对数据进行处理,但是算法见性能和效果,并没有进行科学地度量。这部分工作准备日后进行的。不过目前就从图上来看EWMA效果感觉还是不错的~~over

    参考

    告警方式介绍:
    https://segmentfault.com/a/1190000003021919
    滑动平均介绍:
    http://www.cnblogs.com/liuning8023/p/3548770.html
    https://en.wikipedia.org/wiki/Exponential_smoothing
    相关代码实现:
    http://stackoverflow.com/questions/9621362/how-do-i-compute-a-weighted-moving-average-using-pandas
    http://stackoverflow.com/questions/18517722/weighted-moving-average-in-python
    pandas用户手册:
    http://pandas.pydata.org/pandas-docs/version/0.17.0/api.html#standard-moving-window-functions
    ————————————————
    版权声明:本文为CSDN博主「宇毅」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/xsdxs/article/details/71608157

    相关文章

      网友评论

        本文标题:异常值检测-滑动均值实现智能告警

        本文链接:https://www.haomeiwen.com/subject/vfbisrtx.html