Skip to content

Commit

Permalink
implement circular mean & stddev directly
Browse files Browse the repository at this point in the history
  • Loading branch information
cdzombak committed Jan 12, 2025
1 parent bb5d721 commit 7eb9e1e
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 7 deletions.
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ go 1.22

toolchain go1.23.4

replace gonum.org/v1/gonum => github.com/cdzombak/gonum v0.0.0-20250111220929-90b1f7503766

require (
github.com/stretchr/testify v1.9.0
gonum.org/v1/gonum v0.0.0-00010101000000-000000000000
)

require (
Expand Down
8 changes: 3 additions & 5 deletions libwx.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ package libwx
import (
"errors"
"math"

"gonum.org/v1/gonum/stat"
)

var ErrInputRange = errors.New("one or more input values are outside the calculation's supported range")
Expand Down Expand Up @@ -284,7 +282,7 @@ func HeatIndexWarningC(heatIndex TempC) HeatIndexWarning {
// AvgDirectionDeg calculates the circular mean of the given set of angles (in degrees).
// This is useful to find e.g. the average wind direction.
func AvgDirectionDeg(degrees []Degree) Degree {
return radToDeg(stat.CircularMean(degToRadSlice(clampedDegSlice(degrees)), nil))
return radToDeg(circularMean(degToRadSlice(clampedDegSlice(degrees)), nil))
}

// WeightedAvgDirectionDeg calculates the weighted circular mean of the given set of angles (in degrees).
Expand All @@ -293,11 +291,11 @@ func WeightedAvgDirectionDeg(degrees []Degree, weights []float64) (Degree, error
if len(degrees) != len(weights) {
return 0.0, ErrMismatchedInputLength
}
return radToDeg(stat.CircularMean(degToRadSlice(clampedDegSlice(degrees)), weights)), nil
return radToDeg(circularMean(degToRadSlice(clampedDegSlice(degrees)), weights)), nil
}

// StdDevDeg calculates the circular standard deviation of the given set of angles (in degrees).
// This is useful to find e.g. the variability of wind direction.
func StdDevDeg(degrees []Degree) Degree {
return radToDeg(stat.CircularStdDev(degToRadSlice(clampedDegSlice(degrees))))
return radToDeg(circularStdDev(degToRadSlice(clampedDegSlice(degrees))))
}
35 changes: 35 additions & 0 deletions stat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package libwx

import "math"

// borrowed from gonum:
func circularMean(x, weights []float64) float64 {
if weights != nil && len(x) != len(weights) {
panic("stat: slice length mismatch")
}

var aX, aY float64
if weights != nil {
for i, v := range x {
aX += weights[i] * math.Cos(v)
aY += weights[i] * math.Sin(v)
}
} else {
for _, v := range x {
aX += math.Cos(v)
aY += math.Sin(v)
}
}

return math.Atan2(aY, aX)
}

// from my (open) gonum PR which doesn't (yet) support weights:
func circularStdDev(x []float64) float64 {
var aX, aY float64
for _, v := range x {
aX += math.Cos(v)
aY += math.Sin(v)
}
return math.Sqrt(-2 * math.Log(math.Sqrt(aY*aY+aX*aX)/float64(len(x))))
}

0 comments on commit 7eb9e1e

Please sign in to comment.