自己实现一个滑动窗口

基本概念

  1. 移动平均值: 一个移动平均值计算常常用来在事件序列数据中消除短期波动,展示长期的趋势。 移动平均值的平滑效果通过在计算中考虑到历史值来实现。计算一个移动平均值可以通过少量的状态来进行,对于一个事件序列,我们只需要记录上次发生的时间和上次计算出来的评价值即可。
      diff = currentTime-lastEventTime
      currentAverage = (1.0-alpha)*diff+alpha*lastAverage

上述计算中的alpha的值是一个0~1之间的常量,aplha值决定了一段时间内的平滑水平,alpha越趋于1,历史值对当前的平均值的影响越大,反之亦然

  1. 滑动窗口
  • 某些情况下,我们需要降低历史值对当前移动平均值的影响,例如当两次事件之间的间隔时间较长时,需要重置平滑作用。如果有一个较小的alpha值,可能不需要这么做,因为平滑效果已经很好。但是,如果aplha值很大时,需要适当地降低平滑效果的影响.
  • 考虑下面的例子。 我们有一个事件(比如说网络错误) 很少发生。偶尔出现小的峰值,通常是设什么问 题的。所以我们]想平滑这些小的峰值。只有当连续的峰值州现时,我们才需要发出通知。 如果事件平均一周才发生一次(达不到通知的阈值),但是某一天一小时内出现了多 个峰值(超过了通知阈值),alpha 值较大的平滑效果可能抵消了峰值,导致事件一直无法 触发。 为了中和这种影响,我们可以在计算移动平均值时引人滑动窗口的概念。因为我们已 经保留了上一个事件的时间戳以及当前的平均值,实现一个滑动窗口非常简单,如下面伪 代码所示:
f(cur rent Time last BventT ime) > s1idingWindowInterval
currentAverage = 0
end if  ....

一个完整的实例代码如下

import java.io.Serializable;

public class EWMA implements Serializable {
    private static final long serialVersionUID = -6408346318181111576L;
    // 和UNIX系统计算负载时使用的标准alpha值相同
    public static final double ONE_MINUTE_ALPHA = 1-Math.exp(-5d / 60d / 1d);
    public static final double FIVE_MINUTE_ALPHA = 1-Math.exp(-5d / 60d / 5d);
    public static final double FIFTEEN_MINUTE_ALPHA = 1-Math.exp(-5d / 60d / 15d);

    public static enum Time {
        MILLISECONDS(1),
        SECONDS(1000),
        MINUTES(SECONDS.getMillis() * 60),
        HOURS(MINUTES.getMillis() * 60),
        DAYS(HOURS.getMillis() * 24),
        WEEKS(DAYS.getMillis() * 7);

        private long millis;

        Time(long millis) {
            this.millis = millis;
        }

        public long getMillis() {
            return millis;
        }
    }

    private long window;  //滑动窗口大小
    private long alphaWindow;
    private long last;  //记录上一次的时间
    private double average;  //移动平均值
    private double alpha = -1D;  //平滑水平
    private boolean sliding = false;  //是否移动

    public EWMA() {
    }

    /**
     * 建立指定时间的滑动窗口
     */
    public EWMA sliding(double count,Time time){
        return this.sliding((long)(time.getMillis()*count));
    }

    private EWMA sliding(long window){
        this.sliding = true;
        this.window = window;
        return this;
    }

    /**
     * 指定alpha值
     * @param alpha
     * @return
     */
    public EWMA withAlpha(double alpha){
        if(!(alpha>0.0D)&&alpha<=1.0D){
            throw new IllegalArgumentException("Alpha must be between 0.0 and 1.0");
        }
        this.alpha = alpha;
        return this;
    }

    /**
     * 作为一个alphaWindow窗口的函数
     * alpha = 【1-Math.exp(-5d / 60d / alphaWindow)】
     * @param alphaWindow
     * @return
     */
    public EWMA withAlphaWindow(long alphaWindow){
        this.alpha = -1;
        this.alphaWindow = alphaWindow;
        return this;
    }


    public EWMA withAlphaWindow(double count,Time time){
        return this.withAlphaWindow((long)(time.getMillis()*count));
    }

    /**
     * 默认使用当前时间更新移动平均值
     */
    public void mark(){
        mark(System.currentTimeMillis());
    }

    /**
     * 更新移动平均值
     * @param time
     */
    public synchronized void mark(long time){
        if(this.sliding){
            //如果发生时间间隔大于窗口,则重置滑动窗口
            if(time-this.last > this.window){
                this.last = 0;
            }
        }
        if(this.last == 0){
            this.average = 0;
            this.last = time;
        }
        // 计算上一次和本次的时间差
        long diff = time-this.last;
        // 计算alpha
        double alpha = this.alpha != -1.0 ? this.alpha : Math.exp(-1.0*((double)diff/this.alphaWindow));
        // 计算当前平均值
        this.average = (1.0-alpha)*diff + alpha*this.average;
        this.last = time;
    }

    /**
     * mark()方法多次调用的平均间隔时间(历史平均水平)
     * @return
     */
    public double getAverage(){
        return this.average;
    }

    /**
     * 按照指定的时间返回平均值
     * @param time
     * @return
     */
    public double getAverageIn(Time time){
        return this.average == 0.0?this.average:this.average/time.getMillis();
    }

    /**
     * 返回特定时间度量内调用mark()的频率
     * @param time
     * @return
     */
    public double getAverageRatePer(Time time){
        return this.average == 0.0?this.average:time.getMillis()/this.average;
    }
}

使用实例

//指定一个1分钟的滑动窗口  
  EWMA ewma = new EWMA().sliding(1.0, EWMA.Time.MINUTES).withAlpha(EWMA.ONE_MINUTE_ALPHA);  

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Crossin的编程教室

【每周一坑】生成九宫格图片

非常简单的功能,但在开发中很常见,很多网页/应用里缩略图都是对图片进行缩放+切割得到的。

1333
来自专栏猿湿Xoong

Android | 通过机器学习实现精准字母手势识别

1884
来自专栏一“技”之长

Cocos2d-x-v3场景切换 原

        cocos2d中场景的切换采用的是包装的思想,通过创建一个专场效果类,将需要专场的场景进行包装。代码示例如下:

531
来自专栏一心无二用,本人只专注于基础图像算法的实现与优化。

PhotoShop算法原理解析系列 - 风格化---》查找边缘。                  闲谈.Net类型之public的不public,fixed的不能fixed     当然这个还可

      之所以不写系列文章一、系列文章二这样的标题,是因为我不知道我能坚持多久。我知道我对事情的表达能力和语言的丰富性方面的天赋不高。而一段代码需要我去用心...

2819
来自专栏阿凯的Excel

巧妙设置蛋糕图(Excel绘制图表系列课程)!

一直以踏踏实实做人!安安静静分享Excel技巧为宗旨的我,今天还是标题党了! 为啥尼! 我今天想分享这个图的绘制过程! ? 我真心不知道除了面积折线组合图外还能...

3615
来自专栏AI研习社

Github 项目推荐 | 用 PyTorch 实现全局/局部一致图像补全

本库用 PyTorch 实现了全局/局部一致图像补全(Globally and Locally Consistent Image Completion )。

1662
来自专栏刘望舒

Android性能优化实战之界面卡顿

作者:红橙Darren https://www.jianshu.com/p/18bb507d6e62

1451
来自专栏极客猴

Python 实现识别弱图片验证码

目前,很多网站为了防止爬虫肆意模拟浏览器登录,采用增加验证码的方式来拦截爬虫。验证码的形式有多种,最常见的就是图片验证码。其他验证码的形式有音频验证码,滑动验证...

3622
来自专栏大数据挖掘DT机器学习

R语言进行中文分词,并对6W条微博聚类

由于时间较紧,且人手不够,不能采用分类方法,主要是没有时间人工分类一部分生成训练集……所以只能用聚类方法,聚类最简单的方法无外乎:K-means与层次聚类。 尝...

3615
来自专栏瓜大三哥

IO约束(下)

Output接口类型和约束 FPGA 做Output 的接口时序同样也可以分为系统同步和源同步。在设置XDC约束时,总体思路与Input类似,只是换成要考虑下游...

2337

扫码关注云+社区