From b5ac2a1875cc0fad390f1e8d51d195d8ad9b308d Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Thu, 5 Nov 2015 16:58:07 -0500 Subject: [PATCH 01/32] feat(kmeans): add empty module template for kmeans #SOCRFW-178 --- app/partials/analysis/tools/kMeans/main.jade | 8 ++ .../analysis/tools/kMeans/sidebar.jade | 12 ++ app/scripts/analysis/tools/kmeans.coffee | 109 ++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 app/partials/analysis/tools/kMeans/main.jade create mode 100644 app/partials/analysis/tools/kMeans/sidebar.jade create mode 100644 app/scripts/analysis/tools/kmeans.coffee diff --git a/app/partials/analysis/tools/kMeans/main.jade b/app/partials/analysis/tools/kMeans/main.jade new file mode 100644 index 00000000..07d48a87 --- /dev/null +++ b/app/partials/analysis/tools/kMeans/main.jade @@ -0,0 +1,8 @@ +div(ng-controller="kMeansMainCtrl") + h2 k-means Clustering + p. + k-means clustering is a method of vector quantization, originally from signal processing, + that is popular for cluster analysis in data mining. + k-means clustering aims to partition n observations into k clusters in which each observation belongs + to the cluster with the nearest mean, serving as a prototype of the cluster. + This results in a partitioning of the data space into Voronoi cells. \ No newline at end of file diff --git a/app/partials/analysis/tools/kMeans/sidebar.jade b/app/partials/analysis/tools/kMeans/sidebar.jade new file mode 100644 index 00000000..ff398631 --- /dev/null +++ b/app/partials/analysis/tools/kMeans/sidebar.jade @@ -0,0 +1,12 @@ +div(ng-controller="kMeansSidebarCtrl") + form + fieldset + legend Data parameters + //label Number of columns  + // input(ng-model="nCols").input-small + label Labels column  + input(ng-model="labelCol").input-small + label k  + input(ng-model="k").input-small + div + button(type="submit", ng-click="run()").btn.btn-primary Run diff --git a/app/scripts/analysis/tools/kmeans.coffee b/app/scripts/analysis/tools/kmeans.coffee new file mode 100644 index 00000000..2d1a7cfc --- /dev/null +++ b/app/scripts/analysis/tools/kmeans.coffee @@ -0,0 +1,109 @@ +'use strict' + +kMeans = angular.module('app_analysis_kMeans', []) + +.factory('app_analysis_kMeans_constructor', [ + 'app_analysis_kMeans_manager' + (manager) -> + (sb) -> + + manager.setSb sb unless !sb? + _msgList = manager.getMsgList() + + init: (opt) -> + console.log 'kMeans init invoked' + + destroy: () -> + + msgList: _msgList + ]) + +.factory('app_analysis_kMeans_manager', [ + () -> + _sb = null + + _msgList = + outgoing: ['get table'] + incoming: ['take table'] + scope: ['kMeans'] + + _setSb = (sb) -> + _sb = sb + + _getSb = () -> + _sb + + _getMsgList = () -> + _msgList + + getSb: _getSb + setSb: _setSb + getMsgList: _getMsgList + ]) + +.controller('kMeansMainCtrl', [ + 'app_analysis_kMeans_manager' + 'app_analysis_kMeans_calculator' + '$scope' + (ctrlMngr, calculator, $scope) -> + console.log 'kMeansViewMainCtrl executed' + + prettifyArrayOutput = (arr) -> + if arr? + arr = arr.map (x) -> x.toFixed 3 + '[' + arr.toString().split(',').join('; ') + ']' + ]) + +.controller('kMeansSidebarCtrl', [ + 'app_analysis_kMeans_manager' + 'app_analysis_kMeans_calculator' + '$scope' + '$stateParams' + '$q' + (ctrlMngr, alphaCalculator, $scope, $stateParams, $q) -> + console.log 'kMeansViewSidebarCtrl executed' + + sb = ctrlMngr.getSb() + + $scope.nCols = '5' + $scope.nRows = '5' + + deferred = $q.defer() + + $scope.confLevel = 0.95 + + # subscribe for incoming message with data + token = sb.subscribe + msg: 'take table' + msgScope: ['kMeans'] + listener: (msg, data) -> + _data = data + $scope.nRows = _data.data?.length + $scope.nCols = _data.data[0]?.length + console.log data + alphaCalculator.calculate data, $scope.confLevel + + sb.publish + msg: 'get table' + msgScope: ['kMeans'] + callback: -> sb.unsubscribe token + data: + tableName: $stateParams.projectId + ':' + $stateParams.forkId + promise: deferred + ]) + +.factory('app_analysis_kMeans_calculator', [ + () -> + + _data = [] + + _calculate = (obj, confLevel) -> + _matrix = jStat obj.data + _matrix = jStat jStat.map _matrix, Number + + + _data = + result: _matrix + + calculate: _calculate + ]) \ No newline at end of file From 77db2554014fe875ee609247f4f856287dafeaa2 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Thu, 5 Nov 2015 20:20:09 -0500 Subject: [PATCH 02/32] feat(kmeans): add jsfeat to bower dependencies #SOCRFW-179 --- bower.json | 8 +++++++- test/karma.conf.coffee | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 01639c27..bbe521ea 100644 --- a/bower.json +++ b/bower.json @@ -24,7 +24,8 @@ "wrangler": "https://github.com/StanfordHCI/wrangler.git", "bootstrap": "^3.2.0", "angular-bootstrap": "~0.14.2", - "jstat": "~1.4.6" + "jstat": "~1.4.6", + "jsfeat": "~0.0.8" }, "devDependencies": { "angular-mocks": "^1.3.0" @@ -55,6 +56,11 @@ "main": [ "dist/jstat.js" ] + }, + "jsfeat": { + "main": [ + "build/jsfeat.js" + ] } } } diff --git a/test/karma.conf.coffee b/test/karma.conf.coffee index 90e5c402..f0239332 100644 --- a/test/karma.conf.coffee +++ b/test/karma.conf.coffee @@ -42,6 +42,7 @@ module.exports = (config) -> 'bower_components/bootstrap/dist/js/bootstrap.js' 'bower_components/angular-bootstrap/ui-bootstrap-tpls.js' 'bower_components/jstat/dist/jstat.js' + 'bower_components/jsfeat/build/jsfeat.js' 'bower_components/angular-mocks/angular-mocks.js' # endbower # bower:coffee From 55975b241380120b57ef0194314368d1ce5869d8 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Thu, 5 Nov 2015 21:22:14 -0500 Subject: [PATCH 03/32] feat(kmeans): create factory with kmeans calculations #SOCRFW-180 --- app/app.coffee | 30 +- app/index.jade | 1 + app/partials/analysis/tools/kMeans/main.jade | 7 +- .../analysis/tools/kMeans/sidebar.jade | 12 +- app/scripts/analysis/tools/kmeans.coffee | 314 ++++++++++++++++-- 5 files changed, 330 insertions(+), 34 deletions(-) diff --git a/app/app.coffee b/app/app.coffee index 27986892..3116ff13 100644 --- a/app/app.coffee +++ b/app/app.coffee @@ -29,6 +29,7 @@ App = angular.module('app', [ # 'app_analysis_qualRobEstView' # 'app_analysis_qualRobEst' 'app_analysis_instrPerfEval' + 'app_analysis_kMeans' ]) App.config([ @@ -100,15 +101,22 @@ App.config([ 'sidebar': templateUrl: 'partials/analysis/wrangleData/sidebar.html' ) +# .state('tools' +# url: '/tools' +# views: +# 'main': +# templateUrl: 'partials/analysis/tools/instrPerfEval/main.html' +# 'sidebar': +# templateUrl: 'partials/analysis/tools/instrPerfEval/sidebar.html' +# ) .state('tools' url: '/tools' views: 'main': - templateUrl: 'partials/analysis/tools/instrPerfEval/main.html' + templateUrl: 'partials/analysis/tools/kMeans/main.html' 'sidebar': - templateUrl: 'partials/analysis/tools/instrPerfEval/sidebar.html' + templateUrl: 'partials/analysis/tools/kMeans/sidebar.html' ) - .state('charts' url: '/charts/:projectId/:forkId' views: @@ -131,10 +139,11 @@ App.run([ # 'app_analysis_qualRobEst_constructor' # 'app_analysis_qualRobEstView_constructor' 'app_analysis_instrPerfEval_constructor' + 'app_analysis_kMeans_constructor' 'app_analysis_charts_constructor' #'app.utils.importer' # ($rootScope, core, db, getData, wrangleData, qualRobEst, qualRobEstView, instrPerfEval) -> - ($rootScope, core, db, getData, wrangleData, instrPerfEval) -> + ($rootScope, core, db, getData, wrangleData, instrPerfEval, kMeans) -> map = [ # msgFrom: 'add numbers' @@ -172,6 +181,16 @@ App.run([ scopeFrom: ['database'] msgTo: 'take table' scopeTo: ['instrPerfEval'] + , + msgFrom: 'get data' + scopeFrom: ['kMeans'] + msgTo: 'get table' + scopeTo: ['database'] + , + msgFrom: 'take table' + scopeFrom: ['database'] + msgTo: 'take data' + scopeTo: ['kMeans'] , msgFrom: 'get data' scopeFrom: ['wrangleData'] @@ -214,6 +233,9 @@ App.run([ core.register 'instrPerfEval', instrPerfEval core.start 'instrPerfEval' + core.register 'kMeans', kMeans + core.start 'kMeans' + #core.register 'importer', importer #core.start 'importer' diff --git a/app/index.jade b/app/index.jade index 009c0238..a85450ce 100644 --- a/app/index.jade +++ b/app/index.jade @@ -43,6 +43,7 @@ html(lang='en', ng-app='app') script(src='bower_components/bootstrap/dist/js/bootstrap.js') script(src='bower_components/angular-bootstrap/ui-bootstrap-tpls.js') script(src='bower_components/jstat/dist/jstat.js') + script(src='bower_components/jsfeat/build/jsfeat.js') // endbower body(ng-controller='AppCtrl') .container-fluid diff --git a/app/partials/analysis/tools/kMeans/main.jade b/app/partials/analysis/tools/kMeans/main.jade index 07d48a87..aa2648c4 100644 --- a/app/partials/analysis/tools/kMeans/main.jade +++ b/app/partials/analysis/tools/kMeans/main.jade @@ -1,8 +1,5 @@ div(ng-controller="kMeansMainCtrl") h2 k-means Clustering p. - k-means clustering is a method of vector quantization, originally from signal processing, - that is popular for cluster analysis in data mining. - k-means clustering aims to partition n observations into k clusters in which each observation belongs - to the cluster with the nearest mean, serving as a prototype of the cluster. - This results in a partitioning of the data space into Voronoi cells. \ No newline at end of file + k-means clustering aims to partition n observations into k clusters in which each observation belongs + to the cluster with the nearest mean, serving as a prototype of the cluster. diff --git a/app/partials/analysis/tools/kMeans/sidebar.jade b/app/partials/analysis/tools/kMeans/sidebar.jade index ff398631..9b97182e 100644 --- a/app/partials/analysis/tools/kMeans/sidebar.jade +++ b/app/partials/analysis/tools/kMeans/sidebar.jade @@ -1,12 +1,14 @@ div(ng-controller="kMeansSidebarCtrl") form fieldset - legend Data parameters - //label Number of columns  - // input(ng-model="nCols").input-small + legend Parameters label Labels column  - input(ng-model="labelCol").input-small + input(ng-model="labelCol").input-small label k  - input(ng-model="k").input-small + input(ng-model="k").input-small + label Distance  + input(ng-model="dist").input-small + label Initialization  + input(ng-model="initMethod").input-small div button(type="submit", ng-click="run()").btn.btn-primary Run diff --git a/app/scripts/analysis/tools/kmeans.coffee b/app/scripts/analysis/tools/kmeans.coffee index 2d1a7cfc..9b4a6e68 100644 --- a/app/scripts/analysis/tools/kmeans.coffee +++ b/app/scripts/analysis/tools/kmeans.coffee @@ -23,8 +23,8 @@ kMeans = angular.module('app_analysis_kMeans', []) _sb = null _msgList = - outgoing: ['get table'] - incoming: ['take table'] + outgoing: ['get data'] + incoming: ['take data'] scope: ['kMeans'] _setSb = (sb) -> @@ -45,8 +45,8 @@ kMeans = angular.module('app_analysis_kMeans', []) 'app_analysis_kMeans_manager' 'app_analysis_kMeans_calculator' '$scope' - (ctrlMngr, calculator, $scope) -> - console.log 'kMeansViewMainCtrl executed' + (ctrlMngr, kmeans, $scope) -> + console.log 'kMeansMainCtrl executed' prettifyArrayOutput = (arr) -> if arr? @@ -60,18 +60,18 @@ kMeans = angular.module('app_analysis_kMeans', []) '$scope' '$stateParams' '$q' - (ctrlMngr, alphaCalculator, $scope, $stateParams, $q) -> - console.log 'kMeansViewSidebarCtrl executed' + (ctrlMngr, kmeans, $scope, $stateParams, $q) -> + console.log 'kMeansSidebarCtrl executed' sb = ctrlMngr.getSb() - $scope.nCols = '5' - $scope.nRows = '5' + $scope.labelCol = '1' + $scope.k = '2' + $scope.dist = 'Euclidean' + $scope.initMethod = 'Forgy' deferred = $q.defer() - $scope.confLevel = 0.95 - # subscribe for incoming message with data token = sb.subscribe msg: 'take table' @@ -81,7 +81,7 @@ kMeans = angular.module('app_analysis_kMeans', []) $scope.nRows = _data.data?.length $scope.nCols = _data.data[0]?.length console.log data - alphaCalculator.calculate data, $scope.confLevel + kmeans.calculate data, $scope.confLevel sb.publish msg: 'get table' @@ -93,17 +93,291 @@ kMeans = angular.module('app_analysis_kMeans', []) ]) .factory('app_analysis_kMeans_calculator', [ - () -> + () -> + + _data = [] + + _data = + result: _matrix + + _getUniqueLabels = (labels) -> + labels.filter (x, i, a) -> i is a.indexOf x + + _distance = (v1, v2, type='euclidean', s=[]) -> + + euclidean = (v1, v2) -> + total = 0 + for i in [0..v1.length - 1] + total += Math.pow(v2[i] - v1[i], 2) + Math.sqrt(total) + + manhattan = (v1, v2) -> + total = 0 + for i in [0..v1.length - 1] + total += Math.abs(v2[i] - v1[i]); + total + + max = (v1, v2) -> + max = 0 + for i in [0..v1.length - 1] + max = Math.max(max , Math.abs(v2[i] - v1[i])); + max + + mahalanobis = (v1, v2, s) -> + + l = v1.length + invCov = s + + diff = (v1[k] - v2[k] for k in [0..l - 1]) + total = 0 + for row, i in invCov + for el, j in row + total += invCov[i][j] * Math.pow(diff[i], 2 - i - j) * Math.pow(diff[j], i + j) + Math.sqrt(total) + + if _arrayEqual v1, v2 + return 0 + else + switch type.toLowerCase() + when 'manhattan' then return manhattan v1, v2 + when 'max' then return max v1, v2 + when 'mahalanobis' then return mahalanobis v1, v2, s + else return euclidean v1, v2 + + _arrayEqual = (x, y) -> + a = x.slice().sort() + b = y.slice().sort() + a.length is b.length and a.every (elem, i) -> elem is b[i] + + _matrixMultiply = (a, b) -> + c = (0 for d1 in a.length for d2 in d3.transpose(b)) + for row, i in a + for col, j in d3.transpose(b) + c[i][j] = (row[k] * col[k] for el, k in row).reduce (t, s) -> t + s + c + + _initCentroids = (data, k) -> + nRows = data.length + centroids = [] + for ctr in [0..k - 1] + ctrIdx = Math.floor(Math.random() * nRows) + if centroids.length and ctrIdx is not centroids[ctr - 1].idx + ctrIdx = Math.floor(Math.random() * nRows) + centroids.push + val: data[ctrIdx] + idx: ctrIdx + centroids + + _initLabels = (l, k) -> + labels = [] + labels.push Math.floor(Math.random() * k) for i in [0..l] + labels + + _updateMeans = (data, centroids, labels) -> + ctrData = ([] for ctr in centroids) + for row, rowIdx in data + ctrData[labels[rowIdx]].push row + + means = [] + for ctr, ctrIdx in ctrData + colSums = (0 for col in data[0]) + for row in ctr + for el, elIdx in row + colSums[elIdx] += el + colMeans = colSums.map (x) -> x / ctr.length + means.push colMeans + + centroids = [] + for mean in means + distances = (_distance(row, mean, 'euclidean') for row in data) + ctrIdx = distances.indexOf(Math.min.apply @, distances) + # trying not to assign the same point + if ctrIdx not in centroids.map((x) -> x.idx) + centroids.push + val: data[ctrIdx] + idx: ctrIdx + else + distances = distances.splice(ctrIdx) + ctrIdx = distances.indexOf(Math.min.apply @, distances) + centroids.push + val: data[ctrIdx] + idx: ctrIdx + centroids + + _updatePrecisionMatrix = (data, ctrIdx, labels) -> + matrix = [] + for i in [0..data.length] + if labels[i] is ctrIdx + matrix.push data[i].slice() + # (matrix.push(row) for row, i in data when labels[i] is ctrIdx) + n = matrix.length + + matrixT = d3.transpose matrix + l = matrixT.length + means = (col.reduce((t, s) -> t + s) / n for col in matrixT) + + for row, i in matrix + for col, j in row + matrix[i][j] = col - means[j] + matrixT = d3.transpose(matrix) + + cov = (0 for e1 in [0..l - 1] for e2 in [0..l - 1]) + cov = _matrixMultiply matrixT, matrix + cov = cov.map((row) -> row.map((el) -> el / (n - 1))) - _data = [] + # calculate pseudo-inverse covariance matrix + tCov = new jsfeat.matrix_t l, l, jsfeat.F32_t | jsfeat.C1_t + covData = [] + (covData.push(e) for e in row for row in cov) + tCov.data = covData + tCovInv = new jsfeat.matrix_t l, l, jsfeat.F32_t | jsfeat.C1_t + jsfeat.linalg.svd_invert tCovInv, tCov - _calculate = (obj, confLevel) -> - _matrix = jStat obj.data - _matrix = jStat jStat.map _matrix, Number + invCov = (0 for e1 in [0..l - 1] for e2 in [0..l - 1]) + for row, i in invCov + for col, j in row + invCov[i][j] = tCovInv.data[2 * i + j] + invCov - _data = - result: _matrix + _assignSamples = (data, centroids, distanceType) -> + labels = [] + for row in data + distances = (_distance(row, ctr.val, distanceType) for ctr in centroids) + labels.push distances.indexOf(Math.min.apply @, distances) + labels - calculate: _calculate - ]) \ No newline at end of file + _runKMeans = (data, trueLabels, k, maxIter, centroids, distanceType, uniqueLabels, computeAcc) -> + + evaluateAccuracy = (labels, trueLabels, uniqueLabels) -> + accs = [0] + for k in uniqueLabels + kLabelIdxs = (i for x, i in trueLabels when x is k) + kLabels = (x for x, i in labels when i in kLabelIdxs) + kTrueLabels = (x for x in trueLabels when x is k) + accK = kLabels.map((x, idx) -> x - kTrueLabels[idx]).reduce (r, s) -> r + s + accs.push Math.abs(accK) + acc = (trueLabels.length - accs.reduce((r, s) -> r + s)) / trueLabels.length + acc = if acc < 0.5 then 1 - acc else acc + + step = (data, centroids, trueLabels) -> + maxIter-- + console.log 'Iteration: ' + maxIter + console.log 'Centroids: ' + console.table centroids + + labels = _assignSamples data, centroids, distanceType + means = _updateMeans data, centroids, labels + + console.log 'New means: ' + console.table means + if not _arrayEqual means.map((x) -> x.idx), centroids.map((x) -> x.idx) + centroids = means + Controller.redraw(data, means.map((x) -> x.val), labels) + else + maxIter = 0 + + centroids: centroids + labels: labels + + run = () -> + # main loop + if maxIter + res = step data, centroids, trueLabels + centroids = res.centroids + labels = res.labels + else + clearInterval interval + console.log 'K-Means done.' + if computeAcc + labels = _assignSamples data, centroids, distanceType + acc = evaluateAccuracy labels, trueLabels, uniqueLabels + console.log 'Accuracy: ' + acc * 100 + '%' + else + acc = '' + Core.fireEvent + msg: 'kmeans_done' + data: acc * 100 + + runMahalanobis = () -> + # main loop + if maxIter + maxIter-- + console.log 'Iteration: ' + maxIter + console.log 'Centroids: ' + console.table centroids + + means = centroids.slice() + + for row, i in data + + ctrDistances = (_distance(row, ctr.val, distanceType, covMats[j]) for ctr, j in centroids) + ctrIdx = ctrDistances.indexOf(Math.min.apply @, ctrDistances) + + if ctrIdx isnt lbls[i] + lbls[i] = ctrIdx + centroids = _updateMeans data, centroids, lbls + Controller.redraw(data, centroids.map((x) -> x.val), lbls) + for ctr, j in centroids + covMats[j] = _updatePrecisionMatrix(data, j, lbls) + + if _arrayEqual(means.map((x) -> x.idx), centroids.map((x) -> x.idx)) + maxIter = 0 + + else + clearInterval interval + console.log 'K-Means done.' + # labels = _assignSamples data, centroids, distanceType + if computeAcc + acc = evaluateAccuracy lbls, trueLabels, uniqueLabels + console.log 'Accuracy: ' + acc * 100 + '%' + else + acc = '' + Core.fireEvent + msg: 'kmeans_done' + data: acc * 100 + + if distanceType is 'mahalanobis' + labels = _assignSamples data, centroids, 'euclidean' + centroids = _updateMeans data, centroids, labels + covMats = [] + lbls = labels.slice() + for ctr, ctrIdx in centroids + covMats.push _updatePrecisionMatrix(data, ctrIdx, labels) + interval = setInterval runMahalanobis, 1000 + else + interval = setInterval run, 1000 + + _init = (obj, ctrl) -> + data = obj.data + data = (row.map(Number) for row in data) + labels = obj.labels + labels = labels.map (x) -> Number(x[0]) + computeAcc = on + + distanceType = ctrl.getDistanceType() + uniqueLabels = _getUniqueLabels(labels) + + k = Number k + console.log 'K: ' + k + + if k isnt 2 + computeAcc = off + + ctrl.drawDataPoints data + + initMethod = ctrl.getInitMethod() + if initMethod is 'forgy' + centroids = _initCentroids data, k + initLabels = _assignSamples data, centroids, 'euclidean' + else + initLabels = _initLabels data.length - 1, k + centroids = _updateMeans data, uniqueLabels, initLabels + + ctrl.redraw data, centroids.map((x) -> x.val), initLabels + + console.log 'Starting K-Means' + _runKMeans data, labels, k, maxIter, centroids, distanceType, uniqueLabels, computeAcc + + run: _init + ]) From 1c6573d764a0add6716b3f4414173f93668f14c8 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Mon, 9 Nov 2015 18:14:34 -0500 Subject: [PATCH 04/32] chore: add Papa Parse as bower dependency #SOCRFW-184 --- bower.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 240969da..96ab2440 100644 --- a/bower.json +++ b/bower.json @@ -25,7 +25,9 @@ "bootstrap": "^3.2.0", "angular-bootstrap": "~0.14.2", "jstat": "~1.4.6", - "jsfeat": "~0.0.8" + "jsfeat": "~0.0.8", + "papaparse": "~4.1.2", + "angular-papaparse": "~1.0.0" }, "devDependencies": { "angular-mocks": "^1.3.0" From 58efd290d8b69f0187b95d9c3affbc325580d548 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Mon, 9 Nov 2015 18:27:45 -0500 Subject: [PATCH 05/32] feat(getData): add option to load pre-defined SOCR dataset; add knee pain data #SOCRFW-184 --- app/index.jade | 2 + app/partials/analysis/getData/main.jade | 6 ++ app/partials/analysis/getData/sidebar.jade | 5 +- app/scripts/analysis/getData/getData.coffee | 102 ++++++++++++++++---- test/karma.conf.coffee | 2 + 5 files changed, 98 insertions(+), 19 deletions(-) diff --git a/app/index.jade b/app/index.jade index a85450ce..74db4b3b 100644 --- a/app/index.jade +++ b/app/index.jade @@ -44,6 +44,8 @@ html(lang='en', ng-app='app') script(src='bower_components/angular-bootstrap/ui-bootstrap-tpls.js') script(src='bower_components/jstat/dist/jstat.js') script(src='bower_components/jsfeat/build/jsfeat.js') + script(src='bower_components/papaparse/papaparse.js') + script(src='bower_components/angular-papaparse/dist/js/angular-PapaParse.js') // endbower body(ng-controller='AppCtrl') .container-fluid diff --git a/app/partials/analysis/getData/main.jade b/app/partials/analysis/getData/main.jade index e486d2cd..ef38fa5f 100644 --- a/app/partials/analysis/getData/main.jade +++ b/app/partials/analysis/getData/main.jade @@ -4,6 +4,12 @@ div(ng-controller='getDataMainCtrl') p.lead To perform any analysis, you need data to begin with. Choose one of the available methods |to get the data into the web-app. + div.socrData(collapse="showState.socrData") + h3 SOCR Datasets + select(style="width:100%; margin:0px",ng-model="option") + option(value='KNEE_PAIN') Simulated SOCR Knee Pain Centroid Location Data + button.btn.btn-medium(ng-click="getSocrData()") Load + div.worldBank(collapse="showState.worldBank") h3 WorldBank Data span Size: diff --git a/app/partials/analysis/getData/sidebar.jade b/app/partials/analysis/getData/sidebar.jade index a797ac26..6b901d35 100644 --- a/app/partials/analysis/getData/sidebar.jade +++ b/app/partials/analysis/getData/sidebar.jade @@ -2,13 +2,16 @@ div(ng-controller="getDataSidebarCtrl") span Traditional spreadsheet where you can copy-paste your data and edit it as you like! button.btn.btn-info.btn-sm(ng-click="show('grid')") Show hr + span Use one of the predefined SOCR datasets. + button.btn.btn-info.btn-sm(ng-click="show('socrData')") Use + hr span Use the exhaustive WorldBank data to fuel your analysis. button.btn.btn-info.btn-sm(ng-click="show('worldBank')") Use hr span Use the inbuilt experiment to generate data for your analysis. button.btn.btn-info.btn-sm(ng-click="show('generate')") Generate hr - span Enter json url here. We will try to give the best output! + span Enter JSON URL here to parse input(ng-model="jsonUrl",type="text", tooltip="Enter url here...",tooltip-trigger="focus", tooltip-placement="right").input-medium diff --git a/app/scripts/analysis/getData/getData.coffee b/app/scripts/analysis/getData/getData.coffee index d4ff095e..8ed60818 100644 --- a/app/scripts/analysis/getData/getData.coffee +++ b/app/scripts/analysis/getData/getData.coffee @@ -149,10 +149,49 @@ getData = angular.module('app_analysis_getData', [ console.log deferred.promise switch opts.type + when 'worldBank' - #create the callback + # create the callback + cb = (data, status) -> + # obj[0] will contain meta deta + # obj[1] will contain array + _col = [] + _column = [] + tree = [] + + count = (obj) -> + try + if typeof obj is 'object' and obj isnt null + for key in Object.keys obj + tree.push key + count obj[key] + tree.pop() + else + _col.push tree.join('.') + return _col + catch e + console.log e.message + return true + + # generate titles and references + count data[1][0] + # format data + for c in _col + _column.push + data: c + + # return object + data: data + columns: _column + columnHeader: _col + # purpose is helps in pin pointing which + # handsontable directive to update. + purpose: 'json' + + when 'socrData' + # create the callback cb = (data, status) -> - # obj[0] will contain meta deta. + # obj[0] will contain meta deta # obj[1] will contain array _col = [] _column = [] @@ -172,27 +211,28 @@ getData = angular.module('app_analysis_getData', [ console.log e.message return true - #generate titles and references + # generate titles and references count data[1][0] # format data for c in _col _column.push data: c - #return object + # return object data: data columns: _column columnHeader: _col # purpose is helps in pin pointing which # handsontable directive to update. purpose: 'json' + else #default implementation cb = (data, status) -> console.log data return data - # using broadcast because msg sent from rootScope. + # using broadcast because msg sent from rootScope $rootScope.$broadcast 'app:push notification', initial: msg: 'Asking worldbank...' @@ -205,7 +245,7 @@ getData = angular.module('app_analysis_getData', [ type: 'alert-error' promise: deferred.promise - #make the call using the cb we just created + # make the call using the cb we just created $http.jsonp( opts.url ) @@ -219,6 +259,7 @@ getData = angular.module('app_analysis_getData', [ console.log 'promise rejected' deferred.reject 'promise is rejected' ) + deferred.promise ]) @@ -233,13 +274,13 @@ getData = angular.module('app_analysis_getData', [ '$stateParams' 'app_analysis_getData_inputCache' ($q, $scope, getDataEventMngr, jsonParser, $stateParams, inputCache) -> - #get the sandbox made for this module - #sb = getDataSb.getSb() - #console.log 'sandbox created' + # get the sandbox made for this module + # sb = getDataSb.getSb() + # console.log 'sandbox created' $scope.jsonUrl = 'url..' flag = true - #showGrid + # showGrid $scope.show = (val) -> switch val when 'grid' @@ -252,13 +293,16 @@ getData = angular.module('app_analysis_getData', [ $scope.$emit 'update handsontable', data $scope.$emit 'change in showStates', 'grid' + when 'socrData' + $scope.$emit 'change in showStates', 'socrData' + when 'worldBank' $scope.$emit 'change in showStates', 'worldBank' when 'generate' $scope.$emit 'change in showStates', 'generate' - #getJson + # getJson $scope.getJson = -> console.log $scope.jsonUrl @@ -280,11 +324,10 @@ getData = angular.module('app_analysis_getData', [ console.log 'rejected' ) - #get url data + # get url data $scope.getUrl = -> $scope.getGrid = -> - return ]) .controller('getDataMainCtrl', [ @@ -297,10 +340,10 @@ getData = angular.module('app_analysis_getData', [ console.log 'getDataMainCtrl executed' $scope.getWB = -> - #default value + # default value if $scope.size is undefined $scope.size = 100 - #default option + # default option if $scope.option is undefined $scope.option = '4.2_BASIC.EDU.SPENDING' @@ -325,8 +368,31 @@ getData = angular.module('app_analysis_getData', [ console.log 'rejected:' + msg ) + $scope.getSocrData = -> + # default option + if $scope.option is undefined + $scope.option = 'KNEE_PAIN' + + url = 'https://www.googledrive.com/host//0BzJubeARG-hsLUU1Ul9WekZRV0U' + + Papa.parse url, + download: true, + complete: (dataResults) -> + if dataResults and dataResults.data?.length > 0 + _data = dataResults.data + _data.pop() + console.log 'resolved' + # pass a message to update the handsontable div + # data is the formatted data which plugs into the + # handontable. + $scope.$emit 'update handsontable', _data + # switch the accordion from getJson to grid + # $scope.$emit("change in showStates","grid") + else + console.log 'rejected:' + msg + try - _showState = new showState(['grid', 'worldBank', 'generate'], $scope) + _showState = new showState(['grid', 'socrData', 'worldBank', 'generate'], $scope) catch e console.log e.message @@ -342,7 +408,7 @@ getData = angular.module('app_analysis_getData', [ .factory('showState', -> (obj, scope) -> if arguments.length is 0 - #return false if no arguments are provided + # return false if no arguments are provided return false _obj = obj @@ -351,7 +417,7 @@ getData = angular.module('app_analysis_getData', [ for i in obj scope.showState[i] = true - # index is the array key. + # index is the array key set: (index) -> if scope.showState[index]? for i in _obj diff --git a/test/karma.conf.coffee b/test/karma.conf.coffee index f0239332..239f7a55 100644 --- a/test/karma.conf.coffee +++ b/test/karma.conf.coffee @@ -43,6 +43,8 @@ module.exports = (config) -> 'bower_components/angular-bootstrap/ui-bootstrap-tpls.js' 'bower_components/jstat/dist/jstat.js' 'bower_components/jsfeat/build/jsfeat.js' + 'bower_components/papaparse/papaparse.js' + 'bower_components/angular-papaparse/dist/js/angular-PapaParse.js' 'bower_components/angular-mocks/angular-mocks.js' # endbower # bower:coffee From 66b7db978f85841b4d419242a5203000d19f1433 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Tue, 10 Nov 2015 19:00:30 -0500 Subject: [PATCH 06/32] fix(getData): make SOCR data appear in handsontable #SOCRFW-184 --- app/scripts/analysis/getData/getData.coffee | 61 ++++++--------------- 1 file changed, 17 insertions(+), 44 deletions(-) diff --git a/app/scripts/analysis/getData/getData.coffee b/app/scripts/analysis/getData/getData.coffee index 8ed60818..8c12ddc2 100644 --- a/app/scripts/analysis/getData/getData.coffee +++ b/app/scripts/analysis/getData/getData.coffee @@ -188,44 +188,6 @@ getData = angular.module('app_analysis_getData', [ # handsontable directive to update. purpose: 'json' - when 'socrData' - # create the callback - cb = (data, status) -> - # obj[0] will contain meta deta - # obj[1] will contain array - _col = [] - _column = [] - tree = [] - - count = (obj) -> - try - if typeof obj is 'object' and obj isnt null - for key in Object.keys obj - tree.push key - count obj[key] - tree.pop() - else - _col.push tree.join('.') - return _col - catch e - console.log e.message - return true - - # generate titles and references - count data[1][0] - # format data - for c in _col - _column.push - data: c - - # return object - data: data - columns: _column - columnHeader: _col - # purpose is helps in pin pointing which - # handsontable directive to update. - purpose: 'json' - else #default implementation cb = (data, status) -> @@ -360,6 +322,8 @@ getData = angular.module('app_analysis_getData', [ # Pass a message to update the handsontable div. # data is the formatted data which plugs into the # handontable. + + # TODO: getData module shouldn't know about controllers listening for handsontable update $scope.$emit 'update handsontable', data # Switch the accordion from getJson to grid. #$scope.$emit("change in showStates","grid") @@ -379,8 +343,12 @@ getData = angular.module('app_analysis_getData', [ download: true, complete: (dataResults) -> if dataResults and dataResults.data?.length > 0 - _data = dataResults.data - _data.pop() + _data = + columnHeader: dataResults.data.shift() + data: [null, dataResults.data] + # purpose is helps in pin pointing which + # handsontable directive to update. + purpose: 'json' console.log 'resolved' # pass a message to update the handsontable div # data is the formatted data which plugs into the @@ -459,6 +427,7 @@ getData = angular.module('app_analysis_getData', [ nCols: tableData.nCols - nSpareCols _toHandsontable = () -> + # TODO: implement for poping up data when coming back from analysis tabs toDataFrame: _toDataFrame toHandsontable: _toHandsontable @@ -514,14 +483,18 @@ getData = angular.module('app_analysis_getData', [ currHeight = elem.height() #check if data is in the right format - if arg? and typeof arg.data is 'object' and typeof arg.columns is 'object' +# if arg? and typeof arg.data is 'object' and typeof arg.columns is 'object' + if arg? and typeof arg.data is 'object' obj = data: arg.data[1] - startRows: Object.keys(arg.data[1]).length - startCols: arg.columns.length +# startRows: Object.keys(arg.data[1]).length +# startCols: arg.columns.length colHeaders: arg.columnHeader - columns: arg.columns +# columns: arg.columns minSpareRows: N_SPARE_ROWS + minSpareCols: N_SPARE_COLS + allowInsertRow: true + allowInsertColumn: true else if arg.default is true obj = data: [ From c6716bf8150121f206a4e91420e086f804c2c692 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Wed, 11 Nov 2015 18:48:50 -0500 Subject: [PATCH 07/32] chore: switch to custom wrangler fork to relax data size limitations #SOCRFW-182 --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 96ab2440..d5f46fc7 100644 --- a/bower.json +++ b/bower.json @@ -21,7 +21,7 @@ "angular-ui": "~0.4.0", "angular-ui-router": "~0.2.15", "ngHandsontable": "angular-ui-handsontable#~0.7.0", - "wrangler": "https://github.com/StanfordHCI/wrangler.git", + "wrangler": "https://github.com/alxndrkalinin/wrangler.git", "bootstrap": "^3.2.0", "angular-bootstrap": "~0.14.2", "jstat": "~1.4.6", From a49d3195491af796527eddd3359a5200c2f23435 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Tue, 17 Nov 2015 16:29:15 -0500 Subject: [PATCH 08/32] fix(kmeans): fix invalid message names #SOCRFW-182 --- app/scripts/analysis/tools/kmeans.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/scripts/analysis/tools/kmeans.coffee b/app/scripts/analysis/tools/kmeans.coffee index 9b4a6e68..170c2b50 100644 --- a/app/scripts/analysis/tools/kmeans.coffee +++ b/app/scripts/analysis/tools/kmeans.coffee @@ -74,7 +74,7 @@ kMeans = angular.module('app_analysis_kMeans', []) # subscribe for incoming message with data token = sb.subscribe - msg: 'take table' + msg: 'take data' msgScope: ['kMeans'] listener: (msg, data) -> _data = data @@ -84,7 +84,7 @@ kMeans = angular.module('app_analysis_kMeans', []) kmeans.calculate data, $scope.confLevel sb.publish - msg: 'get table' + msg: 'get data' msgScope: ['kMeans'] callback: -> sb.unsubscribe token data: @@ -97,8 +97,8 @@ kMeans = angular.module('app_analysis_kMeans', []) _data = [] - _data = - result: _matrix +# _data = +# result: _matrix _getUniqueLabels = (labels) -> labels.filter (x, i, a) -> i is a.indexOf x From 80c6eb93d98b191e414eaae0c28aa9fbfbb07f38 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Tue, 17 Nov 2015 19:14:33 -0500 Subject: [PATCH 09/32] feat(kmeans): add sibebar controls for kmeans parameters #SOCRFW-185 --- .../analysis/tools/kMeans/sidebar.jade | 36 ++++++++++++------- .../analysis/tools/{ => kMeans}/kmeans.coffee | 9 ++--- 2 files changed, 29 insertions(+), 16 deletions(-) rename app/scripts/analysis/tools/{ => kMeans}/kmeans.coffee (98%) diff --git a/app/partials/analysis/tools/kMeans/sidebar.jade b/app/partials/analysis/tools/kMeans/sidebar.jade index 9b97182e..ba5035e2 100644 --- a/app/partials/analysis/tools/kMeans/sidebar.jade +++ b/app/partials/analysis/tools/kMeans/sidebar.jade @@ -1,14 +1,26 @@ div(ng-controller="kMeansSidebarCtrl") form - fieldset - legend Parameters - label Labels column  - input(ng-model="labelCol").input-small - label k  - input(ng-model="k").input-small - label Distance  - input(ng-model="dist").input-small - label Initialization  - input(ng-model="initMethod").input-small - div - button(type="submit", ng-click="run()").btn.btn-primary Run + legend Parameters + div.form-group + label Labels column   + //- show data header column names + select(ng-model="labelCol", ng-options="col for col in cols").form-control + div.form-group + label k   + // k from 2 to 10, uses d3.scale.category10() + //- generate options for k from 2 to 10 + - var k = 2 + select(ng-model="k").form-control + while k <= 10 + option(value=k)= k++ + div.form-group + label Distance   + select(ng-model="dist").form-control + each dist in ['Euclidean', 'Mahalanobis', 'Manhattan', 'Maximum'] + option(value=dist)= dist + div.form-group + label Initialization   + select(ng-model="initMethod").form-control + each method in ['Forgy', 'Random Partition'] + option(value=method)= method + button(type="submit", ng-click="run()").btn.btn-primary Run diff --git a/app/scripts/analysis/tools/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee similarity index 98% rename from app/scripts/analysis/tools/kmeans.coffee rename to app/scripts/analysis/tools/kMeans/kmeans.coffee index 170c2b50..5ee8bcfb 100644 --- a/app/scripts/analysis/tools/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -65,7 +65,7 @@ kMeans = angular.module('app_analysis_kMeans', []) sb = ctrlMngr.getSb() - $scope.labelCol = '1' + $scope.cols = [] $scope.k = '2' $scope.dist = 'Euclidean' $scope.initMethod = 'Forgy' @@ -78,10 +78,11 @@ kMeans = angular.module('app_analysis_kMeans', []) msgScope: ['kMeans'] listener: (msg, data) -> _data = data - $scope.nRows = _data.data?.length - $scope.nCols = _data.data[0]?.length + $scope.cols = _data.header + [..., lastCol] = $scope.cols + $scope.labelCol = lastCol console.log data - kmeans.calculate data, $scope.confLevel +# kmeans.run data sb.publish msg: 'get data' From 70a62c9b0ec77fae4227995644408b27e53ba840 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Wed, 18 Nov 2015 18:49:55 -0500 Subject: [PATCH 10/32] feat(kmeans): add sidebar controls for choosing columns #SOCRFW-185 --- app/partials/analysis/tools/kMeans/sidebar.jade | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/app/partials/analysis/tools/kMeans/sidebar.jade b/app/partials/analysis/tools/kMeans/sidebar.jade index ba5035e2..7ba6171b 100644 --- a/app/partials/analysis/tools/kMeans/sidebar.jade +++ b/app/partials/analysis/tools/kMeans/sidebar.jade @@ -2,9 +2,22 @@ div(ng-controller="kMeansSidebarCtrl") form legend Parameters div.form-group - label Labels column   + label X column   //- show data header column names - select(ng-model="labelCol", ng-options="col for col in cols").form-control + select(ng-model="xCol", ng-options="col for col in cols").form-control + div.form-group + label Y column   + //- show data header column names + select(ng-model="yCol", ng-options="col for col in cols").form-control + div.checkbox + label + input(type="checkbox", ng-model="labelson") + | Use labels column + div.form-group + fieldset(ng-disabled="!labelson") + label Labels column   + //- show data header column names + select(ng-model="labelCol", ng-options="col for col in cols").form-control div.form-group label k   // k from 2 to 10, uses d3.scale.category10() From ec3b1ff724805c0561c641ccc76ff7e44e780933 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Wed, 18 Nov 2015 19:17:40 -0500 Subject: [PATCH 11/32] feat(kmeans): pass controls from sidebar to kmeans algorithm #SOCRFW-185 --- .../analysis/tools/kMeans/kmeans.coffee | 108 +++++++++++------- 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index 5ee8bcfb..12936fac 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -52,55 +52,73 @@ kMeans = angular.module('app_analysis_kMeans', []) if arr? arr = arr.map (x) -> x.toFixed 3 '[' + arr.toString().split(',').join('; ') + ']' + + _redraw = () -> + _drawDataPoints = -> + + # TODO: consider using messages instead + graph = + redraw: _redraw + drawDataPoints: _drawDataPoints + kmeans.setGraph graph ]) .controller('kMeansSidebarCtrl', [ - 'app_analysis_kMeans_manager' - 'app_analysis_kMeans_calculator' - '$scope' - '$stateParams' - '$q' - (ctrlMngr, kmeans, $scope, $stateParams, $q) -> - console.log 'kMeansSidebarCtrl executed' - - sb = ctrlMngr.getSb() - - $scope.cols = [] - $scope.k = '2' - $scope.dist = 'Euclidean' - $scope.initMethod = 'Forgy' - - deferred = $q.defer() - - # subscribe for incoming message with data - token = sb.subscribe - msg: 'take data' - msgScope: ['kMeans'] - listener: (msg, data) -> - _data = data - $scope.cols = _data.header - [..., lastCol] = $scope.cols - $scope.labelCol = lastCol - console.log data -# kmeans.run data - - sb.publish - msg: 'get data' - msgScope: ['kMeans'] - callback: -> sb.unsubscribe token - data: - tableName: $stateParams.projectId + ':' + $stateParams.forkId - promise: deferred + 'app_analysis_kMeans_manager' + 'app_analysis_kMeans_calculator' + '$scope' + '$stateParams' + '$q' + (ctrlMngr, kmeans, $scope, $stateParams, $q) -> + console.log 'kMeansSidebarCtrl executed' + + sb = ctrlMngr.getSb() + + $scope.cols = [] + $scope.k = '2' + $scope.dist = 'Euclidean' + $scope.initMethod = 'Forgy' + + deferred = $q.defer() + + # subscribe for incoming message with data + token = sb.subscribe + msg: 'take data' + msgScope: ['kMeans'] + listener: (msg, data) -> + _data = data + $scope.cols = _data.header + [firstCol, secondCol, ..., lastCol] = $scope.cols + $scope.xCol = firstCol + $scope.yCol = secondCol + $scope.labelCol = lastCol + $scope.run = -> + xCol = _data.header.indexOf $scope.xCol + yCol = _data.header.indexOf $scope.yCol + data = ([row[xCol], row[yCol]] for row in _data.data) + kmeans.run data, $scope.k, $scope.dist, $scope.initMethod, $scope.labelCol + + sb.publish + msg: 'get data' + msgScope: ['kMeans'] + callback: -> sb.unsubscribe token + data: + tableName: $stateParams.projectId + ':' + $stateParams.forkId + promise: deferred ]) .factory('app_analysis_kMeans_calculator', [ () -> _data = [] + _graph = null # _data = # result: _matrix + _setGraph = (graph) -> + _graph = graph + _getUniqueLabels = (labels) -> labels.filter (x, i, a) -> i is a.indexOf x @@ -349,14 +367,15 @@ kMeans = angular.module('app_analysis_kMeans', []) else interval = setInterval run, 1000 - _init = (obj, ctrl) -> - data = obj.data + _init = (data, k, distanceType, initMethod, labels) -> data = (row.map(Number) for row in data) - labels = obj.labels labels = labels.map (x) -> Number(x[0]) computeAcc = on - distanceType = ctrl.getDistanceType() + distanceType = distanceType.toLowerCase() + initMethod = initMethod.toLowerCase() + +# distanceType = ctrl.getDistanceType() uniqueLabels = _getUniqueLabels(labels) k = Number k @@ -365,9 +384,9 @@ kMeans = angular.module('app_analysis_kMeans', []) if k isnt 2 computeAcc = off - ctrl.drawDataPoints data + _graph.drawDataPoints data - initMethod = ctrl.getInitMethod() +# initMethod = ctrl.getInitMethod() if initMethod is 'forgy' centroids = _initCentroids data, k initLabels = _assignSamples data, centroids, 'euclidean' @@ -375,10 +394,11 @@ kMeans = angular.module('app_analysis_kMeans', []) initLabels = _initLabels data.length - 1, k centroids = _updateMeans data, uniqueLabels, initLabels - ctrl.redraw data, centroids.map((x) -> x.val), initLabels + _graph.redraw data, centroids.map((x) -> x.val), initLabels console.log 'Starting K-Means' _runKMeans data, labels, k, maxIter, centroids, distanceType, uniqueLabels, computeAcc - run: _init + run: _init + setGraph: _setGraph ]) From 825b0eb55b9edb95615204e406ba29e4f14084dc Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Wed, 18 Nov 2015 20:06:21 -0500 Subject: [PATCH 12/32] fix(kmeans): accept control values as parameters; allow non-numeric labels #SOCRFW-182 --- app/partials/analysis/tools/kMeans/main.jade | 2 +- .../analysis/tools/kMeans/kmeans.coffee | 60 ++++++++++--------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/app/partials/analysis/tools/kMeans/main.jade b/app/partials/analysis/tools/kMeans/main.jade index aa2648c4..fb54d0e7 100644 --- a/app/partials/analysis/tools/kMeans/main.jade +++ b/app/partials/analysis/tools/kMeans/main.jade @@ -1,5 +1,5 @@ div(ng-controller="kMeansMainCtrl") - h2 k-means Clustering + h2 2D k-means Clustering p. k-means clustering aims to partition n observations into k clusters in which each observation belongs to the cluster with the nearest mean, serving as a prototype of the cluster. diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index 12936fac..ca1bb982 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -95,8 +95,13 @@ kMeans = angular.module('app_analysis_kMeans', []) $scope.run = -> xCol = _data.header.indexOf $scope.xCol yCol = _data.header.indexOf $scope.yCol + if $scope.labelson + labelCol = _data.header.indexOf $scope.labelCol + labels = (row[labelCol] for row in _data.data) + else + labels = null data = ([row[xCol], row[yCol]] for row in _data.data) - kmeans.run data, $scope.k, $scope.dist, $scope.initMethod, $scope.labelCol + kmeans.run data, $scope.k, $scope.dist, $scope.initMethod, labels sb.publish msg: 'get data' @@ -112,6 +117,8 @@ kMeans = angular.module('app_analysis_kMeans', []) _data = [] _graph = null + _computeAcc = off + _maxIter = 20 # _data = # result: _matrix @@ -266,7 +273,7 @@ kMeans = angular.module('app_analysis_kMeans', []) labels.push distances.indexOf(Math.min.apply @, distances) labels - _runKMeans = (data, trueLabels, k, maxIter, centroids, distanceType, uniqueLabels, computeAcc) -> + _runKMeans = (data, k, maxIter, centroids, distanceType, uniqueLabels, trueLabels=null) -> evaluateAccuracy = (labels, trueLabels, uniqueLabels) -> accs = [0] @@ -279,7 +286,7 @@ kMeans = angular.module('app_analysis_kMeans', []) acc = (trueLabels.length - accs.reduce((r, s) -> r + s)) / trueLabels.length acc = if acc < 0.5 then 1 - acc else acc - step = (data, centroids, trueLabels) -> + step = (data, centroids) -> maxIter-- console.log 'Iteration: ' + maxIter console.log 'Centroids: ' @@ -292,7 +299,7 @@ kMeans = angular.module('app_analysis_kMeans', []) console.table means if not _arrayEqual means.map((x) -> x.idx), centroids.map((x) -> x.idx) centroids = means - Controller.redraw(data, means.map((x) -> x.val), labels) + _graph.redraw(data, means.map((x) -> x.val), labels) else maxIter = 0 @@ -302,21 +309,21 @@ kMeans = angular.module('app_analysis_kMeans', []) run = () -> # main loop if maxIter - res = step data, centroids, trueLabels + res = step data, centroids centroids = res.centroids labels = res.labels else clearInterval interval console.log 'K-Means done.' - if computeAcc + if _computeAcc labels = _assignSamples data, centroids, distanceType acc = evaluateAccuracy labels, trueLabels, uniqueLabels console.log 'Accuracy: ' + acc * 100 + '%' else acc = '' - Core.fireEvent - msg: 'kmeans_done' - data: acc * 100 +# Core.fireEvent +# msg: 'kmeans_done' +# data: acc * 100 runMahalanobis = () -> # main loop @@ -336,7 +343,7 @@ kMeans = angular.module('app_analysis_kMeans', []) if ctrIdx isnt lbls[i] lbls[i] = ctrIdx centroids = _updateMeans data, centroids, lbls - Controller.redraw(data, centroids.map((x) -> x.val), lbls) + _graph.redraw(data, centroids.map((x) -> x.val), lbls) for ctr, j in centroids covMats[j] = _updatePrecisionMatrix(data, j, lbls) @@ -346,15 +353,14 @@ kMeans = angular.module('app_analysis_kMeans', []) else clearInterval interval console.log 'K-Means done.' - # labels = _assignSamples data, centroids, distanceType - if computeAcc + if _computeAcc acc = evaluateAccuracy lbls, trueLabels, uniqueLabels console.log 'Accuracy: ' + acc * 100 + '%' else acc = '' - Core.fireEvent - msg: 'kmeans_done' - data: acc * 100 +# Core.fireEvent +# msg: 'kmeans_done' +# data: acc * 100 if distanceType is 'mahalanobis' labels = _assignSamples data, centroids, 'euclidean' @@ -367,22 +373,22 @@ kMeans = angular.module('app_analysis_kMeans', []) else interval = setInterval run, 1000 - _init = (data, k, distanceType, initMethod, labels) -> + _init = (data, k, distanceType, initMethod, labels=null) -> data = (row.map(Number) for row in data) - labels = labels.map (x) -> Number(x[0]) - computeAcc = on - - distanceType = distanceType.toLowerCase() - initMethod = initMethod.toLowerCase() - -# distanceType = ctrl.getDistanceType() - uniqueLabels = _getUniqueLabels(labels) k = Number k console.log 'K: ' + k - if k isnt 2 - computeAcc = off + if labels + uniqueLabels = _getUniqueLabels(labels) + # compute accuracy only when # of clusters is equal to number of unique labels + _computeAcc = if uniqueLabels.length is k then on else off + else + uniqueLabels = [0..k-1] + _computeAcc = off + + distanceType = distanceType.toLowerCase() + initMethod = initMethod.toLowerCase() _graph.drawDataPoints data @@ -397,7 +403,7 @@ kMeans = angular.module('app_analysis_kMeans', []) _graph.redraw data, centroids.map((x) -> x.val), initLabels console.log 'Starting K-Means' - _runKMeans data, labels, k, maxIter, centroids, distanceType, uniqueLabels, computeAcc + _runKMeans data, k, _maxIter, centroids, distanceType, uniqueLabels, labels run: _init setGraph: _setGraph From 97cc9933d95c53e5095b8b3e3658ef24f3743486 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Wed, 18 Nov 2015 20:27:38 -0500 Subject: [PATCH 13/32] fix(kmeans): make kmeans work within framework #SOCRFW-182 --- app/scripts/analysis/tools/kMeans/kmeans.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index ca1bb982..0b2a6d66 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -276,10 +276,13 @@ kMeans = angular.module('app_analysis_kMeans', []) _runKMeans = (data, k, maxIter, centroids, distanceType, uniqueLabels, trueLabels=null) -> evaluateAccuracy = (labels, trueLabels, uniqueLabels) -> + # TODO: make accuracy work for k > 2 accs = [0] for k in uniqueLabels + # get true indices for label k kLabelIdxs = (i for x, i in trueLabels when x is k) - kLabels = (x for x, i in labels when i in kLabelIdxs) + # get calculated labels by true indices + kLabels = (uniqueLabels[x] for x, i in labels when i in kLabelIdxs) kTrueLabels = (x for x in trueLabels when x is k) accK = kLabels.map((x, idx) -> x - kTrueLabels[idx]).reduce (r, s) -> r + s accs.push Math.abs(accK) From c3220ac2913342ad06c964713c59f27f59d30e95 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Fri, 20 Nov 2015 16:48:46 -0500 Subject: [PATCH 14/32] refactor(getData): switch from PapaParse to D3 for loading and parsing CSV files #SOCRFW-184 --- app/index.jade | 1 - app/scripts/analysis/getData/getData.coffee | 15 +++++++++------ bower.json | 4 +--- test/karma.conf.coffee | 1 - 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/index.jade b/app/index.jade index 74db4b3b..507f28ec 100644 --- a/app/index.jade +++ b/app/index.jade @@ -45,7 +45,6 @@ html(lang='en', ng-app='app') script(src='bower_components/jstat/dist/jstat.js') script(src='bower_components/jsfeat/build/jsfeat.js') script(src='bower_components/papaparse/papaparse.js') - script(src='bower_components/angular-papaparse/dist/js/angular-PapaParse.js') // endbower body(ng-controller='AppCtrl') .container-fluid diff --git a/app/scripts/analysis/getData/getData.coffee b/app/scripts/analysis/getData/getData.coffee index 8c12ddc2..f493a805 100644 --- a/app/scripts/analysis/getData/getData.coffee +++ b/app/scripts/analysis/getData/getData.coffee @@ -339,13 +339,16 @@ getData = angular.module('app_analysis_getData', [ url = 'https://www.googledrive.com/host//0BzJubeARG-hsLUU1Ul9WekZRV0U' - Papa.parse url, - download: true, - complete: (dataResults) -> - if dataResults and dataResults.data?.length > 0 +# Papa.parse url, +# download: true, +# complete: (dataResults) -> + d3.text url, + (dataResults) -> + if dataResults?.length > 0 + dataResults = d3.csv.parseRows dataResults _data = - columnHeader: dataResults.data.shift() - data: [null, dataResults.data] + columnHeader: dataResults.shift() + data: [null, dataResults] # purpose is helps in pin pointing which # handsontable directive to update. purpose: 'json' diff --git a/bower.json b/bower.json index d5f46fc7..a0c5f320 100644 --- a/bower.json +++ b/bower.json @@ -25,9 +25,7 @@ "bootstrap": "^3.2.0", "angular-bootstrap": "~0.14.2", "jstat": "~1.4.6", - "jsfeat": "~0.0.8", - "papaparse": "~4.1.2", - "angular-papaparse": "~1.0.0" + "jsfeat": "~0.0.8" }, "devDependencies": { "angular-mocks": "^1.3.0" diff --git a/test/karma.conf.coffee b/test/karma.conf.coffee index 239f7a55..aff8bda0 100644 --- a/test/karma.conf.coffee +++ b/test/karma.conf.coffee @@ -44,7 +44,6 @@ module.exports = (config) -> 'bower_components/jstat/dist/jstat.js' 'bower_components/jsfeat/build/jsfeat.js' 'bower_components/papaparse/papaparse.js' - 'bower_components/angular-papaparse/dist/js/angular-PapaParse.js' 'bower_components/angular-mocks/angular-mocks.js' # endbower # bower:coffee From da10cc974ee595e5222ee0254e0ac4b7f018791c Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Fri, 20 Nov 2015 16:51:14 -0500 Subject: [PATCH 15/32] refactor(getData): clean comments #SOCRFW-184 --- app/scripts/analysis/getData/getData.coffee | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/scripts/analysis/getData/getData.coffee b/app/scripts/analysis/getData/getData.coffee index f493a805..d9d9cb7c 100644 --- a/app/scripts/analysis/getData/getData.coffee +++ b/app/scripts/analysis/getData/getData.coffee @@ -339,9 +339,6 @@ getData = angular.module('app_analysis_getData', [ url = 'https://www.googledrive.com/host//0BzJubeARG-hsLUU1Ul9WekZRV0U' -# Papa.parse url, -# download: true, -# complete: (dataResults) -> d3.text url, (dataResults) -> if dataResults?.length > 0 From 3f6bb75693823f3d08a7a4b5257778d7e19d0c54 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Fri, 20 Nov 2015 17:52:25 -0500 Subject: [PATCH 16/32] feat(kmeans): add kmeans directive for visualization #SOCRFW-181 --- app/partials/analysis/tools/kMeans/main.jade | 1 + .../analysis/tools/kMeans/kmeans.coffee | 102 +++++++++++++++--- 2 files changed, 90 insertions(+), 13 deletions(-) diff --git a/app/partials/analysis/tools/kMeans/main.jade b/app/partials/analysis/tools/kMeans/main.jade index fb54d0e7..f6e79629 100644 --- a/app/partials/analysis/tools/kMeans/main.jade +++ b/app/partials/analysis/tools/kMeans/main.jade @@ -3,3 +3,4 @@ div(ng-controller="kMeansMainCtrl") p. k-means clustering aims to partition n observations into k clusters in which each observation belongs to the cluster with the nearest mean, serving as a prototype of the cluster. + app-kmeans diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index 0b2a6d66..ce8ff320 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -52,15 +52,6 @@ kMeans = angular.module('app_analysis_kMeans', []) if arr? arr = arr.map (x) -> x.toFixed 3 '[' + arr.toString().split(',').join('; ') + ']' - - _redraw = () -> - _drawDataPoints = -> - - # TODO: consider using messages instead - graph = - redraw: _redraw - drawDataPoints: _drawDataPoints - kmeans.setGraph graph ]) .controller('kMeansSidebarCtrl', [ @@ -115,14 +106,10 @@ kMeans = angular.module('app_analysis_kMeans', []) .factory('app_analysis_kMeans_calculator', [ () -> - _data = [] _graph = null _computeAcc = off _maxIter = 20 -# _data = -# result: _matrix - _setGraph = (graph) -> _graph = graph @@ -411,3 +398,92 @@ kMeans = angular.module('app_analysis_kMeans', []) run: _init setGraph: _setGraph ]) + +.directive 'appKmeans', [ + 'app_analysis_kMeans_calculator' + (kMeans) -> + restrict: 'E' + template: "" + link: (scope, elem, attr) -> + + _graph = null + _xScale = null + _yScale = null + _color = null + _meanLayer = null + +# _getDistanceType = () -> +# $('.distInput input:radio:checked').val() +# +# _getDataset = () -> +# $('.dataInput input:radio:checked').val() +# +# _getInitMethod = () -> +# $('.initInput input:radio:checked').val() +# +# _getKValue = () -> +# $('.kInput select').val() +# +# _setStartButtonListener = (func) -> +# $('.runKMeans').click () -> +# event.preventDefault() +# _clearChart() +# func() + + _drawDataPoints = (dataPoints) -> + pointDots = _graph.selectAll('.pointDots').data(dataPoints) + pointDots.enter().append('circle').attr('class','pointDots') + .attr('r', 3) + .attr('cx', (d) -> _xScale(d[0])) + .attr('cy', (d) -> _yScale(d[1])) + + _clearChart = () -> + _graph.selectAll('.pointDots').remove() + _graph.selectAll('g > g > *').remove() + + _redraw = (dataPoints, means, assignments) -> + assignmentLines = _meanLayer.selectAll('.assignmentLines').data(assignments) + assignmentLines.enter().append('line').attr('class','assignmentLines') + .attr('x1', (d, i) -> _xScale(dataPoints[i][0])) + .attr('y1', (d, i) -> _yScale(dataPoints[i][1])) + .attr('x2', (d, i) -> _xScale(means[d][0])) + .attr('y2', (d, i) -> _yScale(means[d][1])) + .attr('stroke', (d) -> _color(d)) + + assignmentLines.transition().duration(500) + .attr('x2', (d, i) -> _xScale(means[d][0])) + .attr('y2', (d, i) -> _yScale(means[d][1])) + .attr('stroke', (d) -> _color(d)) + + meanDots = _meanLayer.selectAll('.meanDots').data(means) + meanDots.enter().append('circle').attr('class','meanDots') + .attr('r', 5) + .attr('stroke', (d, i) -> _color(i)) + .attr('stroke-width', 3) + .attr('fill', 'white') + .attr('cx', (d) -> _xScale(d[0])) + .attr('cy', (d) -> _yScale(d[1])) + + meanDots.transition().duration(500) + .attr('cx', (d) -> _xScale(d[0])) + .attr('cy', (d) -> _yScale(d[1])) + meanDots.exit().remove() + + _publishResult = (data) -> + $('.kMeansAcc').text data + '%' + + rawSvg = elem.find("svg")[0] + svg = d3.select(rawSvg) + _graph = svg.append('g').attr('transform', 'translate(350,350)') + _meanLayer = _graph.append('g') + _xScale = d3.scale.linear().domain([0,10]).range([0,300]) + _yScale = d3.scale.linear().domain([0,10]).range([0,300]) + _color = d3.scale.category10() + + kMeans.setGraph + redraw: _redraw + drawDataPoints: _drawDataPoints + clearChart: _clearChart + + console.log 'appKmeans directive linked' +] From 04c96be34e6bcdd3d822cc1e342929fa7a402261 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Fri, 20 Nov 2015 19:42:18 -0500 Subject: [PATCH 17/32] refactor(kmeans): update chart using $watch #SOCRFW-181 --- app/index.jade | 1 - app/scripts/analysis/getData/getData.coffee | 4 +- .../analysis/tools/kMeans/kmeans.coffee | 71 +++++++++---------- test/karma.conf.coffee | 1 - 4 files changed, 38 insertions(+), 39 deletions(-) diff --git a/app/index.jade b/app/index.jade index 507f28ec..a85450ce 100644 --- a/app/index.jade +++ b/app/index.jade @@ -44,7 +44,6 @@ html(lang='en', ng-app='app') script(src='bower_components/angular-bootstrap/ui-bootstrap-tpls.js') script(src='bower_components/jstat/dist/jstat.js') script(src='bower_components/jsfeat/build/jsfeat.js') - script(src='bower_components/papaparse/papaparse.js') // endbower body(ng-controller='AppCtrl') .container-fluid diff --git a/app/scripts/analysis/getData/getData.coffee b/app/scripts/analysis/getData/getData.coffee index d9d9cb7c..8643b55d 100644 --- a/app/scripts/analysis/getData/getData.coffee +++ b/app/scripts/analysis/getData/getData.coffee @@ -337,7 +337,9 @@ getData = angular.module('app_analysis_getData', [ if $scope.option is undefined $scope.option = 'KNEE_PAIN' - url = 'https://www.googledrive.com/host//0BzJubeARG-hsLUU1Ul9WekZRV0U' + # TODO: host on SOCR server +# url = 'https://www.googledrive.com/host//0BzJubeARG-hsLUU1Ul9WekZRV0U' + url = 'https://www.googledrive.com/host//0BzJubeARG-hsQlNiLXhnOWJfaHc' d3.text url, (dataResults) -> diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index ce8ff320..1f1adea5 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -45,13 +45,34 @@ kMeans = angular.module('app_analysis_kMeans', []) 'app_analysis_kMeans_manager' 'app_analysis_kMeans_calculator' '$scope' - (ctrlMngr, kmeans, $scope) -> + (ctrlMngr, kMeans, $scope) -> console.log 'kMeansMainCtrl executed' + _dataPoints = null + _means = null + _assignments = null + prettifyArrayOutput = (arr) -> if arr? arr = arr.map (x) -> x.toFixed 3 '[' + arr.toString().split(',').join('; ') + ']' + + updateChartData = () -> + $scope.dataPoints = _dataPoints + $scope.means = _means + $scope.assignments = _assignments + + _update = (dataPoints, means=null, assignments=null) -> + _dataPoints = dataPoints + _means = means if means + _assignments = assignments if assignments + updateChartData() + + graph = + update: _update + + updateChartData() + kMeans.setGraph graph ]) .controller('kMeansSidebarCtrl', [ @@ -289,7 +310,7 @@ kMeans = angular.module('app_analysis_kMeans', []) console.table means if not _arrayEqual means.map((x) -> x.idx), centroids.map((x) -> x.idx) centroids = means - _graph.redraw(data, means.map((x) -> x.val), labels) + _graph.update(data, means.map((x) -> x.val), labels) else maxIter = 0 @@ -333,7 +354,7 @@ kMeans = angular.module('app_analysis_kMeans', []) if ctrIdx isnt lbls[i] lbls[i] = ctrIdx centroids = _updateMeans data, centroids, lbls - _graph.redraw(data, centroids.map((x) -> x.val), lbls) + _graph.update(data, centroids.map((x) -> x.val), lbls) for ctr, j in centroids covMats[j] = _updatePrecisionMatrix(data, j, lbls) @@ -380,9 +401,8 @@ kMeans = angular.module('app_analysis_kMeans', []) distanceType = distanceType.toLowerCase() initMethod = initMethod.toLowerCase() - _graph.drawDataPoints data + _graph.update data -# initMethod = ctrl.getInitMethod() if initMethod is 'forgy' centroids = _initCentroids data, k initLabels = _assignSamples data, centroids, 'euclidean' @@ -390,7 +410,7 @@ kMeans = angular.module('app_analysis_kMeans', []) initLabels = _initLabels data.length - 1, k centroids = _updateMeans data, uniqueLabels, initLabels - _graph.redraw data, centroids.map((x) -> x.val), initLabels + _graph.update data, centroids.map((x) -> x.val), initLabels console.log 'Starting K-Means' _runKMeans data, k, _maxIter, centroids, distanceType, uniqueLabels, labels @@ -400,8 +420,9 @@ kMeans = angular.module('app_analysis_kMeans', []) ]) .directive 'appKmeans', [ + '$parse' 'app_analysis_kMeans_calculator' - (kMeans) -> + ($parse, kMeans) -> restrict: 'E' template: "" link: (scope, elem, attr) -> @@ -412,24 +433,6 @@ kMeans = angular.module('app_analysis_kMeans', []) _color = null _meanLayer = null -# _getDistanceType = () -> -# $('.distInput input:radio:checked').val() -# -# _getDataset = () -> -# $('.dataInput input:radio:checked').val() -# -# _getInitMethod = () -> -# $('.initInput input:radio:checked').val() -# -# _getKValue = () -> -# $('.kInput select').val() -# -# _setStartButtonListener = (func) -> -# $('.runKMeans').click () -> -# event.preventDefault() -# _clearChart() -# func() - _drawDataPoints = (dataPoints) -> pointDots = _graph.selectAll('.pointDots').data(dataPoints) pointDots.enter().append('circle').attr('class','pointDots') @@ -437,10 +440,6 @@ kMeans = angular.module('app_analysis_kMeans', []) .attr('cx', (d) -> _xScale(d[0])) .attr('cy', (d) -> _yScale(d[1])) - _clearChart = () -> - _graph.selectAll('.pointDots').remove() - _graph.selectAll('g > g > *').remove() - _redraw = (dataPoints, means, assignments) -> assignmentLines = _meanLayer.selectAll('.assignmentLines').data(assignments) assignmentLines.enter().append('line').attr('class','assignmentLines') @@ -469,9 +468,6 @@ kMeans = angular.module('app_analysis_kMeans', []) .attr('cy', (d) -> _yScale(d[1])) meanDots.exit().remove() - _publishResult = (data) -> - $('.kMeansAcc').text data + '%' - rawSvg = elem.find("svg")[0] svg = d3.select(rawSvg) _graph = svg.append('g').attr('transform', 'translate(350,350)') @@ -480,10 +476,13 @@ kMeans = angular.module('app_analysis_kMeans', []) _yScale = d3.scale.linear().domain([0,10]).range([0,300]) _color = d3.scale.category10() - kMeans.setGraph - redraw: _redraw - drawDataPoints: _drawDataPoints - clearChart: _clearChart + scope.$watchCollection 'dataPoints', (newDataPoints) -> + if newDataPoints + _drawDataPoints newDataPoints + + scope.$watchCollection 'assignments', (newAssignments) -> + if newAssignments + _redraw scope.dataPoints, scope.means, newAssignments console.log 'appKmeans directive linked' ] diff --git a/test/karma.conf.coffee b/test/karma.conf.coffee index aff8bda0..f0239332 100644 --- a/test/karma.conf.coffee +++ b/test/karma.conf.coffee @@ -43,7 +43,6 @@ module.exports = (config) -> 'bower_components/angular-bootstrap/ui-bootstrap-tpls.js' 'bower_components/jstat/dist/jstat.js' 'bower_components/jsfeat/build/jsfeat.js' - 'bower_components/papaparse/papaparse.js' 'bower_components/angular-mocks/angular-mocks.js' # endbower # bower:coffee From 011c92bf970c05d04b8056ee9df1240e8d658ba5 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Tue, 24 Nov 2015 15:17:26 -0500 Subject: [PATCH 18/32] fix(kmeans): update chart on labels update #SOCRFW-181 --- .../analysis/tools/kMeans/kmeans.coffee | 120 +++++++++--------- 1 file changed, 61 insertions(+), 59 deletions(-) diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index 1f1adea5..c8c34466 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -3,77 +3,79 @@ kMeans = angular.module('app_analysis_kMeans', []) .factory('app_analysis_kMeans_constructor', [ - 'app_analysis_kMeans_manager' - (manager) -> - (sb) -> + 'app_analysis_kMeans_manager' + (manager) -> + (sb) -> - manager.setSb sb unless !sb? - _msgList = manager.getMsgList() + manager.setSb sb unless !sb? + _msgList = manager.getMsgList() - init: (opt) -> - console.log 'kMeans init invoked' + init: (opt) -> + console.log 'kMeans init invoked' - destroy: () -> + destroy: () -> - msgList: _msgList - ]) + msgList: _msgList +]) .factory('app_analysis_kMeans_manager', [ - () -> - _sb = null + () -> + _sb = null - _msgList = - outgoing: ['get data'] - incoming: ['take data'] - scope: ['kMeans'] + _msgList = + outgoing: ['get data'] + incoming: ['take data'] + scope: ['kMeans'] - _setSb = (sb) -> - _sb = sb + _setSb = (sb) -> + _sb = sb - _getSb = () -> - _sb + _getSb = () -> + _sb - _getMsgList = () -> - _msgList + _getMsgList = () -> + _msgList - getSb: _getSb - setSb: _setSb - getMsgList: _getMsgList - ]) + getSb: _getSb + setSb: _setSb + getMsgList: _getMsgList +]) .controller('kMeansMainCtrl', [ - 'app_analysis_kMeans_manager' - 'app_analysis_kMeans_calculator' - '$scope' - (ctrlMngr, kMeans, $scope) -> - console.log 'kMeansMainCtrl executed' - - _dataPoints = null - _means = null - _assignments = null - - prettifyArrayOutput = (arr) -> - if arr? - arr = arr.map (x) -> x.toFixed 3 - '[' + arr.toString().split(',').join('; ') + ']' - - updateChartData = () -> - $scope.dataPoints = _dataPoints - $scope.means = _means - $scope.assignments = _assignments - - _update = (dataPoints, means=null, assignments=null) -> - _dataPoints = dataPoints - _means = means if means - _assignments = assignments if assignments - updateChartData() - - graph = - update: _update - - updateChartData() - kMeans.setGraph graph - ]) + 'app_analysis_kMeans_manager' + 'app_analysis_kMeans_calculator' + '$scope' + '$timeout' + (ctrlMngr, kMeans, $scope, $timeout) -> + console.log 'kMeansMainCtrl executed' + + _dataPoints = null + _means = null + _assignments = null + + prettifyArrayOutput = (arr) -> + if arr? + arr = arr.map (x) -> x.toFixed 3 + '[' + arr.toString().split(',').join('; ') + ']' + + updateChartData = () -> + $scope.dataPoints = _dataPoints + $scope.means = _means + $scope.assignments = _assignments + + _update = (dataPoints, means=null, assignments=null) -> + _dataPoints = dataPoints + _means = means if means + _assignments = assignments if assignments + # safe enforce $scope.$digest to activate directive watchers + $timeout updateChartData + + graph = + update: _update + + updateChartData() + kMeans.setGraph graph +]) .controller('kMeansSidebarCtrl', [ 'app_analysis_kMeans_manager' @@ -122,7 +124,7 @@ kMeans = angular.module('app_analysis_kMeans', []) data: tableName: $stateParams.projectId + ':' + $stateParams.forkId promise: deferred - ]) +]) .factory('app_analysis_kMeans_calculator', [ () -> From c3177bdcb936462d5095210583ecaf0e976a2221 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Tue, 24 Nov 2015 16:13:47 -0500 Subject: [PATCH 19/32] feat(kmeans): add auto-rescaling data to SVG size #SOCRFW-187 --- app/scripts/analysis/getData/getData.coffee | 4 ++-- app/scripts/analysis/tools/kMeans/kmeans.coffee | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/scripts/analysis/getData/getData.coffee b/app/scripts/analysis/getData/getData.coffee index 8643b55d..89cd8c95 100644 --- a/app/scripts/analysis/getData/getData.coffee +++ b/app/scripts/analysis/getData/getData.coffee @@ -338,8 +338,8 @@ getData = angular.module('app_analysis_getData', [ $scope.option = 'KNEE_PAIN' # TODO: host on SOCR server -# url = 'https://www.googledrive.com/host//0BzJubeARG-hsLUU1Ul9WekZRV0U' - url = 'https://www.googledrive.com/host//0BzJubeARG-hsQlNiLXhnOWJfaHc' + url = 'https://www.googledrive.com/host//0BzJubeARG-hsLUU1Ul9WekZRV0U' +# url = 'https://www.googledrive.com/host//0BzJubeARG-hsQlNiLXhnOWJfaHc' d3.text url, (dataResults) -> diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index c8c34466..1f958a21 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -429,6 +429,9 @@ kMeans = angular.module('app_analysis_kMeans', []) template: "" link: (scope, elem, attr) -> + MARGIN_LEFT = 40 + MARGIN_TOP = 20 + _graph = null _xScale = null _yScale = null @@ -472,14 +475,20 @@ kMeans = angular.module('app_analysis_kMeans', []) rawSvg = elem.find("svg")[0] svg = d3.select(rawSvg) - _graph = svg.append('g').attr('transform', 'translate(350,350)') + _graph = svg.append('g').attr('transform', 'translate(' + MARGIN_LEFT + ',' + MARGIN_TOP + ')') _meanLayer = _graph.append('g') - _xScale = d3.scale.linear().domain([0,10]).range([0,300]) - _yScale = d3.scale.linear().domain([0,10]).range([0,300]) _color = d3.scale.category10() scope.$watchCollection 'dataPoints', (newDataPoints) -> if newDataPoints + xDataPoints = (row[0] for row in newDataPoints) + yDataPoints = (row[1] for row in newDataPoints) + minXDataPoint = d3.min xDataPoints + maxXDataPoint = d3.max xDataPoints + minYDataPoint = d3.min yDataPoints + maxYDataPoint = d3.max yDataPoints + _xScale = d3.scale.linear().domain([minXDataPoint, maxXDataPoint]).range([0, 600]) + _yScale = d3.scale.linear().domain([minYDataPoint, maxYDataPoint]).range([0, 500]) _drawDataPoints newDataPoints scope.$watchCollection 'assignments', (newAssignments) -> From 6b50e8c883ab881e057609d1e262311ff77a6420 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Tue, 24 Nov 2015 17:33:16 -0500 Subject: [PATCH 20/32] feat(kmeans): disable & animate Run button while k-Means is running #SOCRFW-188 --- .../analysis/tools/kMeans/sidebar.jade | 3 +- .../analysis/tools/kMeans/kmeans.coffee | 39 ++++++++++++++----- app/styles/custom.less | 12 ++++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/app/partials/analysis/tools/kMeans/sidebar.jade b/app/partials/analysis/tools/kMeans/sidebar.jade index 7ba6171b..966ec473 100644 --- a/app/partials/analysis/tools/kMeans/sidebar.jade +++ b/app/partials/analysis/tools/kMeans/sidebar.jade @@ -36,4 +36,5 @@ div(ng-controller="kMeansSidebarCtrl") select(ng-model="initMethod").form-control each method in ['Forgy', 'Random Partition'] option(value=method)= method - button(type="submit", ng-click="run()").btn.btn-primary Run + button(type="submit", aria-label="Run k-Means", ng-click="run()", ng-disabled="kmeanson").btn.btn-primary Run  + span.glyphicon.glyphicon-refresh(aria-hidden="true", ng-class="running") diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index 1f958a21..65ca4d42 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -44,9 +44,10 @@ kMeans = angular.module('app_analysis_kMeans', []) .controller('kMeansMainCtrl', [ 'app_analysis_kMeans_manager' 'app_analysis_kMeans_calculator' + '$rootScope' '$scope' '$timeout' - (ctrlMngr, kMeans, $scope, $timeout) -> + (ctrlMngr, kMeans, $rootScope, $scope, $timeout) -> console.log 'kMeansMainCtrl executed' _dataPoints = null @@ -58,6 +59,8 @@ kMeans = angular.module('app_analysis_kMeans', []) arr = arr.map (x) -> x.toFixed 3 '[' + arr.toString().split(',').join('; ') + ']' + showResults = (results) -> + updateChartData = () -> $scope.dataPoints = _dataPoints $scope.means = _means @@ -70,8 +73,13 @@ kMeans = angular.module('app_analysis_kMeans', []) # safe enforce $scope.$digest to activate directive watchers $timeout updateChartData + _finish = (results=null) -> + $rootScope.$broadcast 'kmeans:done', results + showResults results + graph = update: _update + showResults: _finish updateChartData() kMeans.setGraph graph @@ -83,7 +91,8 @@ kMeans = angular.module('app_analysis_kMeans', []) '$scope' '$stateParams' '$q' - (ctrlMngr, kmeans, $scope, $stateParams, $q) -> + '$timeout' + (ctrlMngr, kmeans, $scope, $stateParams, $q, $timeout) -> console.log 'kMeansSidebarCtrl executed' sb = ctrlMngr.getSb() @@ -92,6 +101,8 @@ kMeans = angular.module('app_analysis_kMeans', []) $scope.k = '2' $scope.dist = 'Euclidean' $scope.initMethod = 'Forgy' + $scope.kmeanson = on + $scope.running = 'hidden' deferred = $q.defer() @@ -106,6 +117,7 @@ kMeans = angular.module('app_analysis_kMeans', []) $scope.xCol = firstCol $scope.yCol = secondCol $scope.labelCol = lastCol + $scope.kmeanson = off $scope.run = -> xCol = _data.header.indexOf $scope.xCol yCol = _data.header.indexOf $scope.yCol @@ -115,7 +127,14 @@ kMeans = angular.module('app_analysis_kMeans', []) else labels = null data = ([row[xCol], row[yCol]] for row in _data.data) - kmeans.run data, $scope.k, $scope.dist, $scope.initMethod, labels + $scope.kmeanson = on + $scope.running = 'spinning' + kmeans.run data, $scope.k, $scope.dist, $scope.initMethod + $scope.$on 'kmeans:done', (event, results) -> + # use timeout to call $digest + $timeout -> + $scope.kmeanson = off + $scope.running = 'hidden' sb.publish msg: 'get data' @@ -334,9 +353,10 @@ kMeans = angular.module('app_analysis_kMeans', []) console.log 'Accuracy: ' + acc * 100 + '%' else acc = '' -# Core.fireEvent -# msg: 'kmeans_done' -# data: acc * 100 + _graph.showResults + centroids: centroids + labels: labels + accuracy: acc runMahalanobis = () -> # main loop @@ -371,9 +391,10 @@ kMeans = angular.module('app_analysis_kMeans', []) console.log 'Accuracy: ' + acc * 100 + '%' else acc = '' -# Core.fireEvent -# msg: 'kmeans_done' -# data: acc * 100 + _graph.showResults + centroids: centroids + labels: labels + accuracy: acc if distanceType is 'mahalanobis' labels = _assignSamples data, centroids, 'euclidean' diff --git a/app/styles/custom.less b/app/styles/custom.less index 6c0a0420..accfed7a 100644 --- a/app/styles/custom.less +++ b/app/styles/custom.less @@ -59,3 +59,15 @@ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); height: 300px; overflow: auto; } + +.spinning { + animation-name: spin; + animation-duration: .5s; + animation-iteration-count: infinite; + animation-timing-function: linear; +} + +@keyframes spin { + from { transform: scale( 1 ) rotate( 0deg ); } + to { transform: scale( 1 ) rotate( 360deg ); } +} From 5a1eabe8d869e84223c5256bb9e168eeab64c268 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Tue, 24 Nov 2015 18:43:54 -0500 Subject: [PATCH 21/32] fix(kmeans): re-draw points when selected data changes #SOCRFW-195 --- app/scripts/analysis/getData/getData.coffee | 7 ++++++- app/scripts/analysis/tools/kMeans/kmeans.coffee | 11 ++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/scripts/analysis/getData/getData.coffee b/app/scripts/analysis/getData/getData.coffee index 89cd8c95..c055b904 100644 --- a/app/scripts/analysis/getData/getData.coffee +++ b/app/scripts/analysis/getData/getData.coffee @@ -338,8 +338,13 @@ getData = angular.module('app_analysis_getData', [ $scope.option = 'KNEE_PAIN' # TODO: host on SOCR server - url = 'https://www.googledrive.com/host//0BzJubeARG-hsLUU1Ul9WekZRV0U' + + # knee pain +# url = 'https://www.googledrive.com/host//0BzJubeARG-hsLUU1Ul9WekZRV0U' + # not so easy # url = 'https://www.googledrive.com/host//0BzJubeARG-hsQlNiLXhnOWJfaHc' + # iris + url = 'https://www.googledrive.com/host//0BzJubeARG-hsMnFQLTB3eEx4aTQ' d3.text url, (dataResults) -> diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index 65ca4d42..b7aa0dd6 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -460,12 +460,20 @@ kMeans = angular.module('app_analysis_kMeans', []) _meanLayer = null _drawDataPoints = (dataPoints) -> + _meanLayer.selectAll('.meanDots').remove() + _meanLayer.selectAll('.assignmentLines').remove() + pointDots = _graph.selectAll('.pointDots').data(dataPoints) pointDots.enter().append('circle').attr('class','pointDots') .attr('r', 3) .attr('cx', (d) -> _xScale(d[0])) .attr('cy', (d) -> _yScale(d[1])) + pointDots.transition().duration(100) + .attr('cx', (d) -> _xScale(d[0])) + .attr('cy', (d) -> _yScale(d[1])) + pointDots.exit().remove() + _redraw = (dataPoints, means, assignments) -> assignmentLines = _meanLayer.selectAll('.assignmentLines').data(assignments) assignmentLines.enter().append('line').attr('class','assignmentLines') @@ -500,7 +508,7 @@ kMeans = angular.module('app_analysis_kMeans', []) _meanLayer = _graph.append('g') _color = d3.scale.category10() - scope.$watchCollection 'dataPoints', (newDataPoints) -> + scope.$watch 'dataPoints', (newDataPoints) -> if newDataPoints xDataPoints = (row[0] for row in newDataPoints) yDataPoints = (row[1] for row in newDataPoints) @@ -511,6 +519,7 @@ kMeans = angular.module('app_analysis_kMeans', []) _xScale = d3.scale.linear().domain([minXDataPoint, maxXDataPoint]).range([0, 600]) _yScale = d3.scale.linear().domain([minYDataPoint, maxYDataPoint]).range([0, 500]) _drawDataPoints newDataPoints + , on scope.$watchCollection 'assignments', (newAssignments) -> if newAssignments From 3a671083cefaa7f14b0c3a7528199fb273b489c1 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Tue, 24 Nov 2015 19:32:22 -0500 Subject: [PATCH 22/32] feat(getData): add Iris data to SOCR datasets #SOCRFW-193 --- app/partials/analysis/getData/main.jade | 9 ++++--- app/scripts/analysis/getData/getData.coffee | 29 ++++++++++++--------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/partials/analysis/getData/main.jade b/app/partials/analysis/getData/main.jade index ef38fa5f..34ec16ed 100644 --- a/app/partials/analysis/getData/main.jade +++ b/app/partials/analysis/getData/main.jade @@ -4,10 +4,11 @@ div(ng-controller='getDataMainCtrl') p.lead To perform any analysis, you need data to begin with. Choose one of the available methods |to get the data into the web-app. - div.socrData(collapse="showState.socrData") - h3 SOCR Datasets - select(style="width:100%; margin:0px",ng-model="option") - option(value='KNEE_PAIN') Simulated SOCR Knee Pain Centroid Location Data + form.socrData(collapse="showState.socrData") + legend SOCR Datasets + div.form-group + select(ng-model="socrdataset",\ + ng-options="item as item.name for item in socrDatasets track by item.id").form-control button.btn.btn-medium(ng-click="getSocrData()") Load div.worldBank(collapse="showState.worldBank") diff --git a/app/scripts/analysis/getData/getData.coffee b/app/scripts/analysis/getData/getData.coffee index c055b904..04b38e92 100644 --- a/app/scripts/analysis/getData/getData.coffee +++ b/app/scripts/analysis/getData/getData.coffee @@ -301,6 +301,17 @@ getData = angular.module('app_analysis_getData', [ (getDataEventMngr, $scope, showState, jsonParser, state) -> console.log 'getDataMainCtrl executed' + # available SOCR Datasets + $scope.socrDatasets = [ + id: 'IRIS' + name: 'Iris Flower Dataset' + , + id: 'KNEE_PAIN' + name: 'Simulated SOCR Knee Pain Centroid Location Data' + ] + # select first one by default + $scope.socrdataset = $scope.socrDatasets[0] + $scope.getWB = -> # default value if $scope.size is undefined @@ -333,18 +344,12 @@ getData = angular.module('app_analysis_getData', [ ) $scope.getSocrData = -> - # default option - if $scope.option is undefined - $scope.option = 'KNEE_PAIN' - - # TODO: host on SOCR server - - # knee pain -# url = 'https://www.googledrive.com/host//0BzJubeARG-hsLUU1Ul9WekZRV0U' - # not so easy -# url = 'https://www.googledrive.com/host//0BzJubeARG-hsQlNiLXhnOWJfaHc' - # iris - url = 'https://www.googledrive.com/host//0BzJubeARG-hsMnFQLTB3eEx4aTQ' + switch $scope.socrdataset.id + # TODO: host on SOCR server + when 'IRIS' then url = 'https://www.googledrive.com/host//0BzJubeARG-hsMnFQLTB3eEx4aTQ' + when 'KNEE_PAIN' then url = 'https://www.googledrive.com/host//0BzJubeARG-hsLUU1Ul9WekZRV0U' + # default option + else url = 'https://www.googledrive.com/host//0BzJubeARG-hsMnFQLTB3eEx4aTQ' d3.text url, (dataResults) -> From 26ab0410fcfe4c0830873078465ea56a6958a4c7 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Wed, 25 Nov 2015 16:04:00 -0500 Subject: [PATCH 23/32] refactor(kmeans): split sidebar controller into functions; init k in angular, not jade #SOCRFW-185 --- app/partials/analysis/tools/kMeans/main.jade | 1 + .../analysis/tools/kMeans/sidebar.jade | 7 +- .../analysis/tools/kMeans/kmeans.coffee | 137 +++++++++++------- 3 files changed, 90 insertions(+), 55 deletions(-) diff --git a/app/partials/analysis/tools/kMeans/main.jade b/app/partials/analysis/tools/kMeans/main.jade index f6e79629..f2db6524 100644 --- a/app/partials/analysis/tools/kMeans/main.jade +++ b/app/partials/analysis/tools/kMeans/main.jade @@ -3,4 +3,5 @@ div(ng-controller="kMeansMainCtrl") p. k-means clustering aims to partition n observations into k clusters in which each observation belongs to the cluster with the nearest mean, serving as a prototype of the cluster. + div(ng-show="showresults").kmeans-results Results app-kmeans diff --git a/app/partials/analysis/tools/kMeans/sidebar.jade b/app/partials/analysis/tools/kMeans/sidebar.jade index 966ec473..792b4c3e 100644 --- a/app/partials/analysis/tools/kMeans/sidebar.jade +++ b/app/partials/analysis/tools/kMeans/sidebar.jade @@ -20,12 +20,7 @@ div(ng-controller="kMeansSidebarCtrl") select(ng-model="labelCol", ng-options="col for col in cols").form-control div.form-group label k   - // k from 2 to 10, uses d3.scale.category10() - //- generate options for k from 2 to 10 - - var k = 2 - select(ng-model="k").form-control - while k <= 10 - option(value=k)= k++ + select(ng-model="k", ng-options="k for k in ks").form-control div.form-group label Distance   select(ng-model="dist").form-control diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index b7aa0dd6..facf9772 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -53,6 +53,7 @@ kMeans = angular.module('app_analysis_kMeans', []) _dataPoints = null _means = null _assignments = null + $scope.showresults = off prettifyArrayOutput = (arr) -> if arr? @@ -60,6 +61,7 @@ kMeans = angular.module('app_analysis_kMeans', []) '[' + arr.toString().split(',').join('; ') + ']' showResults = (results) -> + $scope.showresults = on updateChartData = () -> $scope.dataPoints = _dataPoints @@ -95,54 +97,83 @@ kMeans = angular.module('app_analysis_kMeans', []) (ctrlMngr, kmeans, $scope, $stateParams, $q, $timeout) -> console.log 'kMeansSidebarCtrl executed' - sb = ctrlMngr.getSb() - - $scope.cols = [] - $scope.k = '2' - $scope.dist = 'Euclidean' - $scope.initMethod = 'Forgy' - $scope.kmeanson = on - $scope.running = 'hidden' + DEFAULT_CONTROL_VALUES = + k: 2 + distance: 'Euclidean' + initialisation: 'Forgy' + + # set initial values for sidebar controls + initSidebarControls = (initControlValues) -> + kLimits = kmeans.getMinMaxK() + $scope.ks = [kLimits.minK..kLimits.maxK] + $scope.cols = [] + $scope.kmeanson = on + $scope.running = 'hidden' + + $scope.k = initControlValues.k if initControlValues.k in $scope.ks + $scope.dist = initControlValues.distance + $scope.initMethod = initControlValues.initialisation + + # update data-dependent sidebar controls + updateSidebarControls = (data) -> + $scope.cols = data.header + [firstCol, secondCol, ..., lastCol] = $scope.cols + $scope.xCol = firstCol + $scope.yCol = secondCol + $scope.labelCol = lastCol + $scope.kmeanson = off + + # get requested columns from data + parseDataForKMeans = (data) -> + xCol = data.header.indexOf $scope.xCol + yCol = data.header.indexOf $scope.yCol + if $scope.labelson + labelCol = data.header.indexOf $scope.labelCol + labels = (row[labelCol] for row in data.data) + else + labels = null + data = ([row[xCol], row[yCol]] for row in data.data) + obj = + data: data + labels: labels - deferred = $q.defer() + # call k-means service with parsed data and current controls values + callKMeans = (data) -> + $scope.kmeanson = on + $scope.running = 'spinning' + kmeans.run data, $scope.k, $scope.dist, $scope.initMethod + $scope.$on 'kmeans:done', (event, results) -> + # use timeout to call $digest + $timeout -> + $scope.kmeanson = off + $scope.running = 'hidden' # subscribe for incoming message with data - token = sb.subscribe - msg: 'take data' - msgScope: ['kMeans'] - listener: (msg, data) -> - _data = data - $scope.cols = _data.header - [firstCol, secondCol, ..., lastCol] = $scope.cols - $scope.xCol = firstCol - $scope.yCol = secondCol - $scope.labelCol = lastCol - $scope.kmeanson = off - $scope.run = -> - xCol = _data.header.indexOf $scope.xCol - yCol = _data.header.indexOf $scope.yCol - if $scope.labelson - labelCol = _data.header.indexOf $scope.labelCol - labels = (row[labelCol] for row in _data.data) - else - labels = null - data = ([row[xCol], row[yCol]] for row in _data.data) - $scope.kmeanson = on - $scope.running = 'spinning' - kmeans.run data, $scope.k, $scope.dist, $scope.initMethod - $scope.$on 'kmeans:done', (event, results) -> - # use timeout to call $digest - $timeout -> - $scope.kmeanson = off - $scope.running = 'hidden' - - sb.publish - msg: 'get data' - msgScope: ['kMeans'] - callback: -> sb.unsubscribe token - data: - tableName: $stateParams.projectId + ':' + $stateParams.forkId - promise: deferred + subscribeForData = -> + token = sb.subscribe + msg: 'take data' + msgScope: ['kMeans'] + listener: (msg, data) -> + updateSidebarControls(data) + $scope.run = -> + _data = parseDataForKMeans data + callKMeans _data + + # ask core for data + sendDataRequest = (deferred, token) -> + sb.publish + msg: 'get data' + msgScope: ['kMeans'] + callback: -> sb.unsubscribe token + data: + tableName: $stateParams.projectId + ':' + $stateParams.forkId + promise: deferred + + sb = ctrlMngr.getSb() + deferred = $q.defer() + initSidebarControls DEFAULT_CONTROL_VALUES + token = subscribeForData() + sendDataRequest(deferred, token) ]) .factory('app_analysis_kMeans_calculator', [ @@ -151,6 +182,12 @@ kMeans = angular.module('app_analysis_kMeans', []) _graph = null _computeAcc = off _maxIter = 20 + _minK = 2 + _maxK = 10 + + _getMinMaxK = -> + minK: _minK + maxK: _maxK _setGraph = (graph) -> _graph = graph @@ -407,8 +444,10 @@ kMeans = angular.module('app_analysis_kMeans', []) else interval = setInterval run, 1000 - _init = (data, k, distanceType, initMethod, labels=null) -> - data = (row.map(Number) for row in data) + _init = (data, k, distanceType, initMethod) -> + + labels = data.labels + data = (row.map(Number) for row in data.data) k = Number k console.log 'K: ' + k @@ -440,12 +479,12 @@ kMeans = angular.module('app_analysis_kMeans', []) run: _init setGraph: _setGraph + getMinMaxK: _getMinMaxK ]) .directive 'appKmeans', [ '$parse' - 'app_analysis_kMeans_calculator' - ($parse, kMeans) -> + ($parse) -> restrict: 'E' template: "" link: (scope, elem, attr) -> From f1d4ff8ed7a9e223285ce3bf8e406df1f966f24c Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Wed, 25 Nov 2015 16:50:13 -0500 Subject: [PATCH 24/32] refactor(kmeans): set distances, init methods values in angular instead of jade #SOCRFW-185 --- .../analysis/tools/kMeans/sidebar.jade | 8 ++------ .../analysis/tools/kMeans/kmeans.coffee | 20 +++++++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/app/partials/analysis/tools/kMeans/sidebar.jade b/app/partials/analysis/tools/kMeans/sidebar.jade index 792b4c3e..ee3f140d 100644 --- a/app/partials/analysis/tools/kMeans/sidebar.jade +++ b/app/partials/analysis/tools/kMeans/sidebar.jade @@ -23,13 +23,9 @@ div(ng-controller="kMeansSidebarCtrl") select(ng-model="k", ng-options="k for k in ks").form-control div.form-group label Distance   - select(ng-model="dist").form-control - each dist in ['Euclidean', 'Mahalanobis', 'Manhattan', 'Maximum'] - option(value=dist)= dist + select(ng-model="dist", ng-options="dist for dist in distances").form-control div.form-group label Initialization   - select(ng-model="initMethod").form-control - each method in ['Forgy', 'Random Partition'] - option(value=method)= method + select(ng-model="initMethod", ng-options="init for init in inits").form-control button(type="submit", aria-label="Run k-Means", ng-click="run()", ng-disabled="kmeanson").btn.btn-primary Run  span.glyphicon.glyphicon-refresh(aria-hidden="true", ng-class="running") diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index facf9772..4e4e594c 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -104,15 +104,19 @@ kMeans = angular.module('app_analysis_kMeans', []) # set initial values for sidebar controls initSidebarControls = (initControlValues) -> - kLimits = kmeans.getMinMaxK() - $scope.ks = [kLimits.minK..kLimits.maxK] + + params = kmeans.getParameters() + $scope.ks = [params.minK..params.maxK] + $scope.distances = params.distances + $scope.inits = params.initMethods + $scope.cols = [] $scope.kmeanson = on $scope.running = 'hidden' $scope.k = initControlValues.k if initControlValues.k in $scope.ks - $scope.dist = initControlValues.distance - $scope.initMethod = initControlValues.initialisation + $scope.dist = initControlValues.distance if initControlValues.distance in $scope.distances + $scope.initMethod = initControlValues.initialisation if initControlValues.initialisation in $scope.inits # update data-dependent sidebar controls updateSidebarControls = (data) -> @@ -184,10 +188,14 @@ kMeans = angular.module('app_analysis_kMeans', []) _maxIter = 20 _minK = 2 _maxK = 10 + _distances = ['Euclidean', 'Mahalanobis', 'Manhattan', 'Maximum'] + _initMethods = ['Forgy', 'Random Partition'] - _getMinMaxK = -> + _getParameters = -> minK: _minK maxK: _maxK + distances: _distances + initMethods: _initMethods _setGraph = (graph) -> _graph = graph @@ -479,7 +487,7 @@ kMeans = angular.module('app_analysis_kMeans', []) run: _init setGraph: _setGraph - getMinMaxK: _getMinMaxK + getParameters: _getParameters ]) .directive 'appKmeans', [ From 8f2770e75a4dde6b5d5b08d80767007e046df4c4 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Mon, 30 Nov 2015 16:30:50 -0500 Subject: [PATCH 25/32] refactor(kmeans): wrap $broadcast in method of msgManager --- app/scripts/analysis/tools/kMeans/kmeans.coffee | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index 4e4e594c..297ae94e 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -19,7 +19,8 @@ kMeans = angular.module('app_analysis_kMeans', []) ]) .factory('app_analysis_kMeans_manager', [ - () -> + '$rootScope' + ($rootScope) -> _sb = null _msgList = @@ -36,18 +37,22 @@ kMeans = angular.module('app_analysis_kMeans', []) _getMsgList = () -> _msgList + # wrapper function for controller communications + _broadcast = (msg, data) -> + $rootScope.$broadcast msg, data + getSb: _getSb setSb: _setSb getMsgList: _getMsgList + broadcast: _broadcast ]) .controller('kMeansMainCtrl', [ 'app_analysis_kMeans_manager' 'app_analysis_kMeans_calculator' - '$rootScope' '$scope' '$timeout' - (ctrlMngr, kMeans, $rootScope, $scope, $timeout) -> + (msgManager, kMeans, $scope, $timeout) -> console.log 'kMeansMainCtrl executed' _dataPoints = null @@ -76,7 +81,7 @@ kMeans = angular.module('app_analysis_kMeans', []) $timeout updateChartData _finish = (results=null) -> - $rootScope.$broadcast 'kmeans:done', results + msgManager.broadcast 'kmeans:done', results showResults results graph = From cd8a5c7950a180d3bac480f0d8ee0ef59c2c4e7b Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Mon, 30 Nov 2015 17:26:13 -0500 Subject: [PATCH 26/32] refactor(kmeans): rename service injection from ctrlMngr to msgManager --- app/scripts/analysis/tools/kMeans/kmeans.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index 297ae94e..e5d79d4e 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -99,7 +99,7 @@ kMeans = angular.module('app_analysis_kMeans', []) '$stateParams' '$q' '$timeout' - (ctrlMngr, kmeans, $scope, $stateParams, $q, $timeout) -> + (msgManager, kmeans, $scope, $stateParams, $q, $timeout) -> console.log 'kMeansSidebarCtrl executed' DEFAULT_CONTROL_VALUES = @@ -178,7 +178,7 @@ kMeans = angular.module('app_analysis_kMeans', []) tableName: $stateParams.projectId + ':' + $stateParams.forkId promise: deferred - sb = ctrlMngr.getSb() + sb = msgManager.getSb() deferred = $q.defer() initSidebarControls DEFAULT_CONTROL_VALUES token = subscribeForData() From 10f889aaaac9669a066bf414be1657f7a3d3fc92 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Mon, 30 Nov 2015 20:57:32 -0500 Subject: [PATCH 27/32] feat(kmeans): implement accuracy for arbitrary k #SOCRFW-186 --- .../analysis/tools/kMeans/kmeans.coffee | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index e5d79d4e..07dbb321 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -355,18 +355,30 @@ kMeans = angular.module('app_analysis_kMeans', []) _runKMeans = (data, k, maxIter, centroids, distanceType, uniqueLabels, trueLabels=null) -> evaluateAccuracy = (labels, trueLabels, uniqueLabels) -> - # TODO: make accuracy work for k > 2 - accs = [0] + accuracy = {} + # unique labels available for assignment + uniqueEstLabels = _getUniqueLabels labels + for k in uniqueLabels # get true indices for label k - kLabelIdxs = (i for x, i in trueLabels when x is k) + kTrueLabelIdxs = (i for x, i in trueLabels when x is k) # get calculated labels by true indices - kLabels = (uniqueLabels[x] for x, i in labels when i in kLabelIdxs) - kTrueLabels = (x for x in trueLabels when x is k) - accK = kLabels.map((x, idx) -> x - kTrueLabels[idx]).reduce (r, s) -> r + s - accs.push Math.abs(accK) - acc = (trueLabels.length - accs.reduce((r, s) -> r + s)) / trueLabels.length - acc = if acc < 0.5 then 1 - acc else acc + kEstLabels = (x for x, i in labels when i in kTrueLabelIdxs) # numeric + estLabelCounts = uniqueEstLabels.map (uniqueEstLabel) -> + # count number of occurrences for each unique estimated label + counts = kEstLabels.reduce (n, val) -> + n + (val is uniqueEstLabel) + , 0 + counts + # find first most abundant label index + mostFrequentEstLabelIdx = estLabelCounts.indexOf Math.max.apply(null, estLabelCounts) # numeric + currentEstLabel = uniqueEstLabels[mostFrequentEstLabelIdx] + # remove label that was taken + uniqueEstLabels.splice mostFrequentEstLabelIdx, 1 + accuracy[k] = estLabelCounts[mostFrequentEstLabelIdx] / kTrueLabelIdxs.length + + accs = (acc for own label, acc of accuracy) + accuracy['average'] = accs.reduce((r, s) -> r + s) / accs.length step = (data, centroids) -> maxIter-- @@ -400,7 +412,6 @@ kMeans = angular.module('app_analysis_kMeans', []) if _computeAcc labels = _assignSamples data, centroids, distanceType acc = evaluateAccuracy labels, trueLabels, uniqueLabels - console.log 'Accuracy: ' + acc * 100 + '%' else acc = '' _graph.showResults @@ -438,7 +449,6 @@ kMeans = angular.module('app_analysis_kMeans', []) console.log 'K-Means done.' if _computeAcc acc = evaluateAccuracy lbls, trueLabels, uniqueLabels - console.log 'Accuracy: ' + acc * 100 + '%' else acc = '' _graph.showResults From 5429657cdc08aee6b344ddba8163cec853202527 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Mon, 30 Nov 2015 22:05:49 -0500 Subject: [PATCH 28/32] feat(kmeans): display accuracy per class and avg in main area #SOCRFW-189 --- app/partials/analysis/tools/kMeans/main.jade | 11 ++++++- .../analysis/tools/kMeans/kmeans.coffee | 32 +++++++++---------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/app/partials/analysis/tools/kMeans/main.jade b/app/partials/analysis/tools/kMeans/main.jade index f2db6524..6b27e7d6 100644 --- a/app/partials/analysis/tools/kMeans/main.jade +++ b/app/partials/analysis/tools/kMeans/main.jade @@ -3,5 +3,14 @@ div(ng-controller="kMeansMainCtrl") p. k-means clustering aims to partition n observations into k clusters in which each observation belongs to the cluster with the nearest mean, serving as a prototype of the cluster. - div(ng-show="showresults").kmeans-results Results + div(ng-show="showresults").kmeans-results + p.lead Average accuracy: {{avgAccuracy}} + div.table-responsive + table.table.table-bordered.table-condensed + tr + th Label + th Accuracy + tr(ng-repeat="(label, acc) in accs") + td {{label}} + td {{acc.toFixed(2)}} app-kmeans diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index 07dbb321..9c6d0b86 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -59,14 +59,20 @@ kMeans = angular.module('app_analysis_kMeans', []) _means = null _assignments = null $scope.showresults = off + $scope.avgAccuracy = '' + $scope.accs = {} prettifyArrayOutput = (arr) -> if arr? arr = arr.map (x) -> x.toFixed 3 '[' + arr.toString().split(',').join('; ') + ']' - showResults = (results) -> - $scope.showresults = on + showResults = (accuracy) -> + if Object.keys(accuracy).length isnt 0 + $scope.avgAccuracy = accuracy.average.toFixed(2) + delete accuracy.average + $scope.accs = accuracy + $scope.showresults = on updateChartData = () -> $scope.dataPoints = _dataPoints @@ -74,6 +80,7 @@ kMeans = angular.module('app_analysis_kMeans', []) $scope.assignments = _assignments _update = (dataPoints, means=null, assignments=null) -> + $scope.showresults = off if $scope.showresults is on _dataPoints = dataPoints _means = means if means _assignments = assignments if assignments @@ -379,6 +386,7 @@ kMeans = angular.module('app_analysis_kMeans', []) accs = (acc for own label, acc of accuracy) accuracy['average'] = accs.reduce((r, s) -> r + s) / accs.length + accuracy step = (data, centroids) -> maxIter-- @@ -400,6 +408,10 @@ kMeans = angular.module('app_analysis_kMeans', []) centroids: centroids labels: labels + reportAccuracy = (estLabels, trueLabels, uniqueLabels) -> + acc = evaluateAccuracy estLabels, trueLabels, uniqueLabels + _graph.showResults acc + run = () -> # main loop if maxIter @@ -411,13 +423,7 @@ kMeans = angular.module('app_analysis_kMeans', []) console.log 'K-Means done.' if _computeAcc labels = _assignSamples data, centroids, distanceType - acc = evaluateAccuracy labels, trueLabels, uniqueLabels - else - acc = '' - _graph.showResults - centroids: centroids - labels: labels - accuracy: acc + reportAccuracy labels, trueLabels, uniqueLabels runMahalanobis = () -> # main loop @@ -448,13 +454,7 @@ kMeans = angular.module('app_analysis_kMeans', []) clearInterval interval console.log 'K-Means done.' if _computeAcc - acc = evaluateAccuracy lbls, trueLabels, uniqueLabels - else - acc = '' - _graph.showResults - centroids: centroids - labels: labels - accuracy: acc + reportAccuracy lbls, trueLabels, uniqueLabels if distanceType is 'mahalanobis' labels = _assignSamples data, centroids, 'euclidean' From 1d9c4e684bf3b08620c73da9ff6e95c17fd828a1 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Mon, 30 Nov 2015 22:34:57 -0500 Subject: [PATCH 29/32] fix(kmeans): send kmeans:done after unsupervised learning #SOCRFW-189 --- app/scripts/analysis/tools/kMeans/kmeans.coffee | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/kMeans/kmeans.coffee index 9c6d0b86..3c320b64 100644 --- a/app/scripts/analysis/tools/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/kMeans/kmeans.coffee @@ -157,12 +157,12 @@ kMeans = angular.module('app_analysis_kMeans', []) callKMeans = (data) -> $scope.kmeanson = on $scope.running = 'spinning' - kmeans.run data, $scope.k, $scope.dist, $scope.initMethod $scope.$on 'kmeans:done', (event, results) -> # use timeout to call $digest $timeout -> $scope.kmeanson = off $scope.running = 'hidden' + kmeans.run data, $scope.k, $scope.dist, $scope.initMethod # subscribe for incoming message with data subscribeForData = -> @@ -409,7 +409,9 @@ kMeans = angular.module('app_analysis_kMeans', []) labels: labels reportAccuracy = (estLabels, trueLabels, uniqueLabels) -> - acc = evaluateAccuracy estLabels, trueLabels, uniqueLabels + acc = {} + if _computeAcc + acc = evaluateAccuracy estLabels, trueLabels, uniqueLabels _graph.showResults acc run = () -> @@ -423,7 +425,7 @@ kMeans = angular.module('app_analysis_kMeans', []) console.log 'K-Means done.' if _computeAcc labels = _assignSamples data, centroids, distanceType - reportAccuracy labels, trueLabels, uniqueLabels + reportAccuracy labels, trueLabels, uniqueLabels runMahalanobis = () -> # main loop @@ -453,8 +455,7 @@ kMeans = angular.module('app_analysis_kMeans', []) else clearInterval interval console.log 'K-Means done.' - if _computeAcc - reportAccuracy lbls, trueLabels, uniqueLabels + reportAccuracy lbls, trueLabels, uniqueLabels if distanceType is 'mahalanobis' labels = _assignSamples data, centroids, 'euclidean' From 9ff11517c54980f3d73349a78089c0776c69fe1a Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Wed, 2 Dec 2015 17:49:48 -0500 Subject: [PATCH 30/32] feat(main menu): add dropdown for tools submenu #SOCRFW-191 --- app/partials/analysis-nav.jade | 13 ++++++++++--- .../tools/{ => machineLearning}/kMeans/main.jade | 0 .../tools/{ => machineLearning}/kMeans/sidebar.jade | 0 .../{ => psychometrics}/instrPerfEval/main.jade | 0 .../{ => psychometrics}/instrPerfEval/sidebar.jade | 0 .../{ => machineLearning}/kMeans/kmeans.coffee | 0 .../instrPerfEval/instrPerfEval.coffee | 0 7 files changed, 10 insertions(+), 3 deletions(-) rename app/partials/analysis/tools/{ => machineLearning}/kMeans/main.jade (100%) rename app/partials/analysis/tools/{ => machineLearning}/kMeans/sidebar.jade (100%) rename app/partials/analysis/tools/{ => psychometrics}/instrPerfEval/main.jade (100%) rename app/partials/analysis/tools/{ => psychometrics}/instrPerfEval/sidebar.jade (100%) rename app/scripts/analysis/tools/{ => machineLearning}/kMeans/kmeans.coffee (100%) rename app/scripts/analysis/tools/{ => psychometrics}/instrPerfEval/instrPerfEval.coffee (100%) diff --git a/app/partials/analysis-nav.jade b/app/partials/analysis-nav.jade index 56bdc8aa..85a85b38 100644 --- a/app/partials/analysis-nav.jade +++ b/app/partials/analysis-nav.jade @@ -1,11 +1,18 @@ -//main navigation template +// main navigation template ul.nav.navbar-nav li(ng-class="getClass('/getData')") a(ng-href='#/getData') Raw Data li(ng-class="getClass('/wrangleData')") a(ng-href='#/wrangleData') Wrangle Data - li(ng-class="getClass('/tools')") - a(ng-href='#/tools') Tools + li.dropdown + a(href="#", data-toggle="dropdown", role="button", aria-haspopup="true", aria-expanded="false").dropdown-toggle + | Tools + span.caret + ul.dropdown-menu + li(ng-class="getClass('/tools/instrperfeval')") + a(ng-href='#/tools/instrperfeval') Instrument Performance Evaluation + li(ng-class="getClass('/tools/kmeans')") + a(ng-href='#/tools/kmeans') 2D k-Means Clustering li(ng-class="getClass('/charts')") a(ng-href='#/charts') Charts li(ng-class="getClass('/results')") diff --git a/app/partials/analysis/tools/kMeans/main.jade b/app/partials/analysis/tools/machineLearning/kMeans/main.jade similarity index 100% rename from app/partials/analysis/tools/kMeans/main.jade rename to app/partials/analysis/tools/machineLearning/kMeans/main.jade diff --git a/app/partials/analysis/tools/kMeans/sidebar.jade b/app/partials/analysis/tools/machineLearning/kMeans/sidebar.jade similarity index 100% rename from app/partials/analysis/tools/kMeans/sidebar.jade rename to app/partials/analysis/tools/machineLearning/kMeans/sidebar.jade diff --git a/app/partials/analysis/tools/instrPerfEval/main.jade b/app/partials/analysis/tools/psychometrics/instrPerfEval/main.jade similarity index 100% rename from app/partials/analysis/tools/instrPerfEval/main.jade rename to app/partials/analysis/tools/psychometrics/instrPerfEval/main.jade diff --git a/app/partials/analysis/tools/instrPerfEval/sidebar.jade b/app/partials/analysis/tools/psychometrics/instrPerfEval/sidebar.jade similarity index 100% rename from app/partials/analysis/tools/instrPerfEval/sidebar.jade rename to app/partials/analysis/tools/psychometrics/instrPerfEval/sidebar.jade diff --git a/app/scripts/analysis/tools/kMeans/kmeans.coffee b/app/scripts/analysis/tools/machineLearning/kMeans/kmeans.coffee similarity index 100% rename from app/scripts/analysis/tools/kMeans/kmeans.coffee rename to app/scripts/analysis/tools/machineLearning/kMeans/kmeans.coffee diff --git a/app/scripts/analysis/tools/instrPerfEval/instrPerfEval.coffee b/app/scripts/analysis/tools/psychometrics/instrPerfEval/instrPerfEval.coffee similarity index 100% rename from app/scripts/analysis/tools/instrPerfEval/instrPerfEval.coffee rename to app/scripts/analysis/tools/psychometrics/instrPerfEval/instrPerfEval.coffee From afe27a0793293286642d49abe5a2324389dcc7cf Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Wed, 2 Dec 2015 17:50:33 -0500 Subject: [PATCH 31/32] fix(app): fix routing for tools #SOCRFW-192 --- app/app.coffee | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/app.coffee b/app/app.coffee index 3116ff13..412a8d55 100644 --- a/app/app.coffee +++ b/app/app.coffee @@ -101,21 +101,21 @@ App.config([ 'sidebar': templateUrl: 'partials/analysis/wrangleData/sidebar.html' ) -# .state('tools' -# url: '/tools' -# views: -# 'main': -# templateUrl: 'partials/analysis/tools/instrPerfEval/main.html' -# 'sidebar': -# templateUrl: 'partials/analysis/tools/instrPerfEval/sidebar.html' -# ) - .state('tools' - url: '/tools' + .state('instrperfeval' + url: '/tools/instrperfeval' views: 'main': - templateUrl: 'partials/analysis/tools/kMeans/main.html' + templateUrl: 'partials/analysis/tools/psychometrics/instrPerfEval/main.html' 'sidebar': - templateUrl: 'partials/analysis/tools/kMeans/sidebar.html' + templateUrl: 'partials/analysis/tools/psychometrics/instrPerfEval/sidebar.html' + ) + .state('kmeans' + url: '/tools/kmeans' + views: + 'main': + templateUrl: 'partials/analysis/tools/machineLearning/kMeans/main.html' + 'sidebar': + templateUrl: 'partials/analysis/tools/machineLearning/kMeans/sidebar.html' ) .state('charts' url: '/charts/:projectId/:forkId' From a5e42a318b4a65f79dcb88fc8e49b85bfb2598a4 Mon Sep 17 00:00:00 2001 From: alxndrkalinin Date: Wed, 2 Dec 2015 18:04:10 -0500 Subject: [PATCH 32/32] chore: version bump to v0.1.5-alpha.2 #SOCRFW-200 --- app/scripts/services.coffee | 2 +- bower.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/scripts/services.coffee b/app/scripts/services.coffee index 6f163a47..fb09848e 100644 --- a/app/scripts/services.coffee +++ b/app/scripts/services.coffee @@ -4,7 +4,7 @@ services = angular.module('app_services', []) -services.factory 'version', -> "0.0.1.4" +services.factory 'version', -> "v0.1.5-alpha.2" #services.config([ # ()-> diff --git a/bower.json b/bower.json index a0c5f320..b3ce4e41 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "author": "Alexandr Kalinin, Selvam Palanimalai", "name": "SOCRAT", - "version": "v0.1.5-alpha.1", + "version": "v0.1.5-alpha.2", "description": "Flexible HTML5/JS toolkit for interactive data analysis and visuzalization", "homepage": "https://github.com/SOCRedu/SOCR-framework", "dependencies": { diff --git a/package.json b/package.json index a2147366..75f304ae 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "Alexandr Kalinin, Selvam Palanimalai", "name": "SOCRAT", - "version": "v0.1.5-alpha.1", + "version": "v0.1.5-alpha.2", "description": "Flexible HTML5/JS toolkit for interactive data analysis and visuzalization", "homepage": "https://github.com/SOCR/SOCRAT", "devDependencies": {