Skip to content

Commit d2eabf6

Browse files
daniloafstr4d
authored andcommitted
Implement error bars using standard deviation on timeline plots
jqplot.ohlcRendererWithErrorBars.min.js is the standard OHLC Renderer plugin from jqPlot 1.0.9 (revision d96a669) with the error-bar addition given here: https://bitbucket.org/cleonello/jqplot/issues/35/add-error-bar-capability#comment-141580
1 parent 17cba4b commit d2eabf6

File tree

5 files changed

+72
-6
lines changed

5 files changed

+72
-6
lines changed

codespeed/settings.py

+1
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,5 @@
6767
# ('myexe', '21df2423ra'),
6868
# ('myexe', 'L'),]
6969

70+
USE_ERROR_BARS = True # True to enable error bars on Timeline view
7071
USE_MEDIAN_BANDS = True # True to enable median bands on Timeline view

codespeed/static/js/jqplot/jqplot.ohlcRendererWithErrorBars.min.js

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

codespeed/static/js/timeline.js

+55-6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ function shouldPlotEquidistant() {
3434
return $("#equidistant").is(':checked');
3535
}
3636

37+
function shouldPlotErrorBars() {
38+
return $("#show_error_bars").is(':checked');
39+
}
40+
3741
function shouldPlotQuartiles() {
3842
return $("#show_quartile_bands").is(':checked');
3943
}
@@ -50,6 +54,7 @@ function getConfiguration() {
5054
env: $("input[name='environments']:checked").val(),
5155
revs: $("#revisions option:selected").val(),
5256
equid: $("#equidistant").is(':checked') ? "on" : "off",
57+
error: $("#show_error_bars").is(':checked') ? "on" : "off",
5358
quarts: $("#show_quartile_bands").is(':checked') ? "on" : "off",
5459
extr: $("#show_extrema_bands").is(':checked') ? "on" : "off"
5560
};
@@ -98,13 +103,34 @@ function getHighlighterConfig(median) {
98103
function renderPlot(data) {
99104
var plotdata = [],
100105
series = [],
106+
firstdates = [],
107+
lastdates = [],
101108
lastvalues = [];//hopefully the smallest values for determining significant digits.
102109
seriesindex = [];
110+
var errorSeries = 0;
103111
var hiddenSeries = 0;
112+
var mean = data['data_type'] === 'U';
104113
var median = data['data_type'] === 'M';
105114
for (var branch in data.branches) {
106115
// NOTE: Currently, only the "default" branch is shown in the timeline
107116
for (var exe_id in data.branches[branch]) {
117+
if (mean) {
118+
$("span.options.mean").css("display", "inline");
119+
if (shouldPlotErrorBars()) {
120+
marker = false;
121+
var error = new Array();
122+
for (res in data["branches"][branch][exe_id]) {
123+
var date = data["branches"][branch][exe_id][res][0];
124+
var value = data["branches"][branch][exe_id][res][1];
125+
var std_dev = data["branches"][branch][exe_id][res][2];
126+
error.push([date, value - std_dev, value + std_dev, data["branches"][branch][exe_id][res][3]]);
127+
}
128+
plotdata.push(error);
129+
series.push({renderer:$.jqplot.OHLCRenderer, rendererOptions:{errorBar:true}, showLabel: false, showMarker: true,
130+
"label": $("label[for*='executable" + getColor(exe_id) + "']").html() + " error", color: "#C0C0C0"});
131+
errorSeries++;
132+
}
133+
}
108134
// FIXME if (branch !== "default") { label += " - " + branch; }
109135
var label = $("label[for*='executable" + exe_id + "']").html();
110136
var seriesConfig = {
@@ -153,8 +179,15 @@ function renderPlot(data) {
153179
}
154180
series.push(seriesConfig);
155181
seriesindex.push(exe_id);
156-
plotdata.push(data.branches[branch][exe_id]);
157-
lastvalues.push(data.branches[branch][exe_id][0][1]);
182+
var exeData = data.branches[branch][exe_id];
183+
plotdata.push(exeData);
184+
var startDate = new Date(exeData[exeData.length - 1][0])
185+
var endDate = new Date(exeData[0][0]);
186+
startDate.setDate(startDate.getDate() - 1);
187+
endDate.setDate(endDate.getDate() + 1);
188+
firstdates.push(startDate);
189+
lastdates.push(endDate);
190+
lastvalues.push(exeData[0][1]);
158191
}
159192
//determine significant digits
160193
var digits = 2;
@@ -202,7 +235,8 @@ function renderPlot(data) {
202235
labelRenderer: $.jqplot.CanvasAxisLabelRenderer,
203236
tickOptions: {formatString:'%b %d'},
204237
pad: 1.01,
205-
autoscale: true,
238+
min: Math.min.apply(Math, firstdates),
239+
max: Math.max.apply(Math, lastdates),
206240
rendererOptions: {sortMergedLabels:true} /* only relevant when
207241
$.jqplot.CategoryAxisRenderer is used */
208242
}
@@ -211,7 +245,7 @@ function renderPlot(data) {
211245
highlighter: getHighlighterConfig(median),
212246
cursor: {show:true, zoom:true, showTooltip:false, clickReset:true}
213247
};
214-
if (series.length > 4 + hiddenSeries) {
248+
if (series.length > 4 + errorSeries + hiddenSeries) {
215249
// Move legend outside plot area to unclutter
216250
var labels = [];
217251
for (var l in series) {
@@ -231,14 +265,23 @@ function renderPlot(data) {
231265

232266
function renderMiniplot(plotid, data) {
233267
var plotdata = [],
234-
series = [];
268+
series = [],
269+
firstdates = [],
270+
lastdates = [];
235271

236272
for (var branch in data.branches) {
237273
for (var id in data.branches[branch]) {
238274
series.push({
239275
"label": $("label[for*='executable" + id + "']").html(),
240276
"color": getColor(id)
241277
});
278+
var exeData = data.branches[branch][id];
279+
var startDate = new Date(exeData[exeData.length - 1][0])
280+
var endDate = new Date(exeData[0][0]);
281+
startDate.setDate(startDate.getDate() - 1);
282+
endDate.setDate(endDate.getDate() + 1);
283+
firstdates.push(startDate);
284+
lastdates.push(endDate);
242285
plotdata.push(data.branches[branch][id]);
243286
}
244287
}
@@ -268,7 +311,10 @@ function renderMiniplot(plotid, data) {
268311
renderer:$.jqplot.DateAxisRenderer,
269312
pad: 1.01,
270313
autoscale:true,
271-
showTicks: false
314+
showTicks: false,
315+
min: Math.min.apply(Math, firstdates),
316+
max: Math.max.apply(Math, lastdates),
317+
rendererOptions: {sortMergedLabels:true}
272318
}
273319
},
274320
highlighter: {show:false},
@@ -280,6 +326,7 @@ function renderMiniplot(plotid, data) {
280326
function render(data) {
281327
$("#revisions").attr("disabled", false);
282328
$("#equidistant").attr("disabled", false);
329+
$("span.options.mean").css("display", "none");
283330
$("span.options.median").css("display", "none");
284331
$("#plotgrid").html("");
285332
if(data.error !== "None") {
@@ -342,6 +389,7 @@ function initializeSite(event) {
342389
$("input[name='benchmark']" ).change(updateUrl);
343390
$("input[name='environments']").change(updateUrl);
344391
$("#equidistant" ).change(updateUrl);
392+
$("#show_error_bars" ).change(updateUrl);
345393
$("#show_quartile_bands" ).change(updateUrl);
346394
$("#show_extrema_bands" ).change(updateUrl);
347395
}
@@ -397,6 +445,7 @@ function setValuesOfInputFields(event) {
397445

398446
$("#baselinecolor").css("background-color", baselineColor);
399447
$("#equidistant").prop('checked', valueOrDefault(event.parameters.equid, defaults.equidistant) === "on");
448+
$("#show_error_bars").prop('checked', valueOrDefault(event.parameters.error, defaults.error) === "on");
400449
$("#show_quartile_bands").prop('checked', valueOrDefault(event.parameters.quarts, defaults.quartiles) === "on");
401450
$("#show_extrema_bands").prop('checked', valueOrDefault(event.parameters.extr, defaults.extrema) === "on");
402451
}

codespeed/templates/codespeed/timeline.html

+8
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@
8383
<input id="equidistant" name="equidistant" type="checkbox" />
8484
<label for="equidistant">Equidistant</label>
8585
</span>
86+
{% if use_error_bars %}
87+
<span class="options mean" title="Shows error bars in the plots" style="display: none">
88+
<input id="show_error_bars" type="checkbox" name="show_error_bars" checked="checked"/>
89+
<label for="show_error_bars">Show error bars</label>
90+
</span>
91+
{% endif %}
8692
{% if use_median_bands %}
8793
<span class="options median" title="Shows quartile bands in the plots" style="display: none">
8894
<input id="show_quartile_bands" type="checkbox" name="show_quartile_bands" checked="checked"/>
@@ -112,6 +118,7 @@
112118
<script type="text/javascript" src="{{ STATIC_URL }}js/jqplot/jqplot.categoryAxisRenderer.min.js"></script>
113119
<script type="text/javascript" src="{{ STATIC_URL }}js/jqplot/jqplot.canvasTextRenderer.min.js"></script>
114120
<script type="text/javascript" src="{{ STATIC_URL }}js/jqplot/jqplot.canvasAxisLabelRenderer.min.js"></script>
121+
<script type="text/javascript" src="{{ STATIC_URL }}js/jqplot/jqplot.ohlcRendererWithErrorBars.min.js"></script>
115122
<script type="text/javascript">
116123
var CHANGES_URL = "{% url "changes" %}";
117124
</script>
@@ -126,6 +133,7 @@
126133
benchmark: "{{ defaultbenchmark }}",
127134
environment: {{ defaultenvironment.id }},
128135
equidistant: "{{ defaultequid }}",
136+
error: "{{ defaulterr }}",
129137
quartiles: "{{ defaultquarts }}",
130138
extrema: "{{ defaultextr }}"
131139
});

codespeed/views.py

+7
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,10 @@ def timeline(request):
446446
defaultequid = data['equid']
447447
else:
448448
defaultequid = "off"
449+
if 'error' in data:
450+
defaulterr = data['error']
451+
else:
452+
defaulterr = "on"
449453
if 'quarts' in data:
450454
defaultquarts = data['quarts']
451455
else:
@@ -459,6 +463,7 @@ def timeline(request):
459463
executables = {}
460464
for proj in Project.objects.filter(track=True):
461465
executables[proj] = Executable.objects.filter(project=proj)
466+
use_error_bars = hasattr(settings, 'USE_ERROR_BARS') and settings.USE_ERROR_BARS
462467
use_median_bands = hasattr(settings, 'USE_MEDIAN_BANDS') and settings.USE_MEDIAN_BANDS
463468
return render_to_response('codespeed/timeline.html', {
464469
'checkedexecutables': checkedexecutables,
@@ -474,8 +479,10 @@ def timeline(request):
474479
'branch_list': branch_list,
475480
'defaultbranch': defaultbranch,
476481
'defaultequid': defaultequid,
482+
'defaulterr': defaulterr,
477483
'defaultquarts': defaultquarts,
478484
'defaultextr': defaultextr,
485+
'use_error_bars': use_error_bars,
479486
'use_median_bands': use_median_bands,
480487
}, context_instance=RequestContext(request))
481488

0 commit comments

Comments
 (0)