av/turbidity/plot.go

127 lines
3.0 KiB
Go
Raw Normal View History

/*
DESCRIPTION
2022-01-06 06:25:40 +03:00
Plotting functions for the turbidity sensor results.
AUTHORS
2022-01-06 06:25:40 +03:00
Russell Stanley <russell@ausocean.org>
LICENSE
Copyright (C) 2021-2022 the Australian Ocean Lab (AusOcean)
It is free software: you can redistribute it and/or modify them
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
It is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
in gpl.txt. If not, see http://www.gnu.org/licenses.
*/
package turbidity
import (
"fmt"
"math"
"gonum.org/v1/plot"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/plotutil"
"gonum.org/v1/plot/vg"
)
2022-01-10 02:38:54 +03:00
// standarDeviation will return the standard deviation of a float slice
func standarDeviation(slice []float64) float64 {
mean := average(slice)
variance := 0.0
for _, i := range slice {
variance += math.Pow(i-mean, 2.0)
}
return math.Sqrt(variance / float64(len(slice)))
}
// Normalize values in a slice between 0 and 1.
func normalize(slice []float64) []float64 {
max := -math.MaxFloat64
min := math.MaxFloat64
2021-12-21 04:43:05 +03:00
out := make([]float64, len(slice))
2021-12-21 04:43:05 +03:00
if len(slice) <= 1 {
return slice
}
2022-01-06 06:25:40 +03:00
// Find the max and min values of the slice.
2021-12-21 04:43:05 +03:00
for i := range slice {
if slice[i] > max {
max = slice[i]
}
if slice[i] < min {
min = slice[i]
}
}
2021-12-21 04:43:05 +03:00
for i := range slice {
out[i] = (slice[i] - min) / (max - min)
}
return out
}
// Return the average of a slice.
func average(slice []float64) float64 {
2022-01-06 06:25:40 +03:00
var out float64
2021-12-21 04:43:05 +03:00
for i := range slice {
out += slice[i]
}
return out / float64(len(slice))
}
func plotResults(x, sharpness, contrast []float64) error {
err := plotToFile(
"Results",
"Almond Milk (ml)",
"Score",
func(p *plot.Plot) error {
return plotutil.AddLinePoints(p,
"Contrast", plotterXY(x, contrast),
"Sharpness", plotterXY(x, sharpness),
)
},
)
if err != nil {
return fmt.Errorf("Could not plot results: %w", err)
}
return nil
}
// plotToFile creates a plot with a specified name and x&y titles using the
// provided draw function, and then saves to a PNG file with filename of name.
func plotToFile(name, xTitle, yTitle string, draw func(*plot.Plot) error) error {
p := plot.New()
p.Title.Text = name
p.X.Label.Text = xTitle
p.Y.Label.Text = yTitle
err := draw(p)
if err != nil {
return fmt.Errorf("could not draw plot contents: %w", err)
}
if err := p.Save(15*vg.Centimeter, 15*vg.Centimeter, "plots/"+name+".png"); err != nil {
return fmt.Errorf("could not save plot: %w", err)
}
return nil
}
// plotterXY provides a plotter.XYs type value based on the given x and y data.
func plotterXY(x, y []float64) plotter.XYs {
xy := make(plotter.XYs, len(x))
for i := range x {
xy[i].X = x[i]
xy[i].Y = y[i]
}
return xy
}