Skip to content

Implement error bars using standard deviation on timeline plots #204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions codespeed/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
# ('myexe', '21df2423ra'),
# ('myexe', 'L'),]

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


Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 55 additions & 6 deletions codespeed/static/js/timeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ function shouldPlotEquidistant() {
return $("#equidistant").is(':checked');
}

function shouldPlotErrorBars() {
return $("#show_error_bars").is(':checked');
}

function shouldPlotQuartiles() {
return $("#show_quartile_bands").is(':checked');
}
Expand All @@ -50,6 +54,7 @@ function getConfiguration() {
env: $("input[name='environments']:checked").val(),
revs: $("#revisions option:selected").val(),
equid: $("#equidistant").is(':checked') ? "on" : "off",
error: $("#show_error_bars").is(':checked') ? "on" : "off",
quarts: $("#show_quartile_bands").is(':checked') ? "on" : "off",
extr: $("#show_extrema_bands").is(':checked') ? "on" : "off"
};
Expand Down Expand Up @@ -98,13 +103,34 @@ function getHighlighterConfig(median) {
function renderPlot(data) {
var plotdata = [],
series = [],
firstdates = [],
lastdates = [],
lastvalues = [];//hopefully the smallest values for determining significant digits.
seriesindex = [];
var errorSeries = 0;
var hiddenSeries = 0;
var mean = data['data_type'] === 'U';
var median = data['data_type'] === 'M';
for (var branch in data.branches) {
// NOTE: Currently, only the "default" branch is shown in the timeline
for (var exe_id in data.branches[branch]) {
if (mean) {
$("span.options.mean").css("display", "inline");
if (shouldPlotErrorBars()) {
marker = false;
var error = new Array();
for (res in data["branches"][branch][exe_id]) {
var date = data["branches"][branch][exe_id][res][0];
var value = data["branches"][branch][exe_id][res][1];
var std_dev = data["branches"][branch][exe_id][res][2];
error.push([date, value - std_dev, value + std_dev, data["branches"][branch][exe_id][res][3]]);
}
plotdata.push(error);
series.push({renderer:$.jqplot.OHLCRenderer, rendererOptions:{errorBar:true}, showLabel: false, showMarker: true,
"label": $("label[for*='executable" + getColor(exe_id) + "']").html() + " error", color: "#C0C0C0"});
errorSeries++;
}
}
// FIXME if (branch !== "default") { label += " - " + branch; }
var label = $("label[for*='executable" + exe_id + "']").html();
var seriesConfig = {
Expand Down Expand Up @@ -153,8 +179,15 @@ function renderPlot(data) {
}
series.push(seriesConfig);
seriesindex.push(exe_id);
plotdata.push(data.branches[branch][exe_id]);
lastvalues.push(data.branches[branch][exe_id][0][1]);
var exeData = data.branches[branch][exe_id];
plotdata.push(exeData);
var startDate = new Date(exeData[exeData.length - 1][0])
var endDate = new Date(exeData[0][0]);
startDate.setDate(startDate.getDate() - 1);
endDate.setDate(endDate.getDate() + 1);
firstdates.push(startDate);
lastdates.push(endDate);
lastvalues.push(exeData[0][1]);
}
//determine significant digits
var digits = 2;
Expand Down Expand Up @@ -202,7 +235,8 @@ function renderPlot(data) {
labelRenderer: $.jqplot.CanvasAxisLabelRenderer,
tickOptions: {formatString:'%b %d'},
pad: 1.01,
autoscale: true,
min: Math.min.apply(Math, firstdates),
max: Math.max.apply(Math, lastdates),
rendererOptions: {sortMergedLabels:true} /* only relevant when
$.jqplot.CategoryAxisRenderer is used */
}
Expand All @@ -211,7 +245,7 @@ function renderPlot(data) {
highlighter: getHighlighterConfig(median),
cursor: {show:true, zoom:true, showTooltip:false, clickReset:true}
};
if (series.length > 4 + hiddenSeries) {
if (series.length > 4 + errorSeries + hiddenSeries) {
// Move legend outside plot area to unclutter
var labels = [];
for (var l in series) {
Expand All @@ -231,14 +265,23 @@ function renderPlot(data) {

function renderMiniplot(plotid, data) {
var plotdata = [],
series = [];
series = [],
firstdates = [],
lastdates = [];

for (var branch in data.branches) {
for (var id in data.branches[branch]) {
series.push({
"label": $("label[for*='executable" + id + "']").html(),
"color": getColor(id)
});
var exeData = data.branches[branch][id];
var startDate = new Date(exeData[exeData.length - 1][0])
var endDate = new Date(exeData[0][0]);
startDate.setDate(startDate.getDate() - 1);
endDate.setDate(endDate.getDate() + 1);
firstdates.push(startDate);
lastdates.push(endDate);
plotdata.push(data.branches[branch][id]);
}
}
Expand Down Expand Up @@ -268,7 +311,10 @@ function renderMiniplot(plotid, data) {
renderer:$.jqplot.DateAxisRenderer,
pad: 1.01,
autoscale:true,
showTicks: false
showTicks: false,
min: Math.min.apply(Math, firstdates),
max: Math.max.apply(Math, lastdates),
rendererOptions: {sortMergedLabels:true}
}
},
highlighter: {show:false},
Expand All @@ -280,6 +326,7 @@ function renderMiniplot(plotid, data) {
function render(data) {
$("#revisions").attr("disabled", false);
$("#equidistant").attr("disabled", false);
$("span.options.mean").css("display", "none");
$("span.options.median").css("display", "none");
$("#plotgrid").html("");
if(data.error !== "None") {
Expand Down Expand Up @@ -342,6 +389,7 @@ function initializeSite(event) {
$("input[name='benchmark']" ).change(updateUrl);
$("input[name='environments']").change(updateUrl);
$("#equidistant" ).change(updateUrl);
$("#show_error_bars" ).change(updateUrl);
$("#show_quartile_bands" ).change(updateUrl);
$("#show_extrema_bands" ).change(updateUrl);
}
Expand Down Expand Up @@ -397,6 +445,7 @@ function setValuesOfInputFields(event) {

$("#baselinecolor").css("background-color", baselineColor);
$("#equidistant").prop('checked', valueOrDefault(event.parameters.equid, defaults.equidistant) === "on");
$("#show_error_bars").prop('checked', valueOrDefault(event.parameters.error, defaults.error) === "on");
$("#show_quartile_bands").prop('checked', valueOrDefault(event.parameters.quarts, defaults.quartiles) === "on");
$("#show_extrema_bands").prop('checked', valueOrDefault(event.parameters.extr, defaults.extrema) === "on");
}
Expand Down
8 changes: 8 additions & 0 deletions codespeed/templates/codespeed/timeline.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@
<input id="equidistant" name="equidistant" type="checkbox" />
<label for="equidistant">Equidistant</label>
</span>
{% if use_error_bars %}
<span class="options mean" title="Shows error bars in the plots" style="display: none">
<input id="show_error_bars" type="checkbox" name="show_error_bars" checked="checked"/>
<label for="show_error_bars">Show error bars</label>
</span>
{% endif %}
{% if use_median_bands %}
<span class="options median" title="Shows quartile bands in the plots" style="display: none">
<input id="show_quartile_bands" type="checkbox" name="show_quartile_bands" checked="checked"/>
Expand Down Expand Up @@ -114,6 +120,7 @@
<script type="text/javascript" src="{{ STATIC_URL }}js/jqplot/jqplot.categoryAxisRenderer.min.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/jqplot/jqplot.canvasTextRenderer.min.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/jqplot/jqplot.canvasAxisLabelRenderer.min.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/jqplot/jqplot.ohlcRendererWithErrorBars.min.js"></script>
<script type="text/javascript">
var CHANGES_URL = "{% url "changes" %}";
</script>
Expand All @@ -128,6 +135,7 @@
benchmark: "{{ defaultbenchmark }}",
environment: {{ defaultenvironment.id }},
equidistant: "{{ defaultequid }}",
error: "{{ defaulterr }}",
quartiles: "{{ defaultquarts }}",
extrema: "{{ defaultextr }}"
});
Expand Down
7 changes: 7 additions & 0 deletions codespeed/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,10 @@ def timeline(request):
defaultequid = data['equid']
else:
defaultequid = "off"
if 'error' in data:
defaulterr = data['error']
else:
defaulterr = "on"
if 'quarts' in data:
defaultquarts = data['quarts']
else:
Expand All @@ -479,6 +483,7 @@ def timeline(request):
executables = {}
for proj in Project.objects.filter(track=True):
executables[proj] = Executable.objects.filter(project=proj)
use_error_bars = hasattr(settings, 'USE_ERROR_BARS') and settings.USE_ERROR_BARS
use_median_bands = hasattr(settings, 'USE_MEDIAN_BANDS') and settings.USE_MEDIAN_BANDS
return render_to_response('codespeed/timeline.html', {
'checkedexecutables': checkedexecutables,
Expand All @@ -494,8 +499,10 @@ def timeline(request):
'branch_list': branch_list,
'defaultbranch': defaultbranch,
'defaultequid': defaultequid,
'defaulterr': defaulterr,
'defaultquarts': defaultquarts,
'defaultextr': defaultextr,
'use_error_bars': use_error_bars,
'use_median_bands': use_median_bands,
}, context_instance=RequestContext(request))

Expand Down