Creating a Stephen Few style Bullet Graph with gChartPhp and Google Charts
Over the last month I have been focusing in on building a Business Intelligence Sales Dashboard for one of my clients. Dashboards are such handy tools as they give you a quick glance look at the activity going on and provide each in this case, Sales reps with their key metrics to help improve sales techniques and generally the ability to do their jobs (as well as add in a little healthy competition into the sales game!).
After a little research into various different metrics and ways of displaying metrics in a clean and simply way I came across the book "Information Dashboard Design" by Stephen Few, one of the real pioneers in effective data communication who created the "Bullet Graph" and who's work I have been following for a while now.
(Above: The humble Bullet Graph)
Essentially the Bullet Graph is a much smaller and more effective replacement for the ever popular (and I say ghastly looking) dashboard gauge that seems to make an appearance on many a dashboard. The Bullet Graph shows KPI performance tracking through "poor, average, good" ranges (normally red, yellow, green on many gauge style graphs), performance relative to some target plus a few other metrics, all in a very small space compared with a gauge.
Bullet Graphs pack in a lot of information for their relative size and can be interpreted very quickly due to it taking advantage of pre-attentive perception. Our brain is hardwired to subconsciously recognise certain basic shapes which we process first before our conscious mind completes the job. You will notice on the Bullet Graph example above the point at which the performance achieves the target creates a "cross" shape, so with a very quick glance you can tell even before reading the graph or looking more closely at numbers that a target has been achieved and that metric is on track!
Now imagine a stack of 50 bullet graphs vs 50 dashboard gauges packed into the same space... how quickly could you find out the number of achieved targets with each style of visualisation? That may be an interesting follow up post and experiment!
On with the show...
The follow step by step process will walk through how to generate a Bullet Graph using the Google Charts API and a PHP wrapper class called gChartPhp available to make life much easier. I have based the Bullet Graph creation on a great article on the Dealer Diagnostics blog which goes into the basics of building these style data visualisations and the various parts they are made up of, well worth a read.
First things first, you will want to download and include the gChartPHP class into your PHP application (in my case I included it in my SilverStripe CMS). This gives you access to a wide range of different styles of graphs available through Google Charts. Each style of graph is its own PHP class which extends a main gChart class with all the shared functions used across all graphs.
Next, gather your data, whether it is hard-coded or pulled from a database you will need the following pieces of data to build out your bullet graph.
* Some level of performance, for example this could be the number of sales made in a week.
* The target figure which we will match the level of performance against.
* Range figures for displaying the "poor, average, good" KPI ranges, the range figure represents the dividers between the ranges, i.e. between poor and average & between average and good (because we already know the start of the poor range starts at 0, and the end of the good range finished at the end of the graph).
* A max number of units for the X axis (this give you the full length of the graph we are creating).
A basic example of these figures variables for PHP might be:
$sales = 15
$target = 12
$range1 = 6;
$range2 = 10;
$max = 20;
Instantiate the Stacked Bar Chart class (You could also use a regular Bar Chart, but I will add some extra metrics later using this stacked style, make sure you read to the end!). It accepts a width and height parameter.
$bulletGraph = new gStackedBarChart(180,40);
Add the performance figure as an array to the graph, this eventually creates the thick black line which for example measures sales.
Add the target figure to the graph (vertical thin black line). For internal range markers, Google Charts measures the length of an axis on a scale of 0.0 - 1.0 so we must perform a percentage calculation ($target/$max) to find the point on the axis to include the target (Which is 0.01 thick).
Set up the visual style that turns a regular bar chart into a Bullet Graph style.
$bulletGraph->setColors(array("000000","AAAAAA")); //black bar for performance, plus another colour if we want a ghost bar later (See the last paragraph about these).
$bulletGraph->setBarWidth('8','8','12'); //sets up the width of the performance bar and the space either side.
$bulletGraph->addAxisRange(0, 0, $max); //sets the length of the x axis in terms of numbers of performance.
$bulletGraph->setDataRange(0, $max); //sets the allowed range of data, in this case our sales figure will sit between 0 and the max length of the graph.
Next we setup the ranges - poor, average and good. Again using range markers and the percentage calculation. We also set a series of gray scale colours for each range moving from dark to light tone.
Add the visible axis and flip the graph on to its side... alternatively if you require a vertical bullet graph you can exclude the setHorizintal() method and switch the x and y in the setVisibleAxes() method (Remember to also change the width and height dimensions to suit too!).
$bulletGraph->setVisibleAxes(array('x','y')); //display the axis label along the x axis i.e. the count of sales
$bulletGraph->setHorizontal(true); //This sets the graph on its side as a bullet graph (If you require a vertical bullet graph you can turn this off and switch the visible axis from eariler).
$bulletGraph = new gStackedBarChart(40,80);
$bulletGraph->setVisibleAxes(array('y','x')); //display the axis label along the y axis i.e. the count of sales
Add a label to explain what the bullet graph is for...
$bulletGraph->addAxisLabel(1,array('Sales per week'));
Return the Google Charts URL to generate the graph in your template, view or echo to the page wrapped in an <img> tag.
echo "<img src='".$bulletGraph->getUrl()."' />";
Taking things a step further...
Here are a couple of extra additions you can make to the basic bullet graph.
Example 1: Measuring two targets on one Bullet Graph
Add an extra range marker and adjust the thickness of the lines, again you will be able to tell if targets are achieved as they will make the cross shape and each vertical line will display thick or thin.
$target = 12;
$target2 = 14;
$max = 20;
(This Bullet Graph shows that both targets 1 & 2 have been achieved.)
Example 2: Performance against some past performance or forecast
In some cases you may want to track performance against your target and also a previos level of performance such as a personal best or a company record, or alternatively you may want to track against some forecast for exmaple budgeted vs actual expenditure. Again Bullet Graphs to the rescue.
By using gChartPhp's Stacked Bar Chart as our base for the Bullet Graph we can add an extra set of data to the stack and create what I like to call a "ghost"*. This ghost data variable is equal to the personal best figure (or budgeted figure if its a forcast) minus the current level of performance.
This give a lighter coloured line that sits behind the main performance indication line and shows the max level of performance that line has reach (in the case of a personal best) or the budgeted amount (in the case of a forcast).
$sales = 15;
$personalbest = 18;
$ghost = $personalbest - $sales;
(This Bullet Graph shows that the target is achieved but they are not quite at the personal best record yet.)
(This Bullet Graph shows forecast revenue Year to Date, they haven't reached the yearly target, or the forecast revenue yet.)
*The analogy of a "ghost" line comes from my years as a young gamer playing 1080 Snowboarding on the N64... you had the option of turning on the "Ghost" which was a recording of your best previous attempt at a level in the game which was a transperent gray version of your game charater you would try to beat your best time!