From 082157bc5f7978c493b29b026a70d72a9b3ce116 Mon Sep 17 00:00:00 2001 From: "didier.amyot" Date: Sat, 24 May 2014 10:02:01 -0400 Subject: [PATCH] started to add a histogram on key. --- client/bower.json | 4 +- client/coffee/controllers/keyStatCtrl.coffee | 29 ++++++++- client/coffee/vultureApp.coffee | 1 + client/index.html | 2 + client/partials/keyStat.html | 4 ++ main.go | 1 - stats.go | 49 +++++++++++++++- stats_test.go | 62 ++++++++++++++++++++ 8 files changed, 148 insertions(+), 4 deletions(-) diff --git a/client/bower.json b/client/bower.json index 4ccb628..f5d838d 100644 --- a/client/bower.json +++ b/client/bower.json @@ -22,6 +22,8 @@ "codemirror": "~3.19.0", "angular-ui-codemirror": "~0.1.1", "spin.js": "~2.0.1", - "angular-spinner": "~0.4.0" + "angular-spinner": "~0.4.0", + "highcharts-ng": "~0.0.6", + "highcharts": "~3.0.7" } } diff --git a/client/coffee/controllers/keyStatCtrl.coffee b/client/coffee/controllers/keyStatCtrl.coffee index acc60e3..04dd716 100644 --- a/client/coffee/controllers/keyStatCtrl.coffee +++ b/client/coffee/controllers/keyStatCtrl.coffee @@ -4,10 +4,17 @@ humanValueOfStatisticalAbreviation = mean: "mean" std: "standard deviation" var: "variance" + min: "minimum" + max: "maximum" + n: "number of documents" + +getAxisLabel = (min, stepSize, numberOfBins) -> + return (min + 0.5 * stepSize + i * stepSize for i in [0... numberOfBins]) + root.controllers.controller('keyStatCtrl', ['$scope', '$routeParams', 'util', ($scope, $routeParams, util) -> - $scope.name = "didier!" + $scope.name = $routeParams.key $scope.stats = [] url = "/api/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }" @@ -18,5 +25,25 @@ root.controllers.controller('keyStatCtrl', ['$scope', '$routeParams', 'util', ($ $scope.stats.push({name: humanValueOfStatisticalAbreviation[key], value: val }) ) + $scope.chartConfig = + options: + chart: + type: 'column' + plotOptions: + column: + groupPadding: 0 + pointPadding: 0 + borderWidth: 0 + xAxis: + categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + title: + text: 'Distribution' + loading: false + series: [{ + data: [10, 15, 12, 8, 7, 4, 12, 12, 9, 11], + name: $routeParams.key + color: '#008CBA' + }] + ]) diff --git a/client/coffee/vultureApp.coffee b/client/coffee/vultureApp.coffee index 9b5b9c7..a875f40 100644 --- a/client/coffee/vultureApp.coffee +++ b/client/coffee/vultureApp.coffee @@ -5,6 +5,7 @@ angular.module('vulture', [ 'ngRoute' 'ui.codemirror' 'angularSpinner' + 'highcharts-ng' 'leaflet-directive' 'vulture.filters' 'vulture.services' diff --git a/client/index.html b/client/index.html index 046eb7e..090188c 100644 --- a/client/index.html +++ b/client/index.html @@ -56,6 +56,8 @@ + + diff --git a/client/partials/keyStat.html b/client/partials/keyStat.html index f1cb921..147227b 100644 --- a/client/partials/keyStat.html +++ b/client/partials/keyStat.html @@ -8,4 +8,8 @@ + + +
+
\ No newline at end of file diff --git a/main.go b/main.go index 879598f..50ea622 100644 --- a/main.go +++ b/main.go @@ -96,7 +96,6 @@ func serveApi(router *mux.Router) { router.Handle("/{server}/{database}/{collection}/query/{query}/idx/{index:[0-9]+}", ApiHandler(getDocumentByQueryAndIndex)) router.Handle("/{server}/{database}/{collection}/query/{query}/all", ApiHandler(getAllDocuments)) - // getAllDocuments } func createMuxRouter() http.Handler { diff --git a/stats.go b/stats.go index af0e185..e098a62 100644 --- a/stats.go +++ b/stats.go @@ -11,10 +11,55 @@ const initialLength int = 1024 const verySmall float64 = -100000 const veryBig float64 = 100000 +type Histogram struct { + Min float64 `json:"min"` + StepSize float64 `json:"step_size"` + Values []int `json:"values"` +} + type Stats struct { Mean float64 `json:"mean"` Var float64 `json:"var"` Std float64 `json:"std"` + Min float64 `json:"min"` + Max float64 `json:"max"` + N int `json:"n"` +} + +func (this *Stats) findMinHistogramValue() float64 { + return math.Max(this.Min, this.Mean-3*this.Std) +} + +func (this *Stats) findMaxHistogramValue() float64 { + return math.Min(this.Max, this.Mean+3*this.Std) +} + +func (this *Stats) findSuitableBinForDatum(min, step, val float64) int { + var i int + var act float64 = min + for i = 0; act < val; act += step { + i++ + } + return i - 1 + +} + +func (this *Stats) MakeHistogram(numberOfBins int, dataChannel chan float64, outputChannel chan *Histogram) { + min := this.findMinHistogramValue() + max := this.findMaxHistogramValue() + step := (max - min) / float64(numberOfBins) + hist := make([]int, numberOfBins) + + for datum := range dataChannel { + if datum < min || datum > max { + continue + } + i := this.findSuitableBinForDatum(min, step, datum) + hist[i]++ + } + + outputChannel <- &Histogram{min, step, hist} + } type StatAggregator struct { @@ -50,7 +95,9 @@ func (this *StatAggregator) getFinalStats() *Stats { stats.Mean = this.sum / float64(this.n) stats.Var = this.sumOfTheSquares/float64(this.n) - stats.Mean*stats.Mean stats.Std = math.Sqrt(stats.Var) + stats.Min = this.min + stats.Max = this.max + stats.N = this.n return stats - } diff --git a/stats_test.go b/stats_test.go index 01019b3..a7b045a 100644 --- a/stats_test.go +++ b/stats_test.go @@ -37,6 +37,8 @@ func TestAddStatsBasic(t *testing.T) { func TestGetFinalStats(t *testing.T) { s := NewStatAggregator() s.sumOfTheSquares = 5000 + s.max = 900 + s.min = -900 s.sum = 200 s.n = 10 stats := s.getFinalStats() @@ -49,4 +51,64 @@ func TestGetFinalStats(t *testing.T) { if stats.Std != 10. { t.Fail() } + if stats.Min != -900 { + t.Fail() + } + if stats.Max != 900 { + t.Fail() + } + if stats.N != 10 { + t.Fail() + } +} + +func TestFindMinHistogramValue(t *testing.T) { + stats := &Stats{ + Mean: 10, + Std: 2, + Min: 1, + } + + if stats.findMinHistogramValue() != 4 { + t.Fail() + } + + stats.Min = 7 + if stats.findMinHistogramValue() != 7 { + t.Fail() + } + +} + +func TestFindMaxHistogramValue(t *testing.T) { + stats := &Stats{ + Mean: 10, + Std: 2, + Max: 22, + } + + if stats.findMaxHistogramValue() != 16 { + t.Fail() + } + + stats.Max = 14 + if stats.findMaxHistogramValue() != 14 { + t.Fail() + } +} + +func TestFindSuitableBinForDatum(t *testing.T) { + stats := new(Stats) + if bin := stats.findSuitableBinForDatum(-4, 1, -3.2); bin != 0 { + t.Error("wrong bin: ", bin) + } + + if bin := stats.findSuitableBinForDatum(-4, 1, -2.2); bin != 1 { + t.Error("wrong bin: ", bin) + } + + if bin := stats.findSuitableBinForDatum(-4, 1, 10.1); bin != 14 { + t.Error("wrong bin: ", bin) + } + }