Home > Back-end >  JAVA: Problems with Calculating the Simple Moving Average
JAVA: Problems with Calculating the Simple Moving Average

Time:07-16

I put together some code to calculate the moving average out of some sample data.

input is [12, 13, 15, 7, 6, 9, 13] output should be [12.5, 14, 11, 6.5, 7.5, 11]

        int n = 2;
        double[] data = { 12, 13, 15, 7, 6, 9, 13 };
        double buffer[] = new double[n];
        double output[] = new double[data.length];
        int current_index = 0;

        for (int i = 0; i < data.length; i  ) {
            buffer[current_index] = data[i] / n;
            double ma = 0.0;
            for (int j = 0; j < n; j  ) {
                ma  = buffer[j];
            }
            output[i] = ma;
            current_index= (current_index   1) % n;
        }

        //output: [6.0, 12.5, 14.0, 11.0, 6.5, 7.5, 11.0]

Does somebody know how i can get rid of output[0] (6.0)? this number should not be in the result.

Also how can i put the length of output[] to the real length of the output. so normally it should be:

double output[] = new double[data.length   1 - n];

but if i do this i get an indexoutofboundsexception.

CodePudding user response:

As you have less average than input values, you can't assign to output for each input, you need a condition

A condition that allows to wait to have passed enough values to start saving

for (int i = 0; i < data.length; i  ) {
    buffer[current_index] = data[i] / n;
    double ma = 0.0;
    for (int j = 0; j < n; j  ) {
        ma  = buffer[j];
    }

    if (i >= (n - 1)) {  // <<< <<<
        output[i - n   1] = ma;
    }

    current_index = (current_index   1) % n;
}

Extracting to a method, then try all

static double[] movingAverage(double[] data, int n) {
    double[] output = new double[data.length   1 - n];
    double[] buffer = new double[n];
    int current_index = 0;

    for (int i = 0; i < data.length; i  ) {
        buffer[current_index] = data[i] / n;
        double ma = 0.0;
        for (int j = 0; j < n; j  ) {
            ma  = buffer[j];
        }

        if (i >= (n - 1)) {
            output[i - n   1] = ma;
        }

        current_index = (current_index   1) % n;
    }
    return output;
}
double[] data = {12, 13, 15, 7, 6, 9, 13};
for (int n = 1; n < data.length   1; n  ) {
    System.out.println(n   " > "   Arrays.toString(movingAverage(data, n)));
}
1 > [12.0, 13.0, 15.0, 7.0, 6.0, 9.0, 13.0]
2 > [12.5, 14.0, 11.0, 6.5, 7.5, 11.0]
3 > [13.333, 11.666, 9.333, 7.333, 9.333]
4 > [11.75, 10.25, 9.25, 8.75]
5 > [10.6, 10.0, 10.0]
6 > [10.333, 10.5]
7 > [10.714]

CodePudding user response:

You are correct that the following is the size of the array:

double output[] = new double[data.length 1 - n];

You could simplify this piece of code to something like:

    public double[] movingAverage(double[] data, int n) {
        double[] output = new double[data.length 1-n];
        double runningSum = 0.0;
        for (int i = 1; i <= data.length; i  ) {
            runningSum  = data[i-1];
            if (i >= n) {
                double movingAverage = runningSum/n;
                output[i-n] = movingAverage;
                runningSum -= data[i-n];
            }
        }
        return output;
    }

This will reduce your time complexity from O(n^2) to O(n) also since there is just one for loop.

This type of algorithmic pattern is commonly referred to as Sliding Window approach.

CodePudding user response:

Simpler. You want a moving average of 2, it is very simple.

@Test
void test() {
    double[] data = {12, 13, 15, 7, 6, 9, 13};
    double output[] = new double[data.length - 1];

    for (int i = 0; i < data.length - 1; i  ) {
        double k = (data[i]   data[i   1]) / 2;
        output[i] = k;
    }
    //output: [12.5, 14.0, 11.0, 6.5, 7.5, 11.0]
    System.out.println(Arrays.toString(output));
}

If you want the moving average of n numbers, see test2:

@Test
void test2() {
    int n = 3;
    double[] data = {12, 13, 15, 7, 6, 9, 13};
    double output[] = new double[data.length - (n - 1)];

    for (int i = 0; i < data.length - (n - 1); i  ) {
        double buffer = 0;
        for (int j = 0; j < n; j  ) {
            buffer = buffer   data[i   j];
        }
        output[i] = buffer / n;
    }

    //output: [13.333333333333334, 11.666666666666666, 9.333333333333334, 7.333333333333333, 9.333333333333334]
    System.out.println(Arrays.toString(output));
}

If you can use java streams:

    @Test
void  test3(){
    double[] data = {12, 13, 15, 7, 6, 9, 13};
    final double[] output = IntStream.range(0, data.length - 1)
            .mapToDouble(i -> (data[i]   data[i   1]) / 2)
            .toArray();
    System.out.println(Arrays.toString(output));
}

After all, you just want the average over the sequencial next numbers.

  • Related