Skip to content

stat: add circular standard deviation#2011

Open
cdzombak wants to merge 4 commits into
gonum:masterfrom
cdzombak:cdz/add-circular-stddev
Open

stat: add circular standard deviation#2011
cdzombak wants to merge 4 commits into
gonum:masterfrom
cdzombak:cdz/add-circular-stddev

Conversation

@cdzombak

@cdzombak cdzombak commented Jan 12, 2025

Copy link
Copy Markdown

This PR adds a new function, CircularStdDev, to gonum/stat alongside the existing CircularMean.

Checklist:

  • API changes have been discussed
  • code is goformated correctly (goimports)
  • packages with generated code have had code generation run
  • tests pass locally
  • linked to relevant issues (n/a)

Comment thread stat/stat.go Outdated
// CircularStdDev returns the circular standard deviation of the dataset.
//
// sqrt(-2 * log(sqrt((\sum_i w_i * sin(alpha_i))^2 + (\sum_i w_i * cos(alpha_i))^2) / length(x)))
func CircularStdDev(x []float64) float64 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our stat functions take weights. Please add this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I am neither a data scientist nor a statistician, and I don't know how to (correctly) implement a weighted circular standard deviation. I also didn't find a reference implementation in R or scipy — or, crucially, a reference implementation to use to generate test cases.

If you can provide any pointers I'll be happy to add it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is https://rdrr.io/github/matthewkling/windscape/src/R/circ_sd.R, but I don't have the expertise to adapt it into this implementation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blunt. Needs tests.

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

	var aX, aY float64
	if weights != nil {
		var sumW float64
		for i, v := range x {
			w := weights[i]
			sumW += w
			aX += w * math.Cos(v)
			aY += w * math.Sin(v)
		}
		return math.Sqrt(-2 * math.Log(math.Hypot(aY, aX)/sumW))
	} else {
		for _, v := range x {
			aX += math.Cos(v)
			aY += math.Sin(v)
		}
		return math.Sqrt(-2 * math.Log(math.Hypot(aY, aX)/float64(len(x))))
	}
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your help, @kortschak.

3bf72c7 implements weighted circular standard deviation and adds some test cases, the output of which I've validated against astropy's circstats library.

Comment thread stat/stat.go Outdated
Comment thread stat/stat.go
Comment thread stat/stat_test.go Outdated
Comment thread stat/stat_test.go Outdated
@kortschak kortschak changed the title stat: Add circular standard deviation stat: add circular standard deviation Jan 12, 2025
@cdzombak cdzombak requested a review from kortschak January 12, 2025 18:39
Co-authored-by: Dan Kortschak <dan@kortschak.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants