Pregunta

I essentially have an array of values like this:

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

The above array is oversimplified, I'm collecting 1 value per millisecond in my real code and I need to process the output on an algorithm I wrote to find the closest peak before a point in time. My logic fails because in my example above, 0.36 is the real peak, but my algorithm would look backwards and see the very last number 0.25 as the peak, as there's a decrease to 0.24 before it.

The goal is to take these values and apply an algorithm to them which will "smooth" them out a bit so that I have more linear values. (ie: I'd like my results to be curvy, not jaggedy)

I've been told to apply an exponential moving average filter to my values. How can I do this? It's really hard for me to read mathematical equations, I deal much better with code.

How do I process values in my array, applying an exponential moving average calculation to even them out?

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

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

Solución

To compute an exponential moving average, you need to keep some state around and you need a tuning parameter. This calls for a little class (assuming you're using Java 5 or later):

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;
    }
}

Instantiate with the decay parameter you want (may take tuning; should be between 0 and 1) and then use average(…) to filter.


When reading a page on some mathmatical recurrence, all you really need to know when turning it into code is that mathematicians like to write indexes into arrays and sequences with subscripts. (They've a few other notations as well, which doesn't help.) However, the EMA is pretty simple as you only need to remember one old value; no complicated state arrays required.

Otros consejos

I am having a hard time understanding your questions, but I will try to answer anyway.

1) If your algorithm found 0.25 instead of 0.36, then it is wrong. It is wrong because it assumes a monotonic increase or decrease (that is "always going up" or "always going down"). Unless you average ALL your data, your data points---as you present them---are nonlinear. If you really want to find the maximum value between two points in time, then slice your array from t_min to t_max and find the max of that subarray.

2) Now, the concept of "moving averages" is very simple: imagine that I have the following list: [1.4, 1.5, 1.4, 1.5, 1.5]. I can "smooth it out" by taking the average of two numbers: [1.45, 1.45, 1.45, 1.5]. Notice that the first number is the average of 1.5 and 1.4 (second and first numbers); the second (new list) is the average of 1.4 and 1.5 (third and second old list); the third (new list) the average of 1.5 and 1.4 (fourth and third), and so on. I could have made it "period three" or "four", or "n". Notice how the data is much smoother. A good way to "see moving averages at work" is to go to Google Finance, select a stock (try Tesla Motors; pretty volatile (TSLA)) and click on "technicals" at the bottom of the chart. Select "Moving Average" with a given period, and "Exponential moving average" to compare their differences.

Exponential moving average is just another elaboration of this, but weights the "older" data less than the "new" data; this is a way to "bias" the smoothing toward the back. Please read the Wikipedia entry.

So, this is more a comment than an answer, but the little comment box was just to tiny. Good luck.

In a rolling manner.... i also use commons.apache math library

  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;
        }

Take a look at this. If your noise has zero average, consider also the use of a Kalman filter.

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;
   }

}

If you're having trouble with the math, you could go with a simple moving average instead of exponential. So the output you get would be the last x terms divided by x. Untested pseudocode:

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;

Note that you will need to handle the start and end parts of the data since clearly you can't average the last 5 terms when you are on your 2nd data point. Also, there are more efficient ways of calculating this moving average(sum = sum - oldest + newest), but this is to get the concept of what's happening across.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top