EMA Hit Counter

Real nerds use exponential moving average hit counters. Learn how in five or ten minutes. I pity the fool that don't use EMA hit counters.

Suppose you want to know how many hits you have on a page per week.

You could clear your hit counter every week, so at the end of every week, you know how many hits you had during that week. It's simple and works, but maybe we don't want to have to wait until the end of the week to see this week's trend.

We could try another method. To find out at any given time how many hits we've had in the last seven days, you'd need to record or log every hit along with the time of the hit, then figure out how many of those hits are within the desired timeframe. That's more work than the first method. It isn't too difficult to realize, but is it worth the effort and the expense in computer resources?

There is yet another way — there's always another way — but it must be explained properly.

Suppose we have a variable called 'v' that we increase by 0.5 every time we have a hit. We also multiply the same variable by 0.5 at the end of every week. So when we have one hit per week, the variable tends to move closer and closer to a value of 1.00 near the end of each week, giving us the number of hits.

0.5, 0.75, 0.875, 0.9375, 0.96875, etc.

Or suppose we increase v by 0.1 every time we have a hit and multiply by 0.9 every week. We also get an average of 1.00 eventually, but it is a slower approach.

0.1, 0.19, 0.271, 0.3439, 0.40951, etc.

The faster approach for our weekly hit counter makes more sense, because we don't want to have to wait months for fairly accurate results, so we'll use m=0.5 for the week, but we can improve the precision by multiplying in shorter time intervals.

Instead of multiplying by 0.5 every week, we could multiply by 0.9057236642639067 every day for a less drastic or sudden change in output. It's the same effect in the long term. Or better yet, we could multiply by 0.9958826236582974 every hour. Or even better yet, 0.999931237762985 every minute or 0.9999988539239694 every second. To find this magic number, our multiplier m, we use (1-a)1/r where a is the amount we add for each hit and r is the ratio of time units. So when we want to know the hits per week and we are multiplying every day, r equals 7. And if a is 0.5 then 1-a is also 0.5, and all of this means that the multiplier m should be 0.5 to the 1/7th power, which is the 0.9057236642639067 seen near the beginning of this paragraph.

The main obstacle that comes to mind with this method is that we can't multiply the variable every second or minute or hour without perhaps setting up a scheduled task to run a script to perform the action on the variable that is stored in a file or wherever. And that's getting ridiculous and inefficient. But we don't really need to do the multiplication every second. We just need the math to turn out like it happened that way. We just need the right answer.

If we always record the time of the latest hit, we will always know how many seconds have passed since the last hit, and we can multiply v by m for that number of seconds.

v’=v*mt

(using the value of m for resolution to seconds)

Meaning the new value of v is set to the old value times m to the power of t.

So there you go. That explains the principles behind my EMA hit counter.

You can obviously not only use this method to find the average number of hits per week, but also the average hits per month, day, hour, or whatever time period you like.

The only overhead for this method is loading a couple of numbers from a file, doing a little math, and saving the numbers back to the file. It's fast and easy work for your favorite server-side scripting language.

Note that the exponential moving average gives more weight to recent data, just as you would probably want with web stats to help spot trends. There is no exaggeration of trends, it is just that the past hits or the lack thereof will count less and less over time towards the value of v.

I came up with this EMA hit counter a couple of years ago and have been using it on my rockymount.us website. I've been wanting to write about it, and here it looks like I've done it. Alright!


The following PHP code provides a fast weekly counter in addition to slow hourly and daily counters. I've corrected the file locking issue.


<?php

$fn='count.txt';
$br="<br>\n";

$now=time();

$c=0;

$fp=fopen($fn,'c');
if(!flock($fp,LOCK_EX))die('Could not lock file!');

$f=file($fn);

if($f)$t=$now-intval($f[0]);
else{
 $t=0;
 $f=array(0,0,0,0,0);
}

function shoRate($n,$s,$d){
 global $f,$br;
 echo number_format($f[$n],$d),' ',$s,$br;
}

if($f){
 $f[0]=$now;
 $f[1]=floatval($f[1])*pow(0.999970733618364,$t)+.1;
 $f[2]=floatval($f[2])*pow(0.9999987805503309,$t)+.1;
 $f[3]=floatval($f[3])*pow(0.9999988539239694,$t)+.5;
 ++$f[4];

 shoRate(1,'hourly',2);
 shoRate(2,'daily',1);
 shoRate(3,'weekly',1);
 echo $br;

 if($t>5400)echo number_format($t/3600,1),' hours';
 else if($t>90)echo number_format($t/60,1),' minutes';
 else echo $t,' seconds';
 echo ' since last hit',$br;

 $f=join("\n",$f);
 file_put_contents($fn,$f);
}

flock($fp,LOCK_UN);
fclose($fp);

?>

Page generated in 0.165668 seconds.

© 2024 RockyMount.US