HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Moving average calculation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
calculationmovingaverage

Problem

I am hoping to make this method run a little faster. I typically need to run this on a list with more than 100000 entries. Note that at the start and end of the list, I wish to weight the average so that it is closer to the start or end value, hence the smaller framesize.

public PointPairList GetMovingAverage(int frameSize, PointPairList data)
{
    PointPairList movAvgPoints = new PointPairList();
    //Smooth each point in the list
    for (int i = 0; i = data.Count)
        {
            actualFrameSize = frameSize + (data.Count - 1 - end);
        }
        //Now get the sum of the window
        double sum = data.Skip(start).Take(actualFrameSize).Sum(p => p.Y);
        //add the average point to the result
        movAvgPoints.Add(new PointPair(data[i].X, sum/actualFrameSize));
    }
    return movAvgPoints;
}

Solution

Skip() is not optimized for IList, it always enumerates the skipped elements. This means that your algorithm is O(n2) for no good reason. What you should do instead is to manually iterate just the required part of the input in each iteration using for. Something like:

double sum = 0;
for (int i = 0; i < actualFrameSize; i++)
{
    sum += data[start + i].Y;
}


(I don't like using GetRange() for this, as in tinstaafl's answer, because it unnecessarily copies the frame into a new list.)

If frameSize is large, another optimization would be not to recompute the sum of the frame for each index. Instead, just add the item from the front and subtract the item from the back:

public PointPairList GetMovingAverage(int frameSize, PointPairList data)
{
    var movAvgPoints = new PointPairList();

    //Before zero
    int currentFrameSize = frameSize/2;
    double sum = data.Take(currentFrameSize).Sum(p => p.Y);

    //Smooth each point in the list
    for (int i = 0; i = 0)
        {
            sum -= data[removed].Y;
            currentFrameSize--;
        }
        if (added < data.Count)
        {
            sum += data[added].Y;
            currentFrameSize++;
        }

        movAvgPoints.Add(new PointPair(data[i].X, sum/actualFrameSize));
    }

    return movAvgPoints;
}


If the length of data is n and frameSize is f, then the previous algorithm would be O(fn), but the improved one just O(n).

Another thing: there is usually no need to create types that derive from List, just use List directly.

Code Snippets

double sum = 0;
for (int i = 0; i < actualFrameSize; i++)
{
    sum += data[start + i].Y;
}
public PointPairList GetMovingAverage(int frameSize, PointPairList data)
{
    var movAvgPoints = new PointPairList();

    //Before zero
    int currentFrameSize = frameSize/2;
    double sum = data.Take(currentFrameSize).Sum(p => p.Y);

    //Smooth each point in the list
    for (int i = 0; i < data.Count; i++)
    {
        int removed = i - (frameSize/2) - 1;
        int added = i + (frameSize/2);

        if (removed >= 0)
        {
            sum -= data[removed].Y;
            currentFrameSize--;
        }
        if (added < data.Count)
        {
            sum += data[added].Y;
            currentFrameSize++;
        }

        movAvgPoints.Add(new PointPair(data[i].X, sum/actualFrameSize));
    }

    return movAvgPoints;
}

Context

StackExchange Code Review Q#39439, answer score: 9

Revisions (0)

No revisions yet.