Skip to content

math: implement statistical functions for mean, median, mode, variance, and stddev #69195

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions src/math/stats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package math

import (
"math"
"sort"
)

// Mean returns the mean (average) of a slice of float64 numbers.
//
// Special cases are:
// Mean([]float64{}) = 0
func Mean(numbers []float64) float64 {
if len(numbers) == 0 {
return 0
}

var sum float64
for _, num := range numbers {
sum += num
}

return sum / float64(len(numbers))
}

// Median returns the median of a slice of float64 numbers.
//
// Special cases are:
// Median([]float64{}) = 0
func Median(numbers []float64) float64 {
return Percentile(numbers, 50)
}

// Mode returns the mode(s) of a slice of float64 numbers.
//
// Special cases are:
// Mode([]float64{}) = nil
func Mode(numbers []float64) []float64 {
if len(numbers) == 0 {
return nil
}

counts := make(map[float64]int)
for _, num := range numbers {
counts[num]++
}

var maxCount int
for _, count := range counts {
if count > maxCount {
maxCount = count
}
}

var modes []float64
for num, count := range counts {
if count == maxCount {
modes = append(modes, num)
}
}

return modes
}

// Variance returns the variance of a slice of float64 numbers.
//
// Special cases are:
// Variance([]float64{}) = 0
func Variance(numbers []float64) float64 {
if len(numbers) == 0 {
return 0
}

mean := Mean(numbers)
var sumSquares float64
for _, num := range numbers {
diff := num - mean
sumSquares += diff * diff
}

return sumSquares / float64(len(numbers))
}

// StdDev returns the standard deviation of a slice of float64 numbers.
//
// Special cases are:
// StdDev([]float64{}) = 0
func StdDev(numbers []float64) float64 {
return math.Sqrt(Variance(numbers))
}

// Percentile returns the p-th percentile of a slice of float64 numbers.
// The percentile is the value below which a given percentage of observations fall.
//
// Special cases are:
// Percentile([]float64{}, p) = 0 for any p
func Percentile(numbers []float64, p float64) float64 {
if len(numbers) == 0 || p < 0 || p > 100 {
return 0
}

// Sort the slice
sorted := make([]float64, len(numbers))
copy(sorted, numbers)
sort.Float64s(sorted)

index := (p / 100) * float64(len(sorted)-1)
i := int(index)

// Handle the case for interpolation between two values if not an integer
if index == float64(i) {
return sorted[i]
}

// Linear interpolation
return sorted[i]*(float64(i+1)-index) + sorted[i+1]*(index-float64(i))
}