annotate analysis/analysis.js @ 641:a4c0d5025208 Dev_main

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