Friday, October 05, 2007
The simple way to average (or more accurately known as 'find the arithmetic mean of') a list of numbers is to add them all up and divide by the number of items.
 
E.g.
The average of: 6, 2, 4, 5, 6, 3, 2 is 4:
6+2+4+5+6+3+2 = 28
28/7=4
 
Yesterday, we had the need to keep recalculating the average on the fly as more data is received so the user could be constantly informed of the current average. Using the classic algorithm would result in a degradation of performance over time so we chose an alternative.
The basic idea behind the continuous average is to keep track of the total number of items and current average as you go. Then, when a new number comes along work out how far above or below the current average it is and divide it by the number of items so far. This results in an 'adjustment' value that is used to 'correct' the current average to take account of the new value.
 
Here's a simple C# class that implements the algorithm

public class ContinuousAverage
{
   private int count = 0; 
   private decimal? currentAverage = null

   public decimal Average 
   {
      get 
      {
      if (currentAverage == null
         throw new InvalidOperationException ("No numbers have been averaged"); 
   
      return currentAverage.Value; 
      }
   }

   public int Count 
   {
      get { return count; } 
   }

   public void Add(decimal number) 
   {
      count++;

      if (currentAverage == null
      {
         currentAverage = number;
      }
      else 
      {
         decimal differenceFromAverage = number - currentAverage.Value; 
         currentAverage += differenceFromAverage / count;
      }
   }
}

[TestFixture]
public class ContinuousAverageTests
{

   [Test] 
   [ExpectedException(typeof(InvalidOperationException ), "No numbers have been averaged")] 
   public void TestAverageOfNoItemsThrowsException( ) 
   {
      MovingAverage movingAverage = new MovingAverage (); 

      decimal value = movingAverage.Average; 
   }

   [Test] 
   public void TestAverageOfOneItemIsItemValue( ) 
   {
      MovingAverage movingAverage = new MovingAverage (); 
      movingAverage.Add( 1.234M );

      Assert.AreEqual( 1.234M, movingAverage.Average ); 
   }

   [Test] 
   public void TestAverageOfSeveralItemsIsStandardArithmeticMean() 
   {
      MovingAverage movingAverage = new MovingAverage (); 

      movingAverage.Add(6);
      movingAverage.Add(2);
      movingAverage.Add(4);
      movingAverage.Add(5);
      movingAverage.Add(6);
      movingAverage.Add(3);
      movingAverage.Add(2);

      Assert.AreEqual(7, movingAverage.Count);
      Assert.AreEqual(4, movingAverage.Average, "(6+2+4+5+6+3+2)/7 = (28/7) = 4"); 
   }
}

Friday, October 05, 2007 7:06:23 AM (GMT Daylight Time, UTC+01:00)
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):