<!DOCTYPE html>
<html>
<head>
<link data-require="bootstrap-css@2.3.2" data-semver="2.3.2" rel="stylesheet" href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" />
<script data-require="angular.js@1.0.7" data-semver="1.0.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script data-require="ui-bootstrap@0.4.0" data-semver="0.4.0" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.4.0.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<p>
Calls are logged to the console. By opening the console you can
see when the zScoreValue() function is being called. That function is
referenced in the popover attribute.
</p>
<div class="row-fluid" data-ng-app="my-app" data-ng-controller="my-controller" data-ng-init="initTrends()">
<div class="span12">
<table class="table table-bordered table-hover">
<tr><th>Row Name</th><th colspan="6">Values</th></tr>
<tr data-ng-repeat="row in rowNames">
<td><strong>{{ row }}</strong></td>
<td data-ng-repeat="v in getRowData(row)" class="zscore" popover="{{zscores[row][$index]}}" popover-trigger="mouseenter" popover-append-to-body="true" popover-title="zScore">
<!-- TODO : FIXME : the popover causes ALL cells to be recomputed when any cell is entered ! -->
{{ v | number:1 }}
</td>
</tr>
</table>
</div>
</div>
</body>
</html>
// Return an object with { stdDev:sigma, mean:average } since we compute both at the same time.
function stdDevAndMean(data) {
var ss=0; // sum of squares
var m=0; // arithmetic mean
var numer; // current (numeric) value being processed from the column data
var len = data.length;
for (var j = 0; j < len; j++) {
numer = data[j];
m += numer;
ss += Math.pow(numer,2);
}
m /= len;
ss = Math.sqrt((ss/len) - Math.pow(m,2));
return {stdDev:ss, mean:m};
}
function zScore(v, sdm) {
console.log("Computing zScore for " + v);
return (sdm.stdDev == 0) ? 0 : (v - sdm.mean) / sdm.stdDev;
}
var myApp = angular.module('my-app', ['ui.bootstrap']);
myApp.controller('my-controller', function ($scope) {
$scope.rowNames = ['linear','quadratic'];
$scope.rowData = {
'linear': [1, 2, 3, 4, 5, 6],
'quadratic': [1, 4, 9, 16, 25, 36]
};
$scope.trends = {};
$scope.zscores = {};
$scope.initTrends = function() {
var cTrends = {};
var sdm;
for (var row in $scope.rowData) {
var data = $scope.rowData[row];
sdm = stdDevAndMean(data);;
cTrends[row] = sdm;
var zs = [];
for (var i=0, len=data.length; i<len; ++i) {
zs.push(Math.round(zScore(data[i], sdm)*100) / 100);
}
$scope.zscores[row] = zs;
}
$scope.trends.c = cTrends;
}
$scope.getRowData = function(row) {
console.log("getRowData: " + row);
return $scope.rowData[row];
}
$scope.zScoreValue = function(rowName, dataV) {
if ($scope.trends.c != null) {
console.log("Computing zScore for " + rowName + ", from " + dataV);
return zScore(dataV, $scope.trends.c[rowName]);
} else {
return null;
}
}
});
/* Styles go here */