How to calculate the rolling average of a C# array list?
Asked Answered
A

3

5

I'm trying to calculate the rolling averages of every four values in an array list and add those values to a separate array list. My original array list is called numlist and it contains values from 1 to 9

List<int> numlist = new List<int>();

numlist.Add(1);
numlist.Add(2);
numlist.Add(3);
numlist.Add(4);
numlist.Add(5);
numlist.Add(6);
numlist.Add(7);
numlist.Add(8);
numlist.Add(9);

When it calculates rolling averages, it should do it in an way like this:

first average = (1+2+3+4)/4

second average = (2+3+4+5)/4

third average = (3+4+5+6)/4

and so on

so the second array list,

List<double> avelist = new List<double>();

should contain these values

{2.5, 3.5, 4.5, 5.5, 6.5, 7.5}

How can I achieve this?

Autogenous answered 26/4, 2016 at 10:20 Comment(1)
What have you already tried?Pace
T
5

You can use LINQ like this:

List<double> averages = Enumerable.Range(0, numlist.Count - 3).
                              Select(i => numlist.Skip(i).Take(4).Average()).
                              ToList();

In your example, this goes from i = 0 to i = 5 and takes 4 elements from the list starting at index i and calculates their average.

You can output the result like that:

Console.WriteLine(string.Join(" ", averages));

A method with a variable "width" for the rolling average could look like:

public List<double> RollingAverage(List<int> source, int width)
{
    return Enumerable.Range(0, 1 + numlist.Count - width).
                              Select(i => numlist.Skip(i).Take(width).Average()).
                              ToList();
}

Documentation:

Titanite answered 26/4, 2016 at 10:29 Comment(4)
I tried this but when I try to print it out on the console using Console.WriteLine(averages); it only print out : System.Collections.Generic.List`1[System.Double]Autogenous
@Autogenous of course, because Console.WriteLine calls ToString() on the List<double> which outputs the type. Updated my answer with a correct way to output.Rotterdam
what does numlist.Count-3 doAutogenous
@Autogenous the second parameter of Range is the number of elements you want in that range. In that example, you need 6 (= 9 - 3) elements, because you want 6 averages (1-4, 2-5, 3-6, 4-7, 5-8 and 6-9). numList.Count is the number of elements in your original list.Rotterdam
A
6

If you care about performance, you can use a Queue and process each item in the source only once:

IEnumerable<double> RollingAverages(IEnumerable<int> numbers, int length) {
    var queue = new Queue<int>(length);
    double sum = 0;
    foreach (int i in numbers) {
        if (queue.Count == length) {
            yield return sum / length;
            sum -= queue.Dequeue();
        }
        sum += i;
        queue.Enqueue(i);
    }
    yield return sum / length;
}

Call:

foreach (double a in RollingAverages(new List<int> {1,2,3,4,5,6,7,8,9}, 4)) {
    Console.WriteLine(a);
}
Afternoons answered 26/4, 2016 at 10:43 Comment(0)
T
5

You can use LINQ like this:

List<double> averages = Enumerable.Range(0, numlist.Count - 3).
                              Select(i => numlist.Skip(i).Take(4).Average()).
                              ToList();

In your example, this goes from i = 0 to i = 5 and takes 4 elements from the list starting at index i and calculates their average.

You can output the result like that:

Console.WriteLine(string.Join(" ", averages));

A method with a variable "width" for the rolling average could look like:

public List<double> RollingAverage(List<int> source, int width)
{
    return Enumerable.Range(0, 1 + numlist.Count - width).
                              Select(i => numlist.Skip(i).Take(width).Average()).
                              ToList();
}

Documentation:

Titanite answered 26/4, 2016 at 10:29 Comment(4)
I tried this but when I try to print it out on the console using Console.WriteLine(averages); it only print out : System.Collections.Generic.List`1[System.Double]Autogenous
@Autogenous of course, because Console.WriteLine calls ToString() on the List<double> which outputs the type. Updated my answer with a correct way to output.Rotterdam
what does numlist.Count-3 doAutogenous
@Autogenous the second parameter of Range is the number of elements you want in that range. In that example, you need 6 (= 9 - 3) elements, because you want 6 averages (1-4, 2-5, 3-6, 4-7, 5-8 and 6-9). numList.Count is the number of elements in your original list.Rotterdam
R
1

The following code will help you:

List<int> numlist = Enumerable.Range(1, 10).ToList();// generating list
List<double> avelist = new List<double>();
Dictionary<int, double> rollingAvg = new Dictionary<int, double>();
int limit = 4, i = 0;
while (limit + i <= numlist.Count)
{
    avelist.Add(numlist.Skip(i).Take(limit).Average());
    i++;
}

avelist will be like the following @ the End of the execution :

{2.5, 3.5, 4.5, 5.5, 6.5, 7.5}
Rugg answered 26/4, 2016 at 10:33 Comment(1)
values that are in the avelist: 2.5, 3.5 etc is the output that I am trying to obtain from the first array list. So wouldn't this find the rolling averages of the averagesAutogenous

© 2022 - 2024 — McMap. All rights reserved.