# HG changeset patch # User Mikael Berthe # Date 1487789263 -3600 # Node ID 366f991716a9a418aa1e913d2a1925e7ccd1a974 # Parent 17a1a3f4fb860dd887653e4e71860bbe536f176c Add more statistics diff -r 17a1a3f4fb86 -r 366f991716a9 gobm65.go --- a/gobm65.go Mon Feb 20 22:41:58 2017 +0100 +++ b/gobm65.go Wed Feb 22 19:47:43 2017 +0100 @@ -45,6 +45,8 @@ "io" "io/ioutil" "log" + "math" + "sort" "time" flag "github.com/docker/docker/pkg/mflag" @@ -217,6 +219,91 @@ return } +func average(items []measurement) (measurement, error) { + var avgMeasure measurement + var avgCount int + + for _, data := range items { + avgMeasure.Systolic += data.Systolic + avgMeasure.Diastolic += data.Diastolic + avgMeasure.Pulse += data.Pulse + avgCount++ + } + + roundDivision := func(a, b int) int { + return int(0.5 + float64(a)/float64(b)) + } + + if avgCount == 0 { + return avgMeasure, fmt.Errorf("cannot compute average: empty set") + } + + avgMeasure.Systolic = roundDivision(avgMeasure.Systolic, avgCount) + avgMeasure.Diastolic = roundDivision(avgMeasure.Diastolic, avgCount) + avgMeasure.Pulse = roundDivision(avgMeasure.Pulse, avgCount) + + return avgMeasure, nil +} + +func intMedian(numbers []int) int { + middle := len(numbers) / 2 + med := numbers[middle] + if len(numbers)%2 == 0 { + med = (med + numbers[middle-1]) / 2 + } + return med +} + +func median(items []measurement) (measurement, error) { + var med measurement + if len(items) == 0 { + return med, fmt.Errorf("cannot compute average: empty set") + } + + var sys, dia, pul []int + for _, data := range items { + sys = append(sys, data.Systolic) + dia = append(dia, data.Diastolic) + pul = append(pul, data.Pulse) + } + + sort.Ints(sys) + sort.Ints(dia) + sort.Ints(pul) + + med.Systolic = intMedian(sys) + med.Diastolic = intMedian(dia) + med.Pulse = intMedian(pul) + + return med, nil +} + +func stdDeviation(items []measurement) (measurement, error) { + var sDev measurement + + if len(items) <= 1 { + return sDev, fmt.Errorf("cannot compute deviation: set too small") + } + + var sumSys, sumDia, sumPul float64 + avg, err := average(items) + if err != nil { + return sDev, err + } + + for _, data := range items { + sumSys += math.Pow(float64(data.Systolic-avg.Systolic), 2) + sumDia += math.Pow(float64(data.Diastolic-avg.Diastolic), 2) + sumPul += math.Pow(float64(data.Pulse-avg.Pulse), 2) + } + + sDev.Systolic = int(math.Sqrt(sumSys / float64(len(items)-1))) + sDev.Diastolic = int(math.Sqrt(sumDia / float64(len(items)-1))) + sDev.Pulse = int(math.Sqrt(sumPul / float64(len(items)-1))) + + return sDev, nil +} + func main() { inFile := flag.String([]string{"-input-file", "i"}, "", "Input JSON file") outFile := flag.String([]string{"-output-file", "o"}, "", "Output JSON file") @@ -225,6 +312,7 @@ "Filter records from date (YYYY-mm-dd HH:MM:SS)") format := flag.String([]string{"-format", "f"}, "", "Output format (csv, json)") avg := flag.Bool([]string{"-average", "a"}, false, "Compute average") + stats := flag.Bool([]string{"-stats"}, false, "Compute statistics") merge := flag.Bool([]string{"-merge", "m"}, false, "Try to merge input JSON file with fetched data") device := flag.String([]string{"-device", "d"}, "/dev/ttyUSB0", "Serial device") @@ -288,34 +376,47 @@ items = items[0:*limit] } - var avgMeasure measurement - var avgCount int - - for i, data := range items { - if *format == "csv" { + if *format == "csv" { + for i, data := range items { fmt.Printf("%d;%x;%d-%02d-%02d %02d:%02d;%d;%d;%d\n", i+1, data.Header, data.Year, data.Month, data.Day, data.Hour, data.Minute, data.Systolic, data.Diastolic, data.Pulse) } + } - avgMeasure.Systolic += data.Systolic - avgMeasure.Diastolic += data.Diastolic - avgMeasure.Pulse += data.Pulse - avgCount++ + if *stats { + *avg = true + } + + if *avg && len(items) > 0 { + avgMeasure, err := average(items) + if err != nil { + log.Println("Error:", err) + } else { + fmt.Printf("Average: %d;%d;%d\n", avgMeasure.Systolic, + avgMeasure.Diastolic, avgMeasure.Pulse) + } } - if *avg && avgCount > 0 { - roundDivision := func(a, b int) int { - return int(0.5 + float64(a)/float64(b)) + if *stats && len(items) > 1 { + d, err := stdDeviation(items) + if err != nil { + log.Println("Error:", err) + } else { + fmt.Printf("Standard deviation: %d;%d;%d\n", + d.Systolic, d.Diastolic, d.Pulse) } - avgMeasure.Systolic = roundDivision(avgMeasure.Systolic, avgCount) - avgMeasure.Diastolic = roundDivision(avgMeasure.Diastolic, avgCount) - avgMeasure.Pulse = roundDivision(avgMeasure.Pulse, avgCount) - - fmt.Printf("Average: %d;%d;%d\n", avgMeasure.Systolic, - avgMeasure.Diastolic, avgMeasure.Pulse) + } + if *stats && len(items) > 0 { + m, err := median(items) + if err != nil { + log.Println("Error:", err) + } else { + fmt.Printf("Median values: %d;%d;%d\n", + m.Systolic, m.Diastolic, m.Pulse) + } } if *format == "json" || *outFile != "" {