在Java中实现指数移动平均值

时间:2012-02-08 20:27:09

标签: java math lowpass-filter

我基本上有一个像这样的值数组:

0.25, 0.24, 0.27, 0.26, 0.29, 0.34, 0.32, 0.36, 0.32, 0.28, 0.25, 0.24, 0.25

以上数组过于简单,我在实际代码中每毫秒收集1个值,我需要处理我编写的算法的输出,以便在某个时间点之前找到最接近的峰值。我的逻辑失败了,因为在我上面的示例中,0.36是真正的峰值,但我的算法会向后看并查看最后一个数字0.25作为峰值,因为减少到0.24在它之前。

目标是获取这些值并将算法应用于它们,这将使它们“平滑”一点,以便我有更多的线性值。 (即:我希望我的结果是弯曲的,而不是锯齿状的)

我被告知要对我的值应用指数移动平均滤波器。我怎样才能做到这一点?我读取数学方程式真的很难,我对代码的处理要好得多。

如何处理数组中的值,应用指数移动平均值计算来均匀化它们?

float[] mydata = ...
mySmoothedData = exponentialMovingAverage(mydata, 0.5);

float[] exponentialMovingAverage(float[] input, float alpha) {
    // what do I do here?
    return result;
}

6 个答案:

答案 0 :(得分:29)

要计算exponential moving average,您需要保持一些状态,并且需要一个调整参数。这需要一个小类(假设您使用的是Java 5或更高版本):

class ExponentialMovingAverage {
    private double alpha;
    private Double oldValue;
    public ExponentialMovingAverage(double alpha) {
        this.alpha = alpha;
    }

    public double average(double value) {
        if (oldValue == null) {
            oldValue = value;
            return value;
        }
        double newValue = oldValue + alpha * (value - oldValue);
        oldValue = newValue;
        return newValue;
    }
}

使用您想要的衰减参数进行实例化(可以进行调整;应该介于0和1之间),然后使用average(…)进行过滤。


当阅读有关数学重复的页面时,在将其转换为代码时,您真正需要知道的是数学家喜欢将索引写入数组和带有下标的序列。 (他们还有一些其他符号,但没有帮助。)但是,EMA非常简单,因为你只需要记住一个旧值;不需要复杂的状态数组。

答案 1 :(得分:4)

我很难理解你的问题,但无论如何我都会尽力回答。

1)如果你的算法找到0.25而不是0.36,那就错了。这是错误的,因为它假设单调增加或减少(即“总是上升”或“总是下降”)。除非您平均所有数据,否则您的数据点(如您所呈现的那样)是非线性的。如果您确实想要找到两个时间点之间的最大值,请将数组从t_min切片到t_max,然后找到该子数组的最大值。

2)现在,“移动平均线”的概念非常简单:假设我有以下列表:[1.4,1.5,1.4,1.5,1.5]。我可以通过取两个数字的平均值来“平滑”:[1.45,1.45,1.45,1.5]。请注意,第一个数字是1.5和1.4的平均值(第二个和第一个数字);第二个(新列表)是1.4和1.5的平均值(第三和第二个旧列表);第三个(新列表)平均值为1.5和1.4(第四和第三),依此类推。我可以把它变成“三期”或“四期”,或“n”。注意数据如何更顺畅。 “查看移动平均值”的好方法是访问Google财经部,选择股票(尝试特斯拉汽车;非常不稳定(TSLA))并点击图表底部的“技术”。选择具有给定时段的“移动平均线”和“指数移动平均线”以比较它们的差异。

指数移动平均线只是对此的另一种阐述,但是对“旧”数据的权重小于“新”数据;这是一种将平滑“偏向”背面的方法。请阅读维基百科条目。

所以,这更像是一个评论而不是答案,但是小评论框只是微不足道。祝你好运。

答案 2 :(得分:0)

看看this。 如果您的噪音平均为零,请考虑使用Kalman filter

答案 3 :(得分:0)

以滚动的方式......我也使用commons.apache数学库

  public LinkedList EMA(int dperiods, double alpha)
                throws IOException {
            String line;
            int i = 0;
            DescriptiveStatistics stats = new SynchronizedDescriptiveStatistics();
            stats.setWindowSize(dperiods);
            File f = new File("");
            BufferedReader in = new BufferedReader(new FileReader(f));
            LinkedList<Double> ema1 = new LinkedList<Double>();
            // Compute some statistics
            while ((line = in.readLine()) != null) {
                double sum = 0;
                double den = 0;
                System.out.println("line: " + " " + line);
                stats.addValue(Double.parseDouble(line.trim()));
                i++;
                if (i > dperiods)
                    for (int j = 0; j < dperiods; j++) {
                        double var = Math.pow((1 - alpha), j);
                        den += var;
                        sum += stats.getElement(j) * var;
                        System.out.println("elements:"+stats.getElement(j));
                        System.out.println("sum:"+sum);
                    }
                else
                    for (int j = 0; j < i; j++) {
                        double var = Math.pow((1 - alpha), j);
                        den += var;
                        sum += stats.getElement(j) * var;
                    }
                ema1.add(sum / den);
                System.out.println("EMA: " + sum / den);
            }
            return ema1;
        }

答案 4 :(得分:0)

public class MovingAvarage {

public static void main(String[] args) {
    double[] array = {1.2, 3.4, 4.5, 4.5, 4.5};

    double St = 0D;
    for(int i=0; i<array.length; i++) {
        St = movingAvarage(St, array[i]);
    }
    System.out.println(St);

}

private static double movingAvarage(double St, double Yt) {
    double alpha = 0.01, oneMinusAlpha = 0.99;
    if(St <= 0D) {
        St = Yt;
    } else {
        St = alpha*Yt + oneMinusAlpha*St;
    }
    return St;
   }

}

答案 5 :(得分:-4)

如果你在数学方面遇到麻烦,你可以使用简单的移动平均线而不是指数。所以你得到的输出将是最后x项除以x。未经测试的伪代码:

int data[] = getFilled();
int outdata[] = initializeme()
for (int y = 0; y < data.length; y++)
    int sum = 0;
    for (int x = y; x < y-5; x++)
        sum+=data[x];
    outdata[y] = sum / 5;

请注意,您需要处理数据的开始和结束部分,因为当您处于第二个数据点时,显然无法平均最后5个术语。此外,有更有效的方法来计算这个移动平均线(总和=总和 - 最旧+最新),但这是为了获得正在发生的事情的概念。