A fix for Google Chart API

3/11/2015 03:05:00 PM
I've been working with Google Chart recently, and while it makes some pretty cool graphics, there are some things that bug me about it. Here's one: there's no method for adding a series to the graph. Considering the following graph, which grabs and plots data from FRED based on user input:
If you press the button it adds the PCE Deflator to the series that was already there. This is pretty common functionality for a web chart. But Google Chart does it poorly. There are two ways to plot series in google chart: either use addRows or setCell. Here's how it works with addRows: Suppose you've just done your ajax or whatever so you now have a javascript array of dates and an array of corresponding values for the series you want to graph. The Google chart only reads data from Google's DataTable class object, so you need to reorganize your data from whatever came over via ajax into a DataTable using the DataTable's interface, consisting of the addRows and setCell methods. So for example we can do this:
var temp=[]; for(var i=0;i<dates.length;i++){   temp.push(new Date([dates[i]),parseFloat(values[i])]); } var data=new google.visualization.DataTable(); data.addColumn('date','X'); data.addColumn('number','Series Name'); data.addRows(temp);
Importantly, addRows will only accept a vector of rows of data as an argument. If your ajax data source forces you to loop through the rows of data anyway (this is true of the FRED API, for example), and you want to graph all your series at once, then this is fine. But you've already graphed one series, and want to add a second, then you can't use addRows. So you have use setCell instead, and consequently have to keep track of row and column numbers manually:
if(data.getNumberOfRows()!=values.length){ data.addRows(values.length); } data.addColumn('number','Second Series'); for(var i=0;i<values.length;i++){     data.setCell(i,1,parseFloat(values[i])); }
Either way, because Google's chart objects always interpret rows as observations and columns as series, you are forced to iterate through each row of the DataTable to add a series. I don't think this makes any sense--for a graphing application, you want to focus on adding and manipulating whole series--adding columns at a time, not rows. I'd count this oversight as a bug. To fix this bug, I've made a small extension of the Google API: a method addColumnWithValues, which takes three arguments, data type, series name, and an array of all the values. So to add a series you simply do:
data.addColumnWithValues('number','Series 2',values);
No more typing out for loops or manually tracking all the row and column numbers. To use my extension, you'll need to drop this in your javascript code somewhere inside the callback for the google chart:
google.visualization.DataTable.prototype.addColumnWithValues=function(type,name,valuesarray){ var that=this; that.addColumn(type,name); var rowNum=valuesarray.length; var colNum=that.getNumberOfColumns()-1; if(colNum===0){ that.addRows(rowNum); } switch(type){ case 'number': for(var i=0;i<rowNum;i++){ that.setCell(i,colNum,parseFloat(valuesarray[i])); } break; case 'date': for(var i=0;i<rowNum;i++){ that.setCell(i,colNum,new Date(valuesarray[i])); } break; default: for(var i=0;i<rowNum;i++){ that.setCell(i,colNum,valuesarray[i]); } break; } }
Would it have been so hard for Google to include something like this in the original API? I think they should have. Note that this is not necessarily a high-performance solution. It's something of a hack really, that works because javascript is extremely dynamically typed. While this solution works, I suspect Google could get a lot better performance building something up from the lower levels of the DataTable class, avoiding the need to call the setCell method for literally each row of data. In particular, I suspect if they changed the DataTable class so that it stored data as an array of column-arrays instead of an array of row-arrays, almost everything would be faster, and most things would require less coding. I don't think there's much doubt that Google's charts are prettier, but if you want easy line graphs you are always welcome to use my own homegrown javascript graph API--a graphing engine for the HTML5 <canvas> element--which I've used on this blog previously.
Update: I did a speed test comparing google's API with my own canvas graph API. Regardless of the format of the raw data (in column arrays vs FRED-like json objects), my API is about 2 orders of magnitude faster than google chart.