3 * Pure JavaScript plotting plugin using jQuery
8 * Copyright (c) 2009-2012 Chris Leonello
9 * jqPlot is currently available for use in all personal or commercial projects
10 * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
11 * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
12 * choose the license that best suits your project and use it accordingly.
14 * Although not required, the author would appreciate an email letting him
15 * know of any substantial use of jqPlot. You can reach the author at:
16 * chris at jqplot dot com or see http://www.jqplot.com/info.php .
18 * If you are feeling kind and generous, consider supporting the project by
19 * making a donation at: http://www.jqplot.com/donate.php .
21 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
25 * http://hexmen.com/blog/2007/03/printf-sprintf/
26 * http://hexmen.com/js/sprintf.js
27 * The author (Ash Searle) has placed this code in the public domain:
28 * "This code is unrestricted: you are free to use it however you like."
34 * Class: $.jqplot.Trendline
35 * Plugin which will automatically compute and draw trendlines for plotted data.
37 $.jqplot.Trendline = function() {
41 // Wether or not to show the trend line.
42 this.show = $.jqplot.config.enablePlugins;
44 // CSS color spec for the trend line.
45 // By default this wil be the same color as the primary line.
46 this.color = '#666666';
48 // Renderer to use to draw the trend line.
49 // The data series that is plotted may not be rendered as a line.
50 // Therefore, we use our own line renderer here to draw a trend line.
51 this.renderer = new $.jqplot.LineRenderer();
52 // prop: rendererOptions
53 // Options to pass to the line renderer.
54 // By default, markers are not shown on trend lines.
55 this.rendererOptions = {marker:{show:false}};
57 // Label for the trend line to use in the legend.
60 // Either 'exponential', 'exp', or 'linear'.
63 // true or false, wether or not to show the shadow.
65 // prop: markerRenderer
66 // Renderer to use to draw markers on the line.
67 // I think this is wrong.
68 this.markerRenderer = {show:false};
70 // Width of the trend line.
73 // Angle of the shadow on the trend line.
74 this.shadowAngle = 45;
76 // pixel offset for each stroke of the shadow.
77 this.shadowOffset = 1.0;
79 // Alpha transparency of the shadow.
80 this.shadowAlpha = 0.07;
82 // number of strokes to make of the shadow.
84 this.isTrendline = true;
88 $.jqplot.postSeriesInitHooks.push(parseTrendLineOptions);
89 $.jqplot.postDrawSeriesHooks.push(drawTrendline);
90 $.jqplot.addLegendRowHooks.push(addTrendlineLegend);
92 // called witin scope of the legend object
93 // current series passed in
94 // must return null or an object {label:label, color:color}
95 function addTrendlineLegend(series) {
97 if (series.trendline && series.trendline.show) {
98 var lt = series.trendline.label.toString();
100 ret = {label:lt, color:series.trendline.color};
106 // called within scope of a series
107 function parseTrendLineOptions (target, data, seriesDefaults, options, plot) {
108 if (this._type && (this._type === 'line' || this._type == 'bar')) {
109 this.trendline = new $.jqplot.Trendline();
110 options = options || {};
111 $.extend(true, this.trendline, {color:this.color}, seriesDefaults.trendline, options.trendline);
112 this.trendline.renderer.init.call(this.trendline, null);
116 // called within scope of series object
117 function drawTrendline(sctx, options) {
118 // if we have options, merge trendline options in with precedence
119 options = $.extend(true, {}, this.trendline, options);
121 if (this.trendline && options.show) {
123 // this.renderer.setGridData.call(this);
124 var data = options.data || this.data;
125 fit = fitData(data, this.trendline.type);
126 var gridData = options.gridData || this.renderer.makeGridData.call(this, fit.data);
127 this.trendline.renderer.draw.call(this.trendline, sctx, gridData, {showLine:true, shadow:this.trendline.shadow});
131 function regression(x, y, typ) {
132 var type = (typ == null) ? 'linear' : typ;
144 if (type == 'linear') {
148 else if (type == 'exp' || type == 'exponential') {
149 for ( var i=0; i<y.length; i++) {
150 // ignore points <= 0, log undefined.
156 Y.push(Math.log(y[i]));
161 for ( var i = 0; i < N; i++) {
164 SXY = SXY + X[i]* Y[i];
165 SXX = SXX + X[i]* X[i];
166 SYY = SYY + Y[i]* Y[i];
169 slope = (N*SXY - SX*SY)/(N*SXX - SX*SX);
170 intercept = (SY - slope*SX)/N;
172 return [slope, intercept];
175 function linearRegression(X,Y) {
177 ret = regression(X,Y,'linear');
178 return [ret[0],ret[1]];
181 function expRegression(X,Y) {
185 ret = regression(x, y,'exp');
186 var base = Math.exp(ret[0]);
187 var coeff = Math.exp(ret[1]);
188 return [base, coeff];
191 function fitData(data, typ) {
192 var type = (typ == null) ? 'linear' : typ;
199 for (i=0; i<data.length; i++){
200 if (data[i] != null && data[i][0] != null && data[i][1] != null) {
206 if (type == 'linear') {
207 ret = linearRegression(x,y);
208 for ( var i=0; i<x.length; i++){
209 res = ret[0]*x[i] + ret[1];
210 ypred.push([x[i], res]);
213 else if (type == 'exp' || type == 'exponential') {
214 ret = expRegression(x,y);
215 for ( var i=0; i<x.length; i++){
216 res = ret[1]*Math.pow(ret[0],x[i]);
217 ypred.push([x[i], res]);
220 return {data: ypred, slope: ret[0], intercept: ret[1]};