annotate analysis/analysis.js @ 1273:227ca4d00ab1

Added box plot chart to analysis.
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Thu, 24 Mar 2016 10:36:38 +0000
parents 5946b03fffe5
children b4a0244bf5ed
rev   line source
n@1223 1 /*
n@1223 2 * Analysis script for WAET
n@1223 3 */
n@1223 4
n@1223 5 var chartContext;
n@1223 6 window.onload = function() {
n@1223 7 // Load the Visualization API and the corechart package.
n@1223 8 google.charts.load('current', {'packages':['corechart']});
n@1223 9 chartContext = new Chart();
n@1223 10 }
n@1223 11
n@1227 12 function arrayMean(values) {
n@1227 13 var mean = 0;
n@1227 14 for (var value of values) {
n@1227 15 mean += value;
n@1227 16 }
n@1227 17 mean /= values.length;
n@1227 18 return mean;
n@1227 19 }
n@1227 20
n@1227 21 function percentile(values, n) {
n@1227 22 values.sort( function(a,b) {return a - b;} );
n@1227 23 // get ordinal rank
n@1227 24 var rank = Math.min(Math.floor(values.length*n/100), values.length-1);
n@1227 25 return values[rank];
n@1227 26 }
n@1227 27
n@1227 28 function arrayMin(array) {
n@1227 29 // Return the minimum value of an array
n@1227 30 var min = array[0];
n@1227 31 for (var value of array) {
n@1227 32 if (value < min) {
n@1227 33 min = value;
n@1227 34 }
n@1227 35 }
n@1227 36 return min;
n@1227 37 }
n@1227 38
n@1227 39 function arrayMax(array) {
n@1227 40 // Return the minimum value of an array
n@1227 41 var max = array[0];
n@1227 42 for (var value of array) {
n@1227 43 if (value > max) {
n@1227 44 max = value;
n@1227 45 }
n@1227 46 }
n@1227 47 return max;
n@1227 48 }
n@1227 49
n@1273 50 function boxplotRow(array) {
n@1273 51 // Take an array of element values and return array of computed intervals
n@1273 52 var result = {
n@1273 53 median : percentile(array,50),
n@1273 54 pct25 : percentile(array,25),
n@1273 55 pct75 : percentile(array,75),
n@1273 56 IQR : null,
n@1273 57 min: null,
n@1273 58 max: null,
n@1273 59 outliers: new Array()
n@1273 60 }
n@1273 61 result.IQR = result.pct75-result.pct25;
n@1273 62 var rest = [];
n@1273 63 var pct75_IQR = result.pct75+1.5*result.IQR;
n@1273 64 var pct25_IQR = result.pct25-1.5*result.IQR;
n@1273 65 for (var i=0; i<array.length; i++) {
n@1273 66 //outliers, ranger above pct75+1.5*IQR or below pct25-1.5*IQR
n@1273 67 var point = array[i];
n@1273 68 if (point > pct75_IQR || point < pct25_IQR) {
n@1273 69 result.outliers.push(point);
n@1273 70 } else {
n@1273 71 rest.push(point);
n@1273 72 }
n@1273 73 }
n@1273 74 result.max = arrayMax(rest);
n@1273 75 result.min = arrayMin(rest);
n@1273 76 return result;
n@1273 77
n@1273 78 }
n@1273 79
n@1227 80 function arrayHistogram(values,steps,min,max) {
n@1227 81 if (steps == undefined) {
n@1227 82 steps = 0.25;
n@1227 83 console.log("Warning: arrayHistogram called without steps size set, default to 0.25");
n@1227 84 }
n@1227 85 if (min == undefined) {min = arrayMin(values);}
n@1227 86 if (max == undefined) {max = arrayMax(values);}
n@1227 87 var histogram = [];
n@1227 88 var index = min;
n@1227 89 while(index < max) {
n@1227 90 histogram.push({
n@1227 91 marker: index,
n@1227 92 lt: index,
n@1227 93 rt: index+steps,
n@1227 94 count: 0
n@1227 95 });
n@1227 96 index += steps;
n@1227 97 }
n@1227 98 for (var value of values) {
n@1227 99 for (var entry of histogram) {
n@1227 100 if (value >= entry.lt && value <= entry.rt) {
n@1227 101 entry.count++;
n@1227 102 break;
n@1227 103 }
n@1227 104 }
n@1227 105 }
n@1227 106 return histogram;
n@1227 107 }
n@1227 108
n@1223 109 function Chart() {
n@1226 110 this.valueData = null;
n@1226 111 this.commentData = null;
n@1226 112 this.loadStatus = 0;
n@1227 113 this.charts = [];
n@1223 114
n@1226 115 var XMLHttp = new XMLHttpRequest();
n@1226 116 XMLHttp.parent = this;
n@1226 117 XMLHttp.open("GET","../scripts/score_parser.php?format=JSON",true);
n@1226 118 XMLHttp.onload = function() {
n@1226 119 // Now we have the JSON data, extract
n@1226 120 this.parent.valueData = JSON.parse(this.responseText);
n@1226 121 this.parent.loadStatus++;
n@1223 122 }
n@1226 123 XMLHttp.send();
n@1226 124 var XMLHttp2 = new XMLHttpRequest();
n@1226 125 XMLHttp2.parent = this;
n@1226 126 XMLHttp2.open("GET","../scripts/comment_parser.php?format=JSON",true);
n@1226 127 XMLHttp2.onload = function() {
n@1226 128 // Now we have the JSON data, extract
n@1226 129 this.parent.commentData = JSON.parse(this.responseText);
n@1226 130 this.parent.loadStatus++;
n@1226 131 }
n@1226 132 XMLHttp2.send();
n@1223 133
n@1227 134 this.chartObject = function(name) {
n@1227 135 // Create the charting object
n@1227 136 this.name = name;
n@1227 137 this.root = document.createElement("div");
n@1227 138 this.root.className = "chart-holder";
n@1227 139 this.root.setAttribute("name",name);
n@1227 140 this.chartDOM = document.createElement("div");
n@1227 141 this.tableDOM = document.createElement("div");
n@1227 142 this.latexDOM = document.createElement("div");
n@1227 143 this.downloadDOM = document.createElement("div");
n@1227 144 this.chart = undefined;
n@1227 145 this.data = new google.visualization.DataTable();
n@1227 146 this.options = {};
n@1227 147 this.print = document.createElement("button");
n@1228 148 this.sortDataButton = document.createElement("button");
n@1228 149 this.sortDataButton.textContent = "Sort by Data";
n@1228 150 this.sortDataButton.addEventListener("click",this);
n@1228 151 this.sortDataButton.setAttribute("name","sort-data");
n@1228 152 this.sortNameButton = document.createElement("button");
n@1228 153 this.sortNameButton.textContent = "Sort by Name";
n@1228 154 this.sortNameButton.addEventListener("click",this);
n@1228 155 this.sortNameButton.setAttribute("name","sort-name");
n@1228 156 this.draw = function() {
n@1228 157 if (this.chart == undefined) {return;}
n@1228 158 this.tableDOM.innerHTML = null;
n@1228 159 this.latexDOM.innerHTML = null;
n@1228 160 this.buildTable();
n@1228 161 this.writeLatex();
n@1228 162 this.chart.draw(this.data,this.options);
n@1228 163 }
n@1228 164 this.sortData = function() {
n@1265 165 this.data.sort(1);
n@1228 166 }
n@1228 167 this.sortName = function() {
n@1265 168 this.data.sort(0);
n@1228 169 }
n@1227 170 this.handleEvent = function() {
n@1227 171 // Only used to handle the chart.event.addListener(this,'ready') callback
n@1228 172 switch(event.currentTarget.getAttribute("name"))
n@1228 173 {
n@1228 174 case "download":
n@1228 175 window.open(this.chart.getImageURI());
n@1228 176 break;
n@1228 177 case "sort-data":
n@1228 178 this.sortData();
n@1228 179 this.draw();
n@1228 180 break;
n@1228 181 case "sort-name":
n@1228 182 this.sortName();
n@1228 183 this.draw();
n@1228 184 break;
n@1228 185 }
n@1227 186 }
n@1227 187
n@1227 188 this.root.appendChild(this.chartDOM);
n@1227 189 this.root.appendChild(this.tableDOM);
n@1227 190 this.root.appendChild(this.latexDOM);
n@1228 191 this.root.appendChild(this.sortDataButton);
n@1228 192 this.root.appendChild(this.sortNameButton);
n@1227 193 this.root.appendChild(this.print);
n@1227 194 this.print.textContent = "Download";
n@1228 195 this.print.setAttribute("name","download");
n@1227 196 this.print.addEventListener("click",this);
n@1227 197 this.root.appendChild(this.downloadDOM);
n@1227 198 this.buildTable = function() {
n@1227 199 var table = document.createElement("table");
n@1227 200 table.border = "1";
n@1264 201 var numRows = this.data.getNumberOfRows();
n@1264 202 var numColumns = this.data.getNumberOfColumns();
n@1264 203 for (var columnIndex=0; columnIndex<numColumns; columnIndex++)
n@1264 204 {
n@1273 205 var tableTitle = this.data.getColumnLabel(columnIndex);
n@1273 206 if (tableTitle != "") {
n@1273 207 var table_row = document.createElement('tr');
n@1273 208 table.appendChild(table_row);
n@1273 209 var row_title = document.createElement('td');
n@1273 210 table_row.appendChild(row_title);
n@1273 211 row_title.textContent = tableTitle;
n@1273 212 for (var rowIndex=0; rowIndex<numRows; rowIndex++)
n@1264 213 {
n@1273 214 var row_entry = document.createElement('td');
n@1273 215 table_row.appendChild(row_entry);
n@1273 216 var entry = this.data.getValue(rowIndex,columnIndex);
n@1273 217 if (isFinite(Number(entry)))
n@1273 218 {
n@1273 219 entry = String(Number(entry).toFixed(4));
n@1273 220 }
n@1273 221 row_entry.textContent = entry;
n@1264 222 }
n@1227 223 }
n@1227 224 }
n@1227 225 this.tableDOM.appendChild(table);
n@1227 226 };
n@1227 227 this.writeLatex = function() {
n@1264 228 var numRows = this.data.getNumberOfRows();
n@1264 229 var numColumns = this.data.getNumberOfColumns();
n@1227 230 var root = document.createElement("div");
n@1227 231 root.className = "code";
n@1227 232 var holder = document.createElement("pre");
n@1227 233 // Table start
n@1227 234 var start = document.createElement("p");
n@1227 235 start.textContent = "\\" + "begin{tabular}{|l|";
n@1227 236 holder.appendChild(start);
n@1264 237 for (var i=0; i<numRows; i++) {
n@1227 238 start.textContent = start.textContent+"c|";
n@1227 239 }
n@1228 240 start.textContent = start.textContent.concat("}");
n@1227 241 // Now write the rows:
n@1264 242 for (var rIndex=0; rIndex<numColumns; rIndex++) {
n@1273 243 var tableTitle = this.data.getColumnLabel(rIndex);
n@1273 244 if(tableTitle != "")
n@1273 245 {
n@1273 246 var row = document.createElement("p");
n@1273 247 row.textContent = tableTitle.concat(" & ");
n@1273 248 for (var cIndex=0; cIndex<numRows; cIndex++) {
n@1273 249 var entry = this.data.getValue(cIndex,rIndex);
n@1273 250 if (isFinite(Number(entry)))
n@1273 251 {
n@1273 252 entry = String(Number(entry).toFixed(4));
n@1273 253 }
n@1273 254 row.textContent = row.textContent.concat(entry);
n@1273 255 if (cIndex < numRows-1) {
n@1273 256 row.textContent = row.textContent.concat(" & ");
n@1273 257 } else {
n@1273 258 row.textContent = row.textContent.concat(" \\\\ \\hline");
n@1273 259 }
n@1264 260 }
n@1273 261 holder.appendChild(row);
n@1227 262 }
n@1227 263 }
n@1227 264 // Table end
n@1227 265 var end = document.createElement("p");
n@1227 266 end.textContent = "\\" + "end{tabular}";
n@1227 267 holder.appendChild(end);
n@1227 268 root.appendChild(holder);
n@1227 269 this.latexDOM.appendChild(root);
n@1227 270 }
n@1227 271 }
n@1227 272
n@1227 273 this.clear = function() {
n@1227 274 var inject = document.getElementById("test-pages");
n@1227 275 for (var chart of this.charts) {
n@1227 276 inject.removeChild(chart.root);
n@1227 277 }
n@1227 278 this.charts = [];
n@1227 279 }
n@1227 280
n@1227 281 this.drawTestMean = function() {
n@1227 282 // This draws one bargraph per axis with every test element on
n@1227 283 if (this.valueData == null) {
n@1227 284 console.log("Error - Data not loaded");
n@1227 285 return;
n@1227 286 }
n@1227 287 var chartList = [];
n@1227 288
n@1227 289 // Create the data table
n@1227 290 for (var page of this.valueData.pages) {
n@1227 291 for (var element of page.elements) {
n@1227 292 for (var axis of element.axis) {
n@1227 293 // Find the axis
n@1227 294 var axisChart = chartList.find(function(element,index,array){
n@1227 295 if (element.name == this) {return true;} else {return false;}
n@1273 296 },"mean-test-"+axis.name);
n@1227 297 if (axisChart == null) {
n@1273 298 axisChart = new this.chartObject("mean-test-"+axis.name);
n@1227 299 axisChart.options = {
n@1227 300 'title':'Mean of axis: '+axis.name,
n@1227 301 'width':window.innerWidth*0.9,
n@1227 302 'height':(window.innerWidth*0.9)/1.77
n@1227 303 }
n@1227 304 axisChart.data.addColumn('string','id');
n@1227 305 axisChart.data.addColumn('number',axis.name);
n@1227 306 chartList.push(axisChart);
n@1227 307 document.getElementById("test-pages").appendChild(axisChart.root);
n@1227 308 }
n@1227 309 var mean = arrayMean(axis.values);
n@1227 310 axisChart.data.addRow([element.id,mean]);
n@1227 311 }
n@1227 312 }
n@1227 313 }
n@1227 314
n@1227 315 // Build and push charts
n@1227 316 for (var chart of chartList) {
n@1227 317 chart.chart = new google.visualization.ColumnChart(chart.chartDOM);
n@1227 318 chart.chart.draw(chart.data,chart.options);
n@1227 319 chart.buildTable();
n@1227 320 chart.writeLatex();
n@1227 321 this.charts.push(chart);
n@1227 322 }
n@1227 323 }
n@1227 324
n@1273 325 this.drawTestBoxplot = function() {
n@1273 326 if (this.valueData == null) {
n@1273 327 console.log("Error - Data not loaded");
n@1273 328 return;
n@1273 329 }
n@1273 330 var chartList = [];
n@1273 331
n@1273 332 // Creates one chart per axis
n@1273 333
n@1273 334 // Create the data table
n@1273 335 for (var page of this.valueData.pages) {
n@1273 336 for (var element of page.elements) {
n@1273 337 for (var axis of element.axis) {
n@1273 338 // Find the axis
n@1273 339 var axisChart = chartList.find(function(element,index,array){
n@1273 340 if (element.name == this) {return true;} else {return false;}
n@1273 341 },"boxplot-test-"+axis.name);
n@1273 342 if (axisChart == null) {
n@1273 343 // Axis chart doesn't exist
n@1273 344 axisChart = new this.chartObject("boxplot-test-"+axis.name);
n@1273 345 axisChart.options = {
n@1273 346 'title':'Boxplot of axis '+axis.name,
n@1273 347 'width':window.innerWidth*0.9,
n@1273 348 'height':(window.innerWidth*0.9)/1.77,
n@1273 349 legend: {position: 'none'},
n@1273 350 lineWidth: 0,
n@1273 351 series: [{'color': '#D3362D'}],
n@1273 352 intervals: {
n@1273 353 barWidth: 1,
n@1273 354 boxWidth: 1,
n@1273 355 lineWidth: 2,
n@1273 356 style: 'boxes'
n@1273 357 },
n@1273 358 interval: {
n@1273 359 max: {
n@1273 360 style: 'bars',
n@1273 361 fillOpacity: 1,
n@1273 362 color: '#777'
n@1273 363 },
n@1273 364 min: {
n@1273 365 style: 'bars',
n@1273 366 fillOpacity: 1,
n@1273 367 color: '#777'
n@1273 368 }
n@1273 369 }
n@1273 370 };
n@1273 371 axisChart.data.addColumn('string','id');
n@1273 372 axisChart.data.addColumn('number','median');
n@1273 373 axisChart.data.addColumn({id:'max',type:'number',role:'interval'});
n@1273 374 axisChart.data.addColumn({id:'min',type:'number',role:'interval'});
n@1273 375 axisChart.data.addColumn({id:'firstQuartile',type:'number',role:'interval'});
n@1273 376 axisChart.data.addColumn({id:'median',type:'number',role:'interval'});
n@1273 377 axisChart.data.addColumn({id:'thirdQuartile',type:'number',role:'interval'});
n@1273 378 chartList.push(axisChart);
n@1273 379 document.getElementById("test-pages").appendChild(axisChart.root);
n@1273 380 }
n@1273 381 var result = boxplotRow(axis.values);
n@1273 382 axisChart.data.addRow([element.id,result.median,result.max,result.min,result.pct25,result.median,result.pct75]);
n@1273 383 }
n@1273 384 }
n@1273 385 }
n@1273 386 // Build and push charts
n@1273 387 for (var chart of chartList) {
n@1273 388 chart.chart = new google.visualization.LineChart(chart.chartDOM);
n@1273 389 chart.chart.draw(chart.data,chart.options);
n@1273 390 chart.buildTable();
n@1273 391 chart.writeLatex();
n@1273 392 this.charts.push(chart);
n@1273 393 }
n@1273 394 }
n@1273 395
n@1227 396 this.drawPageMean = function() {
n@1223 397 // First we must get the value data
n@1226 398 if (this.valueData == null) {
n@1226 399 console.log("Error - Data not loaded");
n@1226 400 return;
n@1226 401 }
n@1226 402 // We create one plot per page
n@1226 403 for (var page of this.valueData.pages) {
n@1227 404
n@1227 405 // Create the chart resulting point
n@1227 406 var chart = new this.chartObject("mean-page-"+page.id);
n@1227 407 document.getElementById("test-pages").appendChild(chart.root);
n@1226 408
n@1226 409 // Create the data table
n@1227 410 chart.data.addColumn('string','id');
n@1226 411 // Get axis labels
n@1226 412 for (var axis of page.elements[0].axis) {
n@1227 413 chart.data.addColumn('number',axis.name);
n@1226 414 }
n@1226 415 var rows = []; // Rows is an array of tuples [col1, col2, col3 ... colN];
n@1226 416 for (var element of page.elements) {
n@1226 417 var entry = [element.id];
n@1226 418 for (var i=0; i<page.elements[0].axis.length; i++) {
n@1226 419 var mean =0;
n@1226 420 if (i < element.axis.length) {
n@1226 421 var axis = element.axis[i];
n@1227 422 mean = arrayMean(axis.values);
n@1226 423 }
n@1226 424 entry.push(mean);
n@1226 425 }
n@1226 426 rows.push(entry);
n@1226 427 }
n@1227 428 chart.data.addRows(rows);
n@1227 429 chart.options = {
n@1227 430 'title':'Mean of page: '+page.id,
n@1227 431 'width':800,
n@1227 432 'height':700
n@1227 433 }
n@1226 434 // Draw the chart
n@1227 435 chart.chart = new google.visualization.ColumnChart(chart.chartDOM);
n@1227 436 chart.chart.draw(chart.data,chart.options);
n@1227 437 chart.buildTable();
n@1227 438 chart.writeLatex();
n@1227 439 this.charts.push(chart);
n@1227 440 }
n@1227 441 }
n@1227 442
n@1227 443 this.drawElementHistogram = function() {
n@1227 444 // First we must get the value data
n@1227 445 if (this.valueData == null) {
n@1227 446 console.log("Error - Data not loaded");
n@1227 447 return;
n@1227 448 }
n@1227 449 // We create one plot per element, enjoy...
n@1227 450 for (var page of this.valueData.pages) {
n@1227 451 for (var element of page.elements) {
n@1227 452 // Build the chart object
n@1227 453 var chart = new this.chartObject("histogram-element-"+element.id);
n@1227 454 document.getElementById("test-pages").appendChild(chart.root);
n@1227 455 chart.data.addColumn('string','index');
n@1227 456 var histograms = [];
n@1227 457 for (var axis of element.axis) {
n@1227 458 chart.data.addColumn('number',axis.name);
n@1227 459 histograms.push(arrayHistogram(axis.values,0.125,0.0,1.0));
n@1227 460 }
n@1227 461 for (var axis of element.axis) {
n@1227 462 for (var i=0; i<histograms[0].length; i++)
n@1227 463 {
n@1227 464 var entry = [""+histograms[0][i].lt.toPrecision(2)+"-"+histograms[0][i].rt.toPrecision(3)]
n@1227 465 for (var histogram of histograms) {
n@1227 466 entry.push(histogram[i].count);
n@1227 467 }
n@1227 468 chart.data.addRow(entry);
n@1227 469 }
n@1227 470 }
n@1227 471 chart.options = {
n@1227 472 'title':'Histogram of element: '+element.id,
n@1227 473 'width':800,
n@1227 474 'height':700,
n@1227 475 'bar':{'groupWidth': '100%'}
n@1227 476 }
n@1227 477 // Draw the chart
n@1227 478 chart.chart = new google.visualization.ColumnChart(chart.chartDOM);
n@1227 479 chart.chart.draw(chart.data,chart.options);
n@1227 480 chart.buildTable();
n@1227 481 chart.writeLatex();
n@1227 482 this.charts.push(chart);
n@1227 483 }
n@1226 484 }
n@1223 485 }
n@1223 486 }