diff --git a/go.mod b/go.mod index a31517f..9c5be1a 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/alecthomas/kingpin/v2 v2.4.0 github.com/illumos/go-kstat v0.0.0-20210513183136-173c9b0a9973 github.com/kubeservice-stack/common v1.9.1 - github.com/montanaflynn/stats v0.9.0 + github.com/montanaflynn/stats v0.12.2 github.com/prometheus/client_golang v1.23.2 github.com/prometheus/common v0.69.0 github.com/prometheus/exporter-toolkit v0.16.0 diff --git a/go.sum b/go.sum index 0172567..a25649e 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,8 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= -github.com/montanaflynn/stats v0.9.0 h1:tsBJ0RXwph9BmAuFoCmqGv6e8xa0MENQ8m0ptKq29mQ= -github.com/montanaflynn/stats v0.9.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/montanaflynn/stats v0.12.2 h1:qHR+IveGjTbO+lnrz1nKR+xpIcOtovJ5Xu0cst99h80= +github.com/montanaflynn/stats v0.12.2/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= diff --git a/vendor/github.com/montanaflynn/stats/.github-write-test b/vendor/github.com/montanaflynn/stats/.github-write-test deleted file mode 100644 index 30d74d2..0000000 --- a/vendor/github.com/montanaflynn/stats/.github-write-test +++ /dev/null @@ -1 +0,0 @@ -test \ No newline at end of file diff --git a/vendor/github.com/montanaflynn/stats/.gitignore b/vendor/github.com/montanaflynn/stats/.gitignore index 75a2a3a..ba3574a 100644 --- a/vendor/github.com/montanaflynn/stats/.gitignore +++ b/vendor/github.com/montanaflynn/stats/.gitignore @@ -2,6 +2,5 @@ coverage.out coverage.txt release-notes.txt .directory -.chglog .vscode .DS_Store \ No newline at end of file diff --git a/vendor/github.com/montanaflynn/stats/CHANGELOG.md b/vendor/github.com/montanaflynn/stats/CHANGELOG.md index 580ce3e..ea3553a 100644 --- a/vendor/github.com/montanaflynn/stats/CHANGELOG.md +++ b/vendor/github.com/montanaflynn/stats/CHANGELOG.md @@ -2,6 +2,117 @@ ## [Unreleased] + + + + + + +## [v0.12.2] - 2026-07-17 +### Fix +- Regression stability and invalid domains ([#124](https://github.com/montanaflynn/stats/issues/124)) + + + +## [v0.12.1] - 2026-07-16 +### Fix +- Stop Entropy from mutating its input slice ([#123](https://github.com/montanaflynn/stats/issues/123)) + + + +## [v0.12.1] - 2026-07-16 +### Fix +- Stop Entropy from mutating its input slice ([#123](https://github.com/montanaflynn/stats/issues/123)) + + + +## [v0.12.0] - 2026-07-16 + + +## [v0.12.0] - 2026-07-16 + + +## [v0.11.0] - 2026-07-13 +### Add +- Add Interp for piecewise-linear interpolation ([#121](https://github.com/montanaflynn/stats/issues/121)) +- Add Histogram with equal-width bins ([#120](https://github.com/montanaflynn/stats/issues/120)) +- Add KendallTau rank correlation coefficient ([#119](https://github.com/montanaflynn/stats/issues/119)) +- Add SEM, RMS, Product, and PercentileOfScore ([#118](https://github.com/montanaflynn/stats/issues/118)) +- Add MovingMedian, MovingMin, MovingMax, MovingSum, and EWMA ([#117](https://github.com/montanaflynn/stats/issues/117)) +- Add TrimmedMean and Winsorize robust statistics ([#116](https://github.com/montanaflynn/stats/issues/116)) +- Add Kurtosis, PopulationKurtosis, and SampleKurtosis ([#115](https://github.com/montanaflynn/stats/issues/115)) +- Add Clip and Rescale elementwise transforms ([#114](https://github.com/montanaflynn/stats/issues/114)) + + + +## [v0.11.0] - 2026-07-13 +### Add +- Add Interp for piecewise-linear interpolation ([#121](https://github.com/montanaflynn/stats/issues/121)) +- Add Histogram with equal-width bins ([#120](https://github.com/montanaflynn/stats/issues/120)) +- Add KendallTau rank correlation coefficient ([#119](https://github.com/montanaflynn/stats/issues/119)) +- Add SEM, RMS, Product, and PercentileOfScore ([#118](https://github.com/montanaflynn/stats/issues/118)) +- Add MovingMedian, MovingMin, MovingMax, MovingSum, and EWMA ([#117](https://github.com/montanaflynn/stats/issues/117)) +- Add TrimmedMean and Winsorize robust statistics ([#116](https://github.com/montanaflynn/stats/issues/116)) +- Add Kurtosis, PopulationKurtosis, and SampleKurtosis ([#115](https://github.com/montanaflynn/stats/issues/115)) +- Add Clip and Rescale elementwise transforms ([#114](https://github.com/montanaflynn/stats/issues/114)) + + + +## [v0.10.0] - 2026-07-10 +### Add +- Add MovingAverage and MovingStdDev ([#112](https://github.com/montanaflynn/stats/issues/112)) +- Add ZScore and Rank functions ([#111](https://github.com/montanaflynn/stats/issues/111)) +- Add WeightedMean and CoefficientOfVariation ([#110](https://github.com/montanaflynn/stats/issues/110)) +- Add ArgMax, ArgMin and Range functions ([#109](https://github.com/montanaflynn/stats/issues/109)) +- Add CumulativeProduct, CumulativeMax and CumulativeMin ([#108](https://github.com/montanaflynn/stats/issues/108)) +- Add Diff and PercentChange functions ([#107](https://github.com/montanaflynn/stats/issues/107)) +- Add weighted percentile function ([#102](https://github.com/montanaflynn/stats/issues/102)) +- Add NormSample function for normal distribution sampling ([#100](https://github.com/montanaflynn/stats/issues/100)) +- Add Z-test and T-test functions ([#99](https://github.com/montanaflynn/stats/issues/99)) +- Add Spearman rank correlation function ([#98](https://github.com/montanaflynn/stats/issues/98)) + +### Fix +- Stabilize GeometricMean and add input validation +- Use math.Round to avoid ARM64 FMA fusion miscompile ([#97](https://github.com/montanaflynn/stats/issues/97)) +- Correct AutoCorrelation lag handling ([#83](https://github.com/montanaflynn/stats/issues/83)) ([#95](https://github.com/montanaflynn/stats/issues/95)) + + + +## [v0.10.0] - 2026-07-10 +### Add +- Add MovingAverage and MovingStdDev ([#112](https://github.com/montanaflynn/stats/issues/112)) +- Add ZScore and Rank functions ([#111](https://github.com/montanaflynn/stats/issues/111)) +- Add WeightedMean and CoefficientOfVariation ([#110](https://github.com/montanaflynn/stats/issues/110)) +- Add ArgMax, ArgMin and Range functions ([#109](https://github.com/montanaflynn/stats/issues/109)) +- Add CumulativeProduct, CumulativeMax and CumulativeMin ([#108](https://github.com/montanaflynn/stats/issues/108)) +- Add Diff and PercentChange functions ([#107](https://github.com/montanaflynn/stats/issues/107)) +- Add weighted percentile function ([#102](https://github.com/montanaflynn/stats/issues/102)) +- Add NormSample function for normal distribution sampling ([#100](https://github.com/montanaflynn/stats/issues/100)) +- Add Z-test and T-test functions ([#99](https://github.com/montanaflynn/stats/issues/99)) +- Add Spearman rank correlation function ([#98](https://github.com/montanaflynn/stats/issues/98)) + +### Fix +- Stabilize GeometricMean and add input validation +- Use math.Round to avoid ARM64 FMA fusion miscompile ([#97](https://github.com/montanaflynn/stats/issues/97)) +- Correct AutoCorrelation lag handling ([#83](https://github.com/montanaflynn/stats/issues/83)) ([#95](https://github.com/montanaflynn/stats/issues/95)) + + + +## [v0.9.0] - 2026-03-24 +### Add +- Add Skewness, PopulationSkewness, and SampleSkewness functions ([#91](https://github.com/montanaflynn/stats/issues/91)) + +### Fix +- Restore 100% test coverage for skewness +- Remove unused sum[4] in LinearRegression + + + +## [v0.8.2] - 2026-03-11 + + +## [v0.8.1] - 2026-03-11 + ## [v0.8.0] - 2026-03-11 ### Fix @@ -527,7 +638,19 @@ - Merge pull request [#4](https://github.com/montanaflynn/stats/issues/4) from saromanov/sample -[Unreleased]: https://github.com/montanaflynn/stats/compare/v0.8.0...HEAD +[Unreleased]: https://github.com/montanaflynn/stats/compare/v0.12.2...HEAD +[v0.12.2]: https://github.com/montanaflynn/stats/compare/v0.12.1...v0.12.2 +[v0.12.1]: https://github.com/montanaflynn/stats/compare/v0.12.0...v0.12.1 +[v0.12.1]: https://github.com/montanaflynn/stats/compare/v0.12.0...v0.12.1 +[v0.12.0]: https://github.com/montanaflynn/stats/compare/v0.11.0...v0.12.0 +[v0.12.0]: https://github.com/montanaflynn/stats/compare/v0.11.0...v0.12.0 +[v0.11.0]: https://github.com/montanaflynn/stats/compare/v0.10.0...v0.11.0 +[v0.11.0]: https://github.com/montanaflynn/stats/compare/v0.10.0...v0.11.0 +[v0.10.0]: https://github.com/montanaflynn/stats/compare/v0.9.0...v0.10.0 +[v0.10.0]: https://github.com/montanaflynn/stats/compare/v0.9.0...v0.10.0 +[v0.9.0]: https://github.com/montanaflynn/stats/compare/v0.8.2...v0.9.0 +[v0.8.2]: https://github.com/montanaflynn/stats/compare/v0.8.1...v0.8.2 +[v0.8.1]: https://github.com/montanaflynn/stats/compare/v0.8.0...v0.8.1 [v0.8.0]: https://github.com/montanaflynn/stats/compare/v0.7.1...v0.8.0 [v0.7.1]: https://github.com/montanaflynn/stats/compare/v0.7.0...v0.7.1 [v0.7.0]: https://github.com/montanaflynn/stats/compare/v0.6.6...v0.7.0 diff --git a/vendor/github.com/montanaflynn/stats/DOCUMENTATION.md b/vendor/github.com/montanaflynn/stats/DOCUMENTATION.md index 32ec075..0dc1c0d 100644 --- a/vendor/github.com/montanaflynn/stats/DOCUMENTATION.md +++ b/vendor/github.com/montanaflynn/stats/DOCUMENTATION.md @@ -36,18 +36,31 @@ MIT License Copyright (c) 2014-2026 Montana Flynn (Index * [Variables](#pkg-variables) +* [func ArgMax(input Float64Data) (int, error)](#ArgMax) +* [func ArgMin(input Float64Data) (int, error)](#ArgMin) * [func AutoCorrelation(data Float64Data, lags int) (float64, error)](#AutoCorrelation) * [func ChebyshevDistance(dataPointX, dataPointY Float64Data) (distance float64, err error)](#ChebyshevDistance) +* [func Clip(input Float64Data, min, max float64) ([]float64, error)](#Clip) +* [func CoefficientOfVariation(input Float64Data) (float64, error)](#CoefficientOfVariation) * [func Correlation(data1, data2 Float64Data) (float64, error)](#Correlation) * [func Covariance(data1, data2 Float64Data) (float64, error)](#Covariance) * [func CovariancePopulation(data1, data2 Float64Data) (float64, error)](#CovariancePopulation) +* [func CumulativeMax(input Float64Data) ([]float64, error)](#CumulativeMax) +* [func CumulativeMin(input Float64Data) ([]float64, error)](#CumulativeMin) +* [func CumulativeProduct(input Float64Data) ([]float64, error)](#CumulativeProduct) * [func CumulativeSum(input Float64Data) ([]float64, error)](#CumulativeSum) +* [func Diff(input Float64Data) ([]float64, error)](#Diff) +* [func EWMA(input Float64Data, alpha float64) ([]float64, error)](#EWMA) * [func Entropy(input Float64Data) (float64, error)](#Entropy) * [func EuclideanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error)](#EuclideanDistance) * [func ExpGeom(p float64) (exp float64, err error)](#ExpGeom) * [func GeometricMean(input Float64Data) (float64, error)](#GeometricMean) * [func HarmonicMean(input Float64Data) (float64, error)](#HarmonicMean) +* [func Histogram(input Float64Data, bins int) ([]int, []float64, error)](#Histogram) * [func InterQuartileRange(input Float64Data) (float64, error)](#InterQuartileRange) +* [func Interp(x, xp, fp Float64Data) ([]float64, error)](#Interp) +* [func KendallTau(data1, data2 Float64Data) (float64, error)](#KendallTau) +* [func Kurtosis(input Float64Data) (float64, error)](#Kurtosis) * [func ManhattanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error)](#ManhattanDistance) * [func Max(input Float64Data) (max float64, err error)](#Max) * [func Mean(input Float64Data) (float64, error)](#Mean) @@ -58,6 +71,12 @@ MIT License Copyright (c) 2014-2026 Montana Flynn (Examples +* [ArgMax](#example_ArgMax) +* [ArgMin](#example_ArgMin) * [AutoCorrelation](#example_AutoCorrelation) * [ChebyshevDistance](#example_ChebyshevDistance) +* [Clip](#example_Clip) * [Correlation](#example_Correlation) +* [CumulativeMax](#example_CumulativeMax) +* [CumulativeMin](#example_CumulativeMin) +* [CumulativeProduct](#example_CumulativeProduct) * [CumulativeSum](#example_CumulativeSum) +* [Diff](#example_Diff) +* [EWMA](#example_EWMA) * [Entropy](#example_Entropy) * [ExpGeom](#example_ExpGeom) +* [Histogram](#example_Histogram) +* [Interp](#example_Interp) +* [KendallTau](#example_KendallTau) +* [Kurtosis](#example_Kurtosis) * [LinearRegression](#example_LinearRegression) * [LoadRawData](#example_LoadRawData) * [Max](#example_Max) * [Median](#example_Median) * [Min](#example_Min) +* [MovingAverage](#example_MovingAverage) +* [MovingMax](#example_MovingMax) +* [MovingMedian](#example_MovingMedian) +* [MovingMin](#example_MovingMin) +* [MovingStdDev](#example_MovingStdDev) +* [MovingSum](#example_MovingSum) +* [PercentChange](#example_PercentChange) +* [PercentileOfScore](#example_PercentileOfScore) * [ProbGeom](#example_ProbGeom) +* [Product](#example_Product) +* [RMS](#example_RMS) +* [Range](#example_Range) +* [Rank](#example_Rank) +* [Rescale](#example_Rescale) * [Round](#example_Round) +* [SEM](#example_SEM) +* [SampleKurtosis](#example_SampleKurtosis) * [Sigmoid](#example_Sigmoid) * [SoftMax](#example_SoftMax) +* [Spearman](#example_Spearman) * [Sum](#example_Sum) +* [TrimmedMean](#example_TrimmedMean) * [VarGeom](#example_VarGeom) +* [Winsorize](#example_Winsorize) +* [ZScore](#example_ZScore) #### Package files -[correlation.go](/src/github.com/montanaflynn/stats/correlation.go) [cumulative_sum.go](/src/github.com/montanaflynn/stats/cumulative_sum.go) [data.go](/src/github.com/montanaflynn/stats/data.go) [describe.go](/src/github.com/montanaflynn/stats/describe.go) [deviation.go](/src/github.com/montanaflynn/stats/deviation.go) [distances.go](/src/github.com/montanaflynn/stats/distances.go) [doc.go](/src/github.com/montanaflynn/stats/doc.go) [entropy.go](/src/github.com/montanaflynn/stats/entropy.go) [errors.go](/src/github.com/montanaflynn/stats/errors.go) [geometric_distribution.go](/src/github.com/montanaflynn/stats/geometric_distribution.go) [legacy.go](/src/github.com/montanaflynn/stats/legacy.go) [load.go](/src/github.com/montanaflynn/stats/load.go) [max.go](/src/github.com/montanaflynn/stats/max.go) [mean.go](/src/github.com/montanaflynn/stats/mean.go) [median.go](/src/github.com/montanaflynn/stats/median.go) [min.go](/src/github.com/montanaflynn/stats/min.go) [mode.go](/src/github.com/montanaflynn/stats/mode.go) [norm.go](/src/github.com/montanaflynn/stats/norm.go) [outlier.go](/src/github.com/montanaflynn/stats/outlier.go) [percentile.go](/src/github.com/montanaflynn/stats/percentile.go) [quartile.go](/src/github.com/montanaflynn/stats/quartile.go) [ranksum.go](/src/github.com/montanaflynn/stats/ranksum.go) [regression.go](/src/github.com/montanaflynn/stats/regression.go) [round.go](/src/github.com/montanaflynn/stats/round.go) [sample.go](/src/github.com/montanaflynn/stats/sample.go) [sigmoid.go](/src/github.com/montanaflynn/stats/sigmoid.go) [softmax.go](/src/github.com/montanaflynn/stats/softmax.go) [sum.go](/src/github.com/montanaflynn/stats/sum.go) [util.go](/src/github.com/montanaflynn/stats/util.go) [variance.go](/src/github.com/montanaflynn/stats/variance.go) +[clip.go](/src/github.com/montanaflynn/stats/clip.go) [coefficient_of_variation.go](/src/github.com/montanaflynn/stats/coefficient_of_variation.go) [correlation.go](/src/github.com/montanaflynn/stats/correlation.go) [cumulative.go](/src/github.com/montanaflynn/stats/cumulative.go) [cumulative_sum.go](/src/github.com/montanaflynn/stats/cumulative_sum.go) [data.go](/src/github.com/montanaflynn/stats/data.go) [describe.go](/src/github.com/montanaflynn/stats/describe.go) [deviation.go](/src/github.com/montanaflynn/stats/deviation.go) [diff.go](/src/github.com/montanaflynn/stats/diff.go) [distances.go](/src/github.com/montanaflynn/stats/distances.go) [doc.go](/src/github.com/montanaflynn/stats/doc.go) [entropy.go](/src/github.com/montanaflynn/stats/entropy.go) [errors.go](/src/github.com/montanaflynn/stats/errors.go) [ewma.go](/src/github.com/montanaflynn/stats/ewma.go) [extremes.go](/src/github.com/montanaflynn/stats/extremes.go) [geometric_distribution.go](/src/github.com/montanaflynn/stats/geometric_distribution.go) [histogram.go](/src/github.com/montanaflynn/stats/histogram.go) [interp.go](/src/github.com/montanaflynn/stats/interp.go) [kendall.go](/src/github.com/montanaflynn/stats/kendall.go) [kurtosis.go](/src/github.com/montanaflynn/stats/kurtosis.go) [legacy.go](/src/github.com/montanaflynn/stats/legacy.go) [load.go](/src/github.com/montanaflynn/stats/load.go) [max.go](/src/github.com/montanaflynn/stats/max.go) [mean.go](/src/github.com/montanaflynn/stats/mean.go) [median.go](/src/github.com/montanaflynn/stats/median.go) [min.go](/src/github.com/montanaflynn/stats/min.go) [mode.go](/src/github.com/montanaflynn/stats/mode.go) [moving.go](/src/github.com/montanaflynn/stats/moving.go) [norm.go](/src/github.com/montanaflynn/stats/norm.go) [outlier.go](/src/github.com/montanaflynn/stats/outlier.go) [percentile.go](/src/github.com/montanaflynn/stats/percentile.go) [percentile_of_score.go](/src/github.com/montanaflynn/stats/percentile_of_score.go) [percentile_weighted.go](/src/github.com/montanaflynn/stats/percentile_weighted.go) [product.go](/src/github.com/montanaflynn/stats/product.go) [quartile.go](/src/github.com/montanaflynn/stats/quartile.go) [rank.go](/src/github.com/montanaflynn/stats/rank.go) [ranksum.go](/src/github.com/montanaflynn/stats/ranksum.go) [regression.go](/src/github.com/montanaflynn/stats/regression.go) [rescale.go](/src/github.com/montanaflynn/stats/rescale.go) [rms.go](/src/github.com/montanaflynn/stats/rms.go) [rolling.go](/src/github.com/montanaflynn/stats/rolling.go) [round.go](/src/github.com/montanaflynn/stats/round.go) [sample.go](/src/github.com/montanaflynn/stats/sample.go) [sem.go](/src/github.com/montanaflynn/stats/sem.go) [sigmoid.go](/src/github.com/montanaflynn/stats/sigmoid.go) [skewness.go](/src/github.com/montanaflynn/stats/skewness.go) [softmax.go](/src/github.com/montanaflynn/stats/softmax.go) [sum.go](/src/github.com/montanaflynn/stats/sum.go) [trimmed_mean.go](/src/github.com/montanaflynn/stats/trimmed_mean.go) [ttest.go](/src/github.com/montanaflynn/stats/ttest.go) [util.go](/src/github.com/montanaflynn/stats/util.go) [variance.go](/src/github.com/montanaflynn/stats/variance.go) [weighted_mean.go](/src/github.com/montanaflynn/stats/weighted_mean.go) [winsorize.go](/src/github.com/montanaflynn/stats/winsorize.go) [zscore.go](/src/github.com/montanaflynn/stats/zscore.go) [ztest.go](/src/github.com/montanaflynn/stats/ztest.go) @@ -223,7 +328,25 @@ Legacy error names that didn't start with Err -## func [AutoCorrelation](/correlation.go?s=853:918#L38) +## func [ArgMax](/extremes.go?s=129:172#L5) +``` go +func ArgMax(input Float64Data) (int, error) +``` +ArgMax finds the index of the highest number in a slice, +returning the first occurrence in the case of ties + + + +## func [ArgMin](/extremes.go?s=671:714#L28) +``` go +func ArgMin(input Float64Data) (int, error) +``` +ArgMin finds the index of the lowest number in a slice, +returning the first occurrence in the case of ties + + + +## func [AutoCorrelation](/correlation.go?s=2282:2347#L102) ``` go func AutoCorrelation(data Float64Data, lags int) (float64, error) ``` @@ -239,7 +362,29 @@ ChebyshevDistance computes the Chebyshev distance between two data sets -## func [Correlation](/correlation.go?s=112:171#L8) +## func [Clip](/clip.go?s=109:174#L5) +``` go +func Clip(input Float64Data, min, max float64) ([]float64, error) +``` +Clip clamps each value in the input slice into the +inclusive range between min and max. + + + +## func [CoefficientOfVariation](/coefficient_of_variation.go?s=322:385#L11) +``` go +func CoefficientOfVariation(input Float64Data) (float64, error) +``` +CoefficientOfVariation finds the coefficient of variation of a slice +of floats, defined as the sample standard deviation divided by the +mean. This matches the behavior of Python's scipy.stats.variation +with ddof=1. + +The input must not be empty and its mean must not be zero. + + + +## func [Correlation](/correlation.go?s=120:179#L9) ``` go func Correlation(data1, data2 Float64Data) (float64, error) ``` @@ -263,6 +408,30 @@ CovariancePopulation computes covariance for entire population between two varia +## func [CumulativeMax](/cumulative.go?s=486:542#L24) +``` go +func CumulativeMax(input Float64Data) ([]float64, error) +``` +CumulativeMax calculates the cumulative maximum of the input slice + + + +## func [CumulativeMin](/cumulative.go?s=874:930#L44) +``` go +func CumulativeMin(input Float64Data) ([]float64, error) +``` +CumulativeMin calculates the cumulative minimum of the input slice + + + +## func [CumulativeProduct](/cumulative.go?s=89:149#L4) +``` go +func CumulativeProduct(input Float64Data) ([]float64, error) +``` +CumulativeProduct calculates the cumulative product of the input slice + + + ## func [CumulativeSum](/cumulative_sum.go?s=81:137#L4) ``` go func CumulativeSum(input Float64Data) ([]float64, error) @@ -271,6 +440,30 @@ CumulativeSum calculates the cumulative sum of the input slice +## func [Diff](/diff.go?s=238:285#L7) +``` go +func Diff(input Float64Data) ([]float64, error) +``` +Diff calculates the successive differences of the input slice, +returning input[i] - input[i-1] for each i in 1..len(input)-1. +The output has length len(input) - 1; a single-element input +returns an empty slice. + + + +## func [EWMA](/ewma.go?s=392:454#L9) +``` go +func EWMA(input Float64Data, alpha float64) ([]float64, error) +``` +EWMA calculates the exponentially weighted moving average of the input +with smoothing factor alpha. The first output equals the first input and +each subsequent entry is alpha*input[i] + (1-alpha)*output[i-1], so the +result has the same length as the input. The alpha must satisfy +0 < alpha <= 1 or ErrBounds is returned. An empty input returns +ErrEmptyInput. + + + ## func [Entropy](/entropy.go?s=77:125#L6) ``` go func Entropy(input Float64Data) (float64, error) @@ -304,7 +497,7 @@ GeometricMean gets the geometric mean for a slice of numbers -## func [HarmonicMean](/mean.go?s=717:770#L40) +## func [HarmonicMean](/mean.go?s=842:895#L41) ``` go func HarmonicMean(input Float64Data) (float64, error) ``` @@ -312,6 +505,18 @@ HarmonicMean gets the harmonic mean for a slice of numbers +## func [Histogram](/histogram.go?s=327:396#L10) +``` go +func Histogram(input Float64Data, bins int) ([]int, []float64, error) +``` +Histogram calculates the histogram of a slice using the given +number of equal-width bins over [min, max], returning the count +of values in each bin along with the bins+1 bin edges. Each bin +is half-open [edges[i], edges[i+1]) except the last, which also +includes the maximum value. + + + ## func [InterQuartileRange](/quartile.go?s=821:880#L45) ``` go func InterQuartileRange(input Float64Data) (float64, error) @@ -320,6 +525,39 @@ InterQuartileRange finds the range between Q1 and Q3 +## func [Interp](/interp.go?s=547:600#L12) +``` go +func Interp(x, xp, fp Float64Data) ([]float64, error) +``` +Interp calculates the one-dimensional piecewise-linear interpolant to a +function with given discrete data points (xp, fp), evaluated at each x. +Values of x below xp[0] return fp[0] and values above xp[len(xp)-1] return +fp[len(xp)-1], so no extrapolation is performed. Unlike numpy's interp, +which silently returns nonsense for unsorted coordinates, xp must be +strictly increasing or ErrBounds is returned. An empty x or xp returns +ErrEmptyInput and xp and fp of different lengths return ErrSize. + + + +## func [KendallTau](/kendall.go?s=302:360#L9) +``` go +func KendallTau(data1, data2 Float64Data) (float64, error) +``` +KendallTau calculates Kendall's tau-b rank correlation coefficient +between two variables. Tau-b corrects for ties, matching the values +produced by SciPy's kendalltau and pandas' corr(method="kendall"). +Pairs are compared with a simple O(n^2) loop for clarity. + + + +## func [Kurtosis](/kurtosis.go?s=97:146#L6) +``` go +func Kurtosis(input Float64Data) (float64, error) +``` +Kurtosis computes the population excess kurtosis of the dataset + + + ## func [ManhattanDistance](/distances.go?s=1277:1365#L50) ``` go func ManhattanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) @@ -417,7 +655,81 @@ Mode gets the mode [most frequent value(s)] of a slice of float64s -## func [Ncr](/norm.go?s=7384:7406#L239) +## func [MovingAverage](/rolling.go?s=362:430#L8) +``` go +func MovingAverage(input Float64Data, window int) ([]float64, error) +``` +MovingAverage calculates the rolling mean of the input over a trailing +window. Only fully-populated windows produce output, so the result has +len(input)-window+1 entries and entry i is the mean of input[i : i+window]. +The window must satisfy 1 <= window <= len(input) or ErrBounds is +returned. An empty input returns ErrEmptyInput. + + + +## func [MovingMax](/moving.go?s=1892:1956#L60) +``` go +func MovingMax(input Float64Data, window int) ([]float64, error) +``` +MovingMax calculates the rolling maximum of the input over a trailing +window. Only fully-populated windows produce output, so the result has +len(input)-window+1 entries and entry i is the maximum of +input[i : i+window]. The window must satisfy 1 <= window <= len(input) or +ErrBounds is returned. An empty input returns ErrEmptyInput. + + + +## func [MovingMedian](/moving.go?s=365:432#L8) +``` go +func MovingMedian(input Float64Data, window int) ([]float64, error) +``` +MovingMedian calculates the rolling median of the input over a trailing +window. Only fully-populated windows produce output, so the result has +len(input)-window+1 entries and entry i is the median of input[i : i+window]. +The window must satisfy 1 <= window <= len(input) or ErrBounds is +returned. An empty input returns ErrEmptyInput. + + + +## func [MovingMin](/moving.go?s=1136:1200#L34) +``` go +func MovingMin(input Float64Data, window int) ([]float64, error) +``` +MovingMin calculates the rolling minimum of the input over a trailing +window. Only fully-populated windows produce output, so the result has +len(input)-window+1 entries and entry i is the minimum of +input[i : i+window]. The window must satisfy 1 <= window <= len(input) or +ErrBounds is returned. An empty input returns ErrEmptyInput. + + + +## func [MovingStdDev](/rolling.go?s=1239:1306#L36) +``` go +func MovingStdDev(input Float64Data, window int) ([]float64, error) +``` +MovingStdDev calculates the rolling sample standard deviation of the input +over a trailing window. Only fully-populated windows produce output, so the +result has len(input)-window+1 entries and entry i is the sample standard +deviation of input[i : i+window]. The window must satisfy +2 <= window <= len(input) or ErrBounds is returned, since the sample +standard deviation of a single value is undefined. An empty input returns +ErrEmptyInput. + + + +## func [MovingSum](/moving.go?s=2640:2704#L86) +``` go +func MovingSum(input Float64Data, window int) ([]float64, error) +``` +MovingSum calculates the rolling sum of the input over a trailing +window. Only fully-populated windows produce output, so the result has +len(input)-window+1 entries and entry i is the sum of input[i : i+window]. +The window must satisfy 1 <= window <= len(input) or ErrBounds is +returned. An empty input returns ErrEmptyInput. + + + +## func [Ncr](/norm.go?s=7623:7645#L245) ``` go func Ncr(n, r int) int ``` @@ -426,7 +738,7 @@ Aaron Cannon's algorithm. -## func [NormBoxMullerRvs](/norm.go?s=667:736#L23) +## func [NormBoxMullerRvs](/norm.go?s=906:975#L29) ``` go func NormBoxMullerRvs(loc float64, scale float64, size int) []float64 ``` @@ -435,7 +747,7 @@ For more information please visit: func [NormCdf](/norm.go?s=1826:1885#L52) +## func [NormCdf](/norm.go?s=2065:2124#L58) ``` go func NormCdf(x float64, loc float64, scale float64) float64 ``` @@ -443,7 +755,7 @@ NormCdf is the cumulative distribution function. -## func [NormEntropy](/norm.go?s=5773:5825#L180) +## func [NormEntropy](/norm.go?s=6012:6064#L186) ``` go func NormEntropy(loc float64, scale float64) float64 ``` @@ -451,7 +763,7 @@ NormEntropy is the differential entropy of the RV. -## func [NormFit](/norm.go?s=6058:6097#L187) +## func [NormFit](/norm.go?s=6297:6336#L193) ``` go func NormFit(data []float64) [2]float64 ``` @@ -461,7 +773,7 @@ Returns array of Mean followed by Standard Deviation. -## func [NormInterval](/norm.go?s=6976:7047#L221) +## func [NormInterval](/norm.go?s=7215:7286#L227) ``` go func NormInterval(alpha float64, loc float64, scale float64) [2]float64 ``` @@ -469,7 +781,7 @@ NormInterval finds endpoints of the range that contains alpha percent of the dis -## func [NormIsf](/norm.go?s=4330:4393#L137) +## func [NormIsf](/norm.go?s=4569:4632#L143) ``` go func NormIsf(p float64, loc float64, scale float64) (x float64) ``` @@ -477,7 +789,7 @@ NormIsf is the inverse survival function (inverse of sf). -## func [NormLogCdf](/norm.go?s=2016:2078#L57) +## func [NormLogCdf](/norm.go?s=2255:2317#L63) ``` go func NormLogCdf(x float64, loc float64, scale float64) float64 ``` @@ -485,7 +797,7 @@ NormLogCdf is the log of the cumulative distribution function. -## func [NormLogPdf](/norm.go?s=1590:1652#L47) +## func [NormLogPdf](/norm.go?s=1829:1891#L53) ``` go func NormLogPdf(x float64, loc float64, scale float64) float64 ``` @@ -493,7 +805,7 @@ NormLogPdf is the log of the probability density function. -## func [NormLogSf](/norm.go?s=2423:2484#L67) +## func [NormLogSf](/norm.go?s=2662:2723#L73) ``` go func NormLogSf(x float64, loc float64, scale float64) float64 ``` @@ -501,7 +813,7 @@ NormLogSf is the log of the survival function. -## func [NormMean](/norm.go?s=6560:6609#L206) +## func [NormMean](/norm.go?s=6799:6848#L212) ``` go func NormMean(loc float64, scale float64) float64 ``` @@ -509,7 +821,7 @@ NormMean is the mean/expected value of the distribution. -## func [NormMedian](/norm.go?s=6431:6482#L201) +## func [NormMedian](/norm.go?s=6670:6721#L207) ``` go func NormMedian(loc float64, scale float64) float64 ``` @@ -517,7 +829,7 @@ NormMedian is the median of the distribution. -## func [NormMoment](/norm.go?s=4694:4752#L146) +## func [NormMoment](/norm.go?s=4933:4991#L152) ``` go func NormMoment(n int, loc float64, scale float64) float64 ``` @@ -526,7 +838,7 @@ For more information please visit: func [NormPdf](/norm.go?s=1357:1416#L42) +## func [NormPdf](/norm.go?s=1596:1655#L48) ``` go func NormPdf(x float64, loc float64, scale float64) float64 ``` @@ -534,7 +846,7 @@ NormPdf is the probability density function. -## func [NormPpf](/norm.go?s=2854:2917#L75) +## func [NormPpf](/norm.go?s=3093:3156#L81) ``` go func NormPpf(p float64, loc float64, scale float64) (x float64) ``` @@ -545,7 +857,7 @@ For more information please visit: func [NormPpfRvs](/norm.go?s=247:310#L12) +## func [NormPpfRvs](/norm.go?s=486:549#L18) ``` go func NormPpfRvs(loc float64, scale float64, size int) []float64 ``` @@ -554,7 +866,16 @@ For more information please visit: func [NormSf](/norm.go?s=2250:2308#L62) +## func [NormSample](/norm.go?s=194:257#L12) +``` go +func NormSample(loc float64, scale float64, size int) []float64 +``` +NormSample generates random samples from a normal distribution +with the given mean (loc) and standard deviation (scale). + + + +## func [NormSf](/norm.go?s=2489:2547#L68) ``` go func NormSf(x float64, loc float64, scale float64) float64 ``` @@ -562,7 +883,7 @@ NormSf is the survival function (also defined as 1 - cdf, but sf is sometimes mo -## func [NormStats](/norm.go?s=5277:5345#L162) +## func [NormStats](/norm.go?s=5516:5584#L168) ``` go func NormStats(loc float64, scale float64, moments string) []float64 ``` @@ -573,7 +894,7 @@ Returns array of m v s k in that order. -## func [NormStd](/norm.go?s=6814:6862#L216) +## func [NormStd](/norm.go?s=7053:7101#L222) ``` go func NormStd(loc float64, scale float64) float64 ``` @@ -581,7 +902,7 @@ NormStd is the standard deviation of the distribution. -## func [NormVar](/norm.go?s=6675:6723#L211) +## func [NormVar](/norm.go?s=6914:6962#L217) ``` go func NormVar(loc float64, scale float64) float64 ``` @@ -589,7 +910,7 @@ NormVar is the variance of the distribution. -## func [Pearson](/correlation.go?s=655:710#L33) +## func [Pearson](/correlation.go?s=663:718#L34) ``` go func Pearson(data1, data2 Float64Data) (float64, error) ``` @@ -597,6 +918,20 @@ Pearson calculates the Pearson product-moment correlation coefficient between tw +## func [PercentChange](/diff.go?s=891:947#L29) +``` go +func PercentChange(input Float64Data) ([]float64, error) +``` +PercentChange calculates the fractional change between successive +elements of the input slice, returning +(input[i] - input[i-1]) / input[i-1] for each i in 1..len(input)-1. +The output has length len(input) - 1; a single-element input +returns an empty slice. A zero denominator follows IEEE 754 +semantics, yielding +Inf, -Inf, or NaN (for 0/0), matching the +behavior of pandas pct_change. + + + ## func [Percentile](/percentile.go?s=598:681#L20) ``` go func Percentile(input Float64Data, percent float64) (percentile float64, err error) @@ -626,6 +961,56 @@ PercentileNearestRank finds the relative standing in a slice of floats using the +## func [PercentileOfScore](/percentile_of_score.go?s=374:447#L11) +``` go +func PercentileOfScore(input Float64Data, score float64) (float64, error) +``` +PercentileOfScore calculates the percentile rank of a score +relative to a slice of floats, defined as the percentage of +values strictly below the score plus half the percentage of +values equal to the score. The result is between 0 and 100. +This matches the behavior of Python's +scipy.stats.percentileofscore with kind="rank". + + + +## func [PercentileWeighted](/percentile_weighted.go?s=620:719#L19) +``` go +func PercentileWeighted(data, weights Float64Data, percent float64) (percentile float64, err error) +``` +PercentileWeighted finds the weighted percentile of a slice of floats +using the weighted empirical CDF (inverse CDF / nearest-rank method). + +For a given percent p, it returns the smallest data value x such that +the cumulative weight of all values <= x is at least p% of the total +weight. This matches the behavior of Python's statsmodels +DescrStatsW.quantile. + +The data and weights slices must be the same length. Weights must be +non-negative and at least one weight must be positive. The percent +parameter must be between 0 and 100 (exclusive). + + + +## func [PopulationKurtosis](/kurtosis.go?s=391:450#L13) +``` go +func PopulationKurtosis(input Float64Data) (float64, error) +``` +PopulationKurtosis computes the population excess kurtosis (Fisher +definition) using the fourth central moment normalized by the squared +variance, so a normal distribution has a kurtosis of zero. + + + +## func [PopulationSkewness](/skewness.go?s=318:377#L12) +``` go +func PopulationSkewness(input Float64Data) (float64, error) +``` +PopulationSkewness computes the population skewness using the third +central moment normalized by the cube of the standard deviation. + + + ## func [PopulationVariance](/variance.go?s=828:896#L31) ``` go func PopulationVariance(input Float64Data) (pvar float64, err error) @@ -644,6 +1029,56 @@ See https://en.wi +## func [Product](/product.go?s=299:347#L10) +``` go +func Product(input Float64Data) (float64, error) +``` +Product calculates the product of a slice of floats by +multiplying the values from left to right. It is the scalar +counterpart of CumulativeProduct. Large inputs can overflow +to Inf; use GeometricMean for an overflow-safe summary of +multiplicative data. + + + +## func [RMS](/rms.go?s=156:200#L7) +``` go +func RMS(input Float64Data) (float64, error) +``` +RMS calculates the root mean square of a slice of floats, +defined as the square root of the mean of the squared values. + + + +## func [Range](/extremes.go?s=1181:1227#L51) +``` go +func Range(input Float64Data) (float64, error) +``` +Range finds the difference between the highest and +lowest numbers in a slice + + + +## func [Rank](/rank.go?s=183:230#L6) +``` go +func Rank(input Float64Data) ([]float64, error) +``` +Rank assigns fractional (average) ranks to the input values. +Ranks are 1-based and tied values receive the average of the +ranks they would have been assigned. + + + +## func [Rescale](/rescale.go?s=174:224#L6) +``` go +func Rescale(input Float64Data) ([]float64, error) +``` +Rescale normalizes the input values to the range of 0 to 1 +by subtracting the minimum and dividing by the range, +also known as min-max normalization. + + + ## func [Round](/round.go?s=88:154#L6) ``` go func Round(input float64, places int) (rounded float64, err error) @@ -652,6 +1087,17 @@ Round a float to a specific decimal place or precision +## func [SEM](/sem.go?s=265:309#L9) +``` go +func SEM(input Float64Data) (float64, error) +``` +SEM calculates the standard error of the mean of a slice +of floats, defined as the sample standard deviation divided +by the square root of the sample size. This matches the +behavior of Python's scipy.stats.sem with ddof=1. + + + ## func [Sample](/sample.go?s=112:192#L9) ``` go func Sample(input Float64Data, takenum int, replacement bool) ([]float64, error) @@ -660,6 +1106,24 @@ Sample returns sample from input with replacement or without +## func [SampleKurtosis](/kurtosis.go?s=1071:1126#L41) +``` go +func SampleKurtosis(input Float64Data) (float64, error) +``` +SampleKurtosis computes the bias-corrected sample excess kurtosis, +matching pandas .kurt() and scipy.stats.kurtosis with bias=False. + + + +## func [SampleSkewness](/skewness.go?s=1049:1104#L44) +``` go +func SampleSkewness(input Float64Data) (float64, error) +``` +SampleSkewness computes the adjusted Fisher-Pearson standardized moment +coefficient, correcting for bias in small samples. + + + ## func [SampleVariance](/variance.go?s=1058:1122#L42) ``` go func SampleVariance(input Float64Data) (svar float64, err error) @@ -679,6 +1143,14 @@ activation function. +## func [Skewness](/skewness.go?s=90:139#L6) +``` go +func Skewness(input Float64Data) (float64, error) +``` +Skewness computes the population skewness of the dataset + + + ## func [SoftMax](/softmax.go?s=206:256#L8) ``` go func SoftMax(input Float64Data) ([]float64, error) @@ -689,6 +1161,16 @@ is commonly used in machine learning neural networks. +## func [Spearman](/correlation.go?s=1006:1062#L41) +``` go +func Spearman(data1, data2 Float64Data) (float64, error) +``` +Spearman calculates the Spearman rank correlation coefficient between two variables. +It works by ranking the data and then computing the Pearson correlation of the ranks. +This method handles tied values using fractional (average) ranking. + + + ## func [StableSample](/sample.go?s=974:1042#L50) ``` go func StableSample(input Float64Data, takenum int) ([]float64, error) @@ -745,6 +1227,24 @@ Sum adds all the numbers of a slice together +## func [TTest](/ttest.go?s=505:604#L16) +``` go +func TTest(data1, data2 Float64Data, populationMean float64) (t float64, pvalue float64, err error) +``` +TTest performs a one-sample or two-sample (independent) Student's t-test. + +For a one-sample t-test, pass the sample data as data1, nil for data2, +and the expected population mean as populationMean. + +For a two-sample independent t-test (assuming equal variance), pass both +sample datasets. The populationMean parameter is ignored in this case. + +Returns the t statistic and the two-tailed p-value. + +https://en.wikipedia.org/wiki/Student%27s_t-test + + + ## func [Trimean](/quartile.go?s=1320:1368#L65) ``` go func Trimean(input Float64Data) (float64, error) @@ -753,6 +1253,21 @@ Trimean finds the average of the median and the midhinge +## func [TrimmedMean](/trimmed_mean.go?s=450:519#L13) +``` go +func TrimmedMean(input Float64Data, percent float64) (float64, error) +``` +TrimmedMean finds the mean of a slice of floats after removing a +fraction of the smallest and largest values. This matches the +behavior of Python's scipy.stats.trim_mean. + +The percent parameter is the fraction removed from each tail and +must be in the range [0, 0.5). The number of elements trimmed from +each tail is floor(len(input) * percent). A percent of zero returns +the same result as Mean. + + + ## func [VarGeom](/geometric_distribution.go?s=885:933#L37) ``` go func VarGeom(p float64) (exp float64, err error) @@ -786,6 +1301,67 @@ Variance the amount of variation in the dataset +## func [WeightedMean](/weighted_mean.go?s=415:476#L12) +``` go +func WeightedMean(data, weights Float64Data) (float64, error) +``` +WeightedMean finds the weighted mean of a slice of floats, defined as +the sum of each data value multiplied by its weight divided by the sum +of all the weights. This matches the behavior of Python's +numpy.average with the weights argument. + +The data and weights slices must be the same length. Weights must be +non-negative and at least one weight must be positive. + + + +## func [Winsorize](/winsorize.go?s=618:687#L16) +``` go +func Winsorize(input Float64Data, percent float64) ([]float64, error) +``` +Winsorize limits the effect of outliers in a slice of floats by +clamping a fraction of the smallest and largest values. This matches +the behavior of Python's scipy.stats.mstats.winsorize with symmetric +limits. + +The percent parameter is the fraction clamped in each tail and must +be in the range [0, 0.5). With k = floor(len(input) * percent), +values below the k-th smallest value are set to it and values above +the k-th largest value are set to it. The returned slice preserves +the original element order and a percent of zero returns a copy of +the input. + + + +## func [ZScore](/zscore.go?s=205:254#L6) +``` go +func ZScore(input Float64Data) ([]float64, error) +``` +ZScore standardizes the input values by subtracting the mean +and dividing by the sample standard deviation, returning the +number of standard deviations each value is from the mean. + + + +## func [ZTest](/ztest.go?s=537:654#L17) +``` go +func ZTest(data1, data2 Float64Data, populationMean, populationStdDev float64) (z float64, pvalue float64, err error) +``` +ZTest performs a one-sample or two-sample Z-test. + +For a one-sample Z-test, pass the sample data as data1, nil for data2, +the known population mean as populationMean, and the known population +standard deviation as populationStdDev. + +For a two-sample Z-test, pass both sample datasets and the known population +standard deviations. The populationMean parameter is ignored in this case. + +Returns the Z statistic and the two-tailed p-value. + +https://en.wikipedia.org/wiki/Z-test + + + ## type [Coordinate](/regression.go?s=143:183#L9) ``` go @@ -826,7 +1402,7 @@ LogReg is a shortcut to LogarithmicRegression -## type [Description](/describe.go?s=89:349#L6) +## type [Description](/describe.go?s=89:381#L6) ``` go type Description struct { Count int @@ -834,6 +1410,7 @@ type Description struct { Std float64 Max float64 Min float64 + Range float64 DescriptionPercentiles []descriptionPercentile AllowedNaN bool } @@ -847,14 +1424,14 @@ Holds information about the dataset provided to Describe -### func [Describe](/describe.go?s=579:672#L23) +### func [Describe](/describe.go?s=611:704#L24) ``` go func Describe(input Float64Data, allowNaN bool, percentiles *[]float64) (*Description, error) ``` Describe generates descriptive statistics about a provided dataset, similar to python's pandas.describe() -### func [DescribePercentileFunc](/describe.go?s=917:1084#L29) +### func [DescribePercentileFunc](/describe.go?s=949:1116#L30) ``` go func DescribePercentileFunc(input Float64Data, allowNaN bool, percentiles *[]float64, percentileFunc func(Float64Data, float64) (float64, error)) (*Description, error) ``` @@ -865,7 +1442,7 @@ Takes in a function to use for percentile calculation -### func (\*Description) [String](/describe.go?s=2078:2127#L68) +### func (\*Description) [String](/describe.go?s=2161:2210#L71) ``` go func (d *Description) String(decimals int) string ``` @@ -877,6 +1454,7 @@ Represents the Description instance in a string format with specified number of std 0.82 max 3.00 min 1.00 + range 2.00 25.00% NaN 50.00% 1.50 75.00% 2.50 @@ -907,7 +1485,25 @@ LoadRawData parses and converts a slice of mixed data types to floats -### func (Float64Data) [AutoCorrelation](/data.go?s=3257:3320#L91) +### func (Float64Data) [ArgMax](/extremes.go?s=1521:1563#L66) +``` go +func (f Float64Data) ArgMax() (int, error) +``` +ArgMax returns the index of the highest number in the data + + + + +### func (Float64Data) [ArgMin](/extremes.go?s=1647:1689#L69) +``` go +func (f Float64Data) ArgMin() (int, error) +``` +ArgMin returns the index of the lowest number in the data + + + + +### func (Float64Data) [AutoCorrelation](/data.go?s=3274:3337#L91) ``` go func (f Float64Data) AutoCorrelation(lags int) (float64, error) ``` @@ -916,7 +1512,26 @@ AutoCorrelation is the correlation of a signal with a delayed copy of itself as -### func (Float64Data) [Correlation](/data.go?s=3058:3122#L86) +### func (Float64Data) [Clip](/clip.go?s=550:612#L30) +``` go +func (f Float64Data) Clip(min, max float64) ([]float64, error) +``` +Clip clamps each value in the input slice into the +inclusive range between min and max. + + + + +### func (Float64Data) [CoefficientOfVariation](/coefficient_of_variation.go?s=768:830#L29) +``` go +func (f Float64Data) CoefficientOfVariation() (float64, error) +``` +CoefficientOfVariation finds the sample standard deviation divided by the mean + + + + +### func (Float64Data) [Correlation](/data.go?s=3075:3139#L86) ``` go func (f Float64Data) Correlation(d Float64Data) (float64, error) ``` @@ -925,7 +1540,7 @@ Correlation describes the degree of relationship between two sets of data -### func (Float64Data) [Covariance](/data.go?s=4801:4864#L141) +### func (Float64Data) [Covariance](/data.go?s=4996:5059#L146) ``` go func (f Float64Data) Covariance(d Float64Data) (float64, error) ``` @@ -934,7 +1549,7 @@ Covariance is a measure of how much two sets of data change -### func (Float64Data) [CovariancePopulation](/data.go?s=4983:5056#L146) +### func (Float64Data) [CovariancePopulation](/data.go?s=5178:5251#L151) ``` go func (f Float64Data) CovariancePopulation(d Float64Data) (float64, error) ``` @@ -943,6 +1558,33 @@ CovariancePopulation computes covariance for entire population between two varia +### func (Float64Data) [CumulativeMax](/cumulative.go?s=1416:1471#L69) +``` go +func (f Float64Data) CumulativeMax() ([]float64, error) +``` +CumulativeMax calculates the cumulative maximum of the data + + + + +### func (Float64Data) [CumulativeMin](/cumulative.go?s=1565:1620#L74) +``` go +func (f Float64Data) CumulativeMin() ([]float64, error) +``` +CumulativeMin calculates the cumulative minimum of the data + + + + +### func (Float64Data) [CumulativeProduct](/cumulative.go?s=1259:1318#L64) +``` go +func (f Float64Data) CumulativeProduct() ([]float64, error) +``` +CumulativeProduct calculates the cumulative product of the data + + + + ### func (Float64Data) [CumulativeSum](/data.go?s=883:938#L28) ``` go func (f Float64Data) CumulativeSum() ([]float64, error) @@ -952,7 +1594,25 @@ CumulativeSum returns the cumulative sum of the data -### func (Float64Data) [Entropy](/data.go?s=5480:5527#L162) +### func (Float64Data) [Diff](/diff.go?s=1220:1266#L45) +``` go +func (f Float64Data) Diff() ([]float64, error) +``` +Diff returns the successive differences of the data + + + + +### func (Float64Data) [EWMA](/ewma.go?s=848:907#L30) +``` go +func (f Float64Data) EWMA(alpha float64) ([]float64, error) +``` +EWMA returns the exponentially weighted moving average of the data with smoothing factor alpha + + + + +### func (Float64Data) [Entropy](/data.go?s=5675:5722#L167) ``` go func (f Float64Data) Entropy() (float64, error) ``` @@ -961,11 +1621,11 @@ Entropy provides calculation of the entropy -### func (Float64Data) [GeometricMean](/data.go?s=1332:1385#L40) +### func (Float64Data) [GeometricMean](/data.go?s=1340:1393#L40) ``` go func (f Float64Data) GeometricMean() (float64, error) ``` -GeometricMean returns the median of the data +GeometricMean returns the geometric mean of the data @@ -979,16 +1639,25 @@ Get item in slice -### func (Float64Data) [HarmonicMean](/data.go?s=1460:1512#L43) +### func (Float64Data) [HarmonicMean](/data.go?s=1477:1529#L43) ``` go func (f Float64Data) HarmonicMean() (float64, error) ``` -HarmonicMean returns the mode of the data +HarmonicMean returns the harmonic mean of the data + + + + +### func (Float64Data) [Histogram](/histogram.go?s=1359:1425#L55) +``` go +func (f Float64Data) Histogram(bins int) ([]int, []float64, error) +``` +Histogram returns the counts and equal-width bin edges of the data -### func (Float64Data) [InterQuartileRange](/data.go?s=3755:3813#L106) +### func (Float64Data) [InterQuartileRange](/data.go?s=3950:4008#L111) ``` go func (f Float64Data) InterQuartileRange() (float64, error) ``` @@ -997,6 +1666,25 @@ InterQuartileRange finds the range between Q1 and Q3 +### func (Float64Data) [KendallTau](/kendall.go?s=1384:1447#L55) +``` go +func (f Float64Data) KendallTau(d Float64Data) (float64, error) +``` +KendallTau calculates Kendall's tau-b rank correlation coefficient +between two variables. + + + + +### func (Float64Data) [Kurtosis](/kurtosis.go?s=1501:1549#L58) +``` go +func (f Float64Data) Kurtosis() (float64, error) +``` +Kurtosis finds the population excess kurtosis of a slice of floats + + + + ### func (Float64Data) [Len](/data.go?s=217:247#L10) ``` go func (f Float64Data) Len() int @@ -1042,7 +1730,7 @@ Median returns the median of the data -### func (Float64Data) [MedianAbsoluteDeviation](/data.go?s=1630:1693#L46) +### func (Float64Data) [MedianAbsoluteDeviation](/data.go?s=1647:1710#L46) ``` go func (f Float64Data) MedianAbsoluteDeviation() (float64, error) ``` @@ -1051,7 +1739,7 @@ MedianAbsoluteDeviation the median of the absolute deviations from the dataset m -### func (Float64Data) [MedianAbsoluteDeviationPopulation](/data.go?s=1842:1915#L51) +### func (Float64Data) [MedianAbsoluteDeviationPopulation](/data.go?s=1859:1932#L51) ``` go func (f Float64Data) MedianAbsoluteDeviationPopulation() (float64, error) ``` @@ -1060,7 +1748,7 @@ MedianAbsoluteDeviationPopulation finds the median of the absolute deviations fr -### func (Float64Data) [Midhinge](/data.go?s=3912:3973#L111) +### func (Float64Data) [Midhinge](/data.go?s=4107:4168#L116) ``` go func (f Float64Data) Midhinge(d Float64Data) (float64, error) ``` @@ -1087,7 +1775,61 @@ Mode returns the mode of the data -### func (Float64Data) [Pearson](/data.go?s=3455:3515#L96) +### func (Float64Data) [MovingAverage](/rolling.go?s=1768:1833#L58) +``` go +func (f Float64Data) MovingAverage(window int) ([]float64, error) +``` +MovingAverage returns the rolling mean of the data over a trailing window + + + + +### func (Float64Data) [MovingMax](/moving.go?s=3475:3536#L118) +``` go +func (f Float64Data) MovingMax(window int) ([]float64, error) +``` +MovingMax returns the rolling maximum of the data over a trailing window + + + + +### func (Float64Data) [MovingMedian](/moving.go?s=3125:3189#L108) +``` go +func (f Float64Data) MovingMedian(window int) ([]float64, error) +``` +MovingMedian returns the rolling median of the data over a trailing window + + + + +### func (Float64Data) [MovingMin](/moving.go?s=3303:3364#L113) +``` go +func (f Float64Data) MovingMin(window int) ([]float64, error) +``` +MovingMin returns the rolling minimum of the data over a trailing window + + + + +### func (Float64Data) [MovingStdDev](/rolling.go?s=1969:2033#L63) +``` go +func (f Float64Data) MovingStdDev(window int) ([]float64, error) +``` +MovingStdDev returns the rolling sample standard deviation of the data over a trailing window + + + + +### func (Float64Data) [MovingSum](/moving.go?s=3643:3704#L123) +``` go +func (f Float64Data) MovingSum(window int) ([]float64, error) +``` +MovingSum returns the rolling sum of the data over a trailing window + + + + +### func (Float64Data) [Pearson](/data.go?s=3472:3532#L96) ``` go func (f Float64Data) Pearson(d Float64Data) (float64, error) ``` @@ -1096,7 +1838,16 @@ Pearson calculates the Pearson product-moment correlation coefficient between tw -### func (Float64Data) [Percentile](/data.go?s=2696:2755#L76) +### func (Float64Data) [PercentChange](/diff.go?s=1374:1429#L48) +``` go +func (f Float64Data) PercentChange() ([]float64, error) +``` +PercentChange returns the fractional change between successive elements of the data + + + + +### func (Float64Data) [Percentile](/data.go?s=2713:2772#L76) ``` go func (f Float64Data) Percentile(p float64) (float64, error) ``` @@ -1105,7 +1856,7 @@ Percentile finds the relative standing in a slice of floats -### func (Float64Data) [PercentileNearestRank](/data.go?s=2869:2939#L81) +### func (Float64Data) [PercentileNearestRank](/data.go?s=2886:2956#L81) ``` go func (f Float64Data) PercentileNearestRank(p float64) (float64, error) ``` @@ -1114,7 +1865,25 @@ PercentileNearestRank finds the relative standing using the Nearest Rank method -### func (Float64Data) [PopulationVariance](/data.go?s=4495:4553#L131) +### func (Float64Data) [PercentileOfScore](/percentile_of_score.go?s=786:856#L29) +``` go +func (f Float64Data) PercentileOfScore(score float64) (float64, error) +``` +PercentileOfScore calculates the percentile rank of a score relative to the data + + + + +### func (Float64Data) [PopulationKurtosis](/kurtosis.go?s=1655:1713#L63) +``` go +func (f Float64Data) PopulationKurtosis() (float64, error) +``` +PopulationKurtosis finds the population excess kurtosis of a slice of floats + + + + +### func (Float64Data) [PopulationVariance](/data.go?s=4690:4748#L136) ``` go func (f Float64Data) PopulationVariance() (float64, error) ``` @@ -1123,7 +1892,16 @@ PopulationVariance finds the amount of variance within a population -### func (Float64Data) [Quartile](/data.go?s=3610:3673#L101) +### func (Float64Data) [Product](/product.go?s=544:591#L24) +``` go +func (f Float64Data) Product() (float64, error) +``` +Product calculates the product of the data + + + + +### func (Float64Data) [Quartile](/data.go?s=3805:3868#L106) ``` go func (f Float64Data) Quartile(d Float64Data) (Quartiles, error) ``` @@ -1132,7 +1910,7 @@ Quartile returns the three quartile points from a slice of data -### func (Float64Data) [QuartileOutliers](/data.go?s=2542:2599#L71) +### func (Float64Data) [QuartileOutliers](/data.go?s=2559:2616#L71) ``` go func (f Float64Data) QuartileOutliers() (Outliers, error) ``` @@ -1141,7 +1919,7 @@ QuartileOutliers finds the mild and extreme outliers -### func (Float64Data) [Quartiles](/data.go?s=5628:5679#L167) +### func (Float64Data) [Quartiles](/data.go?s=5823:5874#L172) ``` go func (f Float64Data) Quartiles() (Quartiles, error) ``` @@ -1150,7 +1928,53 @@ Quartiles returns the three quartile points from instance of Float64Data -### func (Float64Data) [Sample](/data.go?s=4208:4269#L121) +### func (Float64Data) [RMS](/rms.go?s=454:497#L21) +``` go +func (f Float64Data) RMS() (float64, error) +``` +RMS calculates the root mean square of the data + + + + +### func (Float64Data) [Range](/extremes.go?s=1795:1840#L72) +``` go +func (f Float64Data) Range() (float64, error) +``` +Range returns the difference between the highest and lowest numbers in the data + + + + +### func (Float64Data) [Rank](/rank.go?s=382:428#L14) +``` go +func (f Float64Data) Rank() ([]float64, error) +``` +Rank assigns fractional (average) ranks to the input values + + + + +### func (Float64Data) [Rescale](/rescale.go?s=603:652#L27) +``` go +func (f Float64Data) Rescale() ([]float64, error) +``` +Rescale normalizes the input values to the range of 0 to 1 +by subtracting the minimum and dividing by the range + + + + +### func (Float64Data) [SEM](/sem.go?s=625:668#L22) +``` go +func (f Float64Data) SEM() (float64, error) +``` +SEM calculates the standard error of the mean of the data + + + + +### func (Float64Data) [Sample](/data.go?s=4403:4464#L126) ``` go func (f Float64Data) Sample(n int, r bool) ([]float64, error) ``` @@ -1159,7 +1983,16 @@ Sample returns sample from input with replacement or without -### func (Float64Data) [SampleVariance](/data.go?s=4652:4706#L136) +### func (Float64Data) [SampleKurtosis](/kurtosis.go?s=1836:1890#L68) +``` go +func (f Float64Data) SampleKurtosis() (float64, error) +``` +SampleKurtosis finds the bias-corrected sample excess kurtosis of a slice of floats + + + + +### func (Float64Data) [SampleVariance](/data.go?s=4847:4901#L141) ``` go func (f Float64Data) SampleVariance() (float64, error) ``` @@ -1168,7 +2001,7 @@ SampleVariance finds the amount of variance within a sample -### func (Float64Data) [Sigmoid](/data.go?s=5169:5218#L151) +### func (Float64Data) [Sigmoid](/data.go?s=5364:5413#L156) ``` go func (f Float64Data) Sigmoid() ([]float64, error) ``` @@ -1177,7 +2010,7 @@ Sigmoid returns the input values along the sigmoid or s-shaped curve -### func (Float64Data) [SoftMax](/data.go?s=5359:5408#L157) +### func (Float64Data) [SoftMax](/data.go?s=5554:5603#L162) ``` go func (f Float64Data) SoftMax() ([]float64, error) ``` @@ -1187,7 +2020,16 @@ with sum of all the probabilities being equal to one. -### func (Float64Data) [StandardDeviation](/data.go?s=2026:2083#L56) +### func (Float64Data) [Spearman](/data.go?s=3648:3709#L101) +``` go +func (f Float64Data) Spearman(d Float64Data) (float64, error) +``` +Spearman calculates the Spearman rank correlation coefficient between two variables. + + + + +### func (Float64Data) [StandardDeviation](/data.go?s=2043:2100#L56) ``` go func (f Float64Data) StandardDeviation() (float64, error) ``` @@ -1196,7 +2038,7 @@ StandardDeviation the amount of variation in the dataset -### func (Float64Data) [StandardDeviationPopulation](/data.go?s=2199:2266#L61) +### func (Float64Data) [StandardDeviationPopulation](/data.go?s=2216:2283#L61) ``` go func (f Float64Data) StandardDeviationPopulation() (float64, error) ``` @@ -1205,7 +2047,7 @@ StandardDeviationPopulation finds the amount of variation from the population -### func (Float64Data) [StandardDeviationSample](/data.go?s=2382:2445#L66) +### func (Float64Data) [StandardDeviationSample](/data.go?s=2399:2462#L66) ``` go func (f Float64Data) StandardDeviationSample() (float64, error) ``` @@ -1232,7 +2074,7 @@ Swap switches out two numbers in slice -### func (Float64Data) [Trimean](/data.go?s=4059:4119#L116) +### func (Float64Data) [Trimean](/data.go?s=4254:4314#L121) ``` go func (f Float64Data) Trimean(d Float64Data) (float64, error) ``` @@ -1241,7 +2083,17 @@ Trimean finds the average of the median and the midhinge -### func (Float64Data) [Variance](/data.go?s=4350:4398#L126) +### func (Float64Data) [TrimmedMean](/trimmed_mean.go?s=1132:1198#L36) +``` go +func (f Float64Data) TrimmedMean(percent float64) (float64, error) +``` +TrimmedMean finds the mean of the data after removing a fraction of +the smallest and largest values from each tail + + + + +### func (Float64Data) [Variance](/data.go?s=4545:4593#L131) ``` go func (f Float64Data) Variance() (float64, error) ``` @@ -1250,6 +2102,35 @@ Variance the amount of variation in the dataset +### func (Float64Data) [WeightedMean](/weighted_mean.go?s=976:1047#L40) +``` go +func (f Float64Data) WeightedMean(weights Float64Data) (float64, error) +``` +WeightedMean finds the weighted mean of the data using the given weights + + + + +### func (Float64Data) [Winsorize](/winsorize.go?s=1319:1385#L48) +``` go +func (f Float64Data) Winsorize(percent float64) ([]float64, error) +``` +Winsorize returns a copy of the data with a fraction of the smallest +and largest values in each tail clamped + + + + +### func (Float64Data) [ZScore](/zscore.go?s=632:680#L27) +``` go +func (f Float64Data) ZScore() ([]float64, error) +``` +ZScore standardizes the input values by subtracting the mean +and dividing by the sample standard deviation + + + + ## type [Outliers](/outlier.go?s=73:139#L4) ``` go type Outliers struct { @@ -1315,7 +2196,7 @@ Series is a container for a series of data -### func [ExponentialRegression](/regression.go?s=1089:1157#L50) +### func [ExponentialRegression](/regression.go?s=1061:1129#L49) ``` go func ExponentialRegression(s Series) (regressions Series, err error) ``` @@ -1329,7 +2210,7 @@ func LinearRegression(s Series) (regressions Series, err error) LinearRegression finds the least squares linear regression on data series -### func [LogarithmicRegression](/regression.go?s=1903:1971#L85) +### func [LogarithmicRegression](/regression.go?s=1875:1943#L84) ``` go func LogarithmicRegression(s Series) (regressions Series, err error) ``` diff --git a/vendor/github.com/montanaflynn/stats/Makefile b/vendor/github.com/montanaflynn/stats/Makefile index 969df12..dfa95a6 100644 --- a/vendor/github.com/montanaflynn/stats/Makefile +++ b/vendor/github.com/montanaflynn/stats/Makefile @@ -24,11 +24,10 @@ docs: godoc2md github.com/montanaflynn/stats | sed -e s#src/target/##g > DOCUMENTATION.md release: - git-chglog --output CHANGELOG.md --next-tag ${TAG} + @test -n "${TAG}" || { echo "TAG is required, e.g. make release TAG=v0.10.0"; exit 1; } + sh scripts/update-changelog.sh ${TAG} git add CHANGELOG.md - git commit -m "Update changelog with ${TAG} changes" - git tag ${TAG} - git-chglog $(TAG) | tail -n +4 | gsed '1s/^/$(TAG)\n/gm' > release-notes.txt + git commit -m "chore: update changelog for ${TAG}" + git tag -a ${TAG} -m "${TAG}" git push origin master ${TAG} - hub release create --copy -F release-notes.txt ${TAG} diff --git a/vendor/github.com/montanaflynn/stats/README.md b/vendor/github.com/montanaflynn/stats/README.md index 1cd4895..60e479c 100644 --- a/vendor/github.com/montanaflynn/stats/README.md +++ b/vendor/github.com/montanaflynn/stats/README.md @@ -1,6 +1,6 @@ # Stats - Golang Statistics Package -[![][action-svg]][action-url] [![][codecov-svg]][codecov-url] [![][goreport-svg]][goreport-url] [![][godoc-svg]][godoc-url] [![][pkggodev-svg]][pkggodev-url] [![][license-svg]][license-url] +[![][action-svg]][action-url] [![][codecov-svg]][codecov-url] [![][pkggodev-svg]][pkggodev-url] [![][license-svg]][license-url] A well tested and comprehensive Golang statistics library / package / module with no dependencies. @@ -14,7 +14,7 @@ go get github.com/montanaflynn/stats ## Example Usage -All the functions can be seen in [examples/main.go](examples/main.go) but here's a little taste: +All the functions can be seen in [examples/functions/main.go](examples/functions/main.go) but here's a little taste: ```go // start with some source data to use @@ -34,20 +34,19 @@ fmt.Println(roundedMedian) // 4 ## Documentation -The entire API documentation is available on [GoDoc.org](http://godoc.org/github.com/montanaflynn/stats) or [pkg.go.dev](https://pkg.go.dev/github.com/montanaflynn/stats). +The entire API documentation is available on [pkg.go.dev](https://pkg.go.dev/github.com/montanaflynn/stats). You can also view docs offline with the following commands: ``` # Command line -godoc . # show all exported apis -godoc . Median # show a single function -godoc -ex . Round # show function with example -godoc . Float64Data # show the type and methods - -# Local website -godoc -http=:4444 # start the godoc server on port 4444 -open http://localhost:4444/pkg/github.com/montanaflynn/stats/ +go doc -all . # show all exported apis +go doc Median # show a single function +go doc Float64Data # show the type and methods + +# Local website (go install golang.org/x/pkgsite/cmd/pkgsite@latest) +pkgsite -http=:4444 # start the pkgsite server on port 4444 +open http://localhost:4444/github.com/montanaflynn/stats ``` The exported API is as follows: @@ -70,19 +69,32 @@ type Float64Data []float64 func LoadRawData(raw interface{}) (f Float64Data) {} +func ArgMax(input Float64Data) (int, error) {} +func ArgMin(input Float64Data) (int, error) {} func AutoCorrelation(data Float64Data, lags int) (float64, error) {} func ChebyshevDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {} +func Clip(input Float64Data, min, max float64) ([]float64, error) {} +func CoefficientOfVariation(input Float64Data) (float64, error) {} func Correlation(data1, data2 Float64Data) (float64, error) {} func Covariance(data1, data2 Float64Data) (float64, error) {} func CovariancePopulation(data1, data2 Float64Data) (float64, error) {} +func CumulativeMax(input Float64Data) ([]float64, error) {} +func CumulativeMin(input Float64Data) ([]float64, error) {} +func CumulativeProduct(input Float64Data) ([]float64, error) {} func CumulativeSum(input Float64Data) ([]float64, error) {} func Describe(input Float64Data, allowNaN bool, percentiles *[]float64) (*Description, error) {} func DescribePercentileFunc(input Float64Data, allowNaN bool, percentiles *[]float64, percentileFunc func(Float64Data, float64) (float64, error)) (*Description, error) {} +func Diff(input Float64Data) ([]float64, error) {} +func EWMA(input Float64Data, alpha float64) ([]float64, error) {} func Entropy(input Float64Data) (float64, error) {} func EuclideanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {} func GeometricMean(input Float64Data) (float64, error) {} func HarmonicMean(input Float64Data) (float64, error) {} +func Histogram(input Float64Data, bins int) ([]int, []float64, error) {} func InterQuartileRange(input Float64Data) (float64, error) {} +func Interp(x, xp, fp Float64Data) ([]float64, error) {} +func KendallTau(data1, data2 Float64Data) (float64, error) {} +func Kurtosis(input Float64Data) (float64, error) {} func ManhattanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {} func Max(input Float64Data) (max float64, err error) {} func Mean(input Float64Data) (float64, error) {} @@ -93,6 +105,12 @@ func Midhinge(input Float64Data) (float64, error) {} func Min(input Float64Data) (min float64, err error) {} func MinkowskiDistance(dataPointX, dataPointY Float64Data, lambda float64) (distance float64, err error) {} func Mode(input Float64Data) (mode []float64, err error) {} +func MovingAverage(input Float64Data, window int) ([]float64, error) {} +func MovingMax(input Float64Data, window int) ([]float64, error) {} +func MovingMedian(input Float64Data, window int) ([]float64, error) {} +func MovingMin(input Float64Data, window int) ([]float64, error) {} +func MovingStdDev(input Float64Data, window int) ([]float64, error) {} +func MovingSum(input Float64Data, window int) ([]float64, error) {} func NormBoxMullerRvs(loc float64, scale float64, size int) []float64 {} func NormCdf(x float64, loc float64, scale float64) float64 {} func NormEntropy(loc float64, scale float64) float64 {} @@ -107,20 +125,33 @@ func NormMedian(loc float64, scale float64) float64 {} func NormMoment(n int, loc float64, scale float64) float64 {} func NormPdf(x float64, loc float64, scale float64) float64 {} func NormPpf(p float64, loc float64, scale float64) (x float64) {} +func NormSample(loc float64, scale float64, size int) []float64 {} func NormPpfRvs(loc float64, scale float64, size int) []float64 {} func NormSf(x float64, loc float64, scale float64) float64 {} func NormStats(loc float64, scale float64, moments string) []float64 {} func NormStd(loc float64, scale float64) float64 {} func NormVar(loc float64, scale float64) float64 {} func Pearson(data1, data2 Float64Data) (float64, error) {} +func PercentChange(input Float64Data) ([]float64, error) {} func Percentile(input Float64Data, percent float64) (percentile float64, err error) {} func PercentileNearestRank(input Float64Data, percent float64) (percentile float64, err error) {} +func PercentileOfScore(input Float64Data, score float64) (float64, error) {} +func PercentileWeighted(data, weights Float64Data, percent float64) (percentile float64, err error) {} +func PopulationKurtosis(input Float64Data) (float64, error) {} func PopulationSkewness(input Float64Data) (float64, error) {} func PopulationVariance(input Float64Data) (pvar float64, err error) {} +func Product(input Float64Data) (float64, error) {} +func RMS(input Float64Data) (float64, error) {} +func Range(input Float64Data) (float64, error) {} +func Rank(input Float64Data) ([]float64, error) {} +func Rescale(input Float64Data) ([]float64, error) {} +func SEM(input Float64Data) (float64, error) {} func Sample(input Float64Data, takenum int, replacement bool) ([]float64, error) {} +func SampleKurtosis(input Float64Data) (float64, error) {} func SampleSkewness(input Float64Data) (float64, error) {} func SampleVariance(input Float64Data) (svar float64, err error) {} func Skewness(input Float64Data) (float64, error) {} +func Spearman(data1, data2 Float64Data) (float64, error) {} func Sigmoid(input Float64Data) ([]float64, error) {} func SoftMax(input Float64Data) ([]float64, error) {} func StableSample(input Float64Data, takenum int) ([]float64, error) {} @@ -130,10 +161,16 @@ func StandardDeviationSample(input Float64Data) (sdev float64, err error) {} func StdDevP(input Float64Data) (sdev float64, err error) {} func StdDevS(input Float64Data) (sdev float64, err error) {} func Sum(input Float64Data) (sum float64, err error) {} +func TTest(data1, data2 Float64Data, populationMean float64) (t float64, pvalue float64, err error) {} func Trimean(input Float64Data) (float64, error) {} +func TrimmedMean(input Float64Data, percent float64) (float64, error) {} func VarP(input Float64Data) (sdev float64, err error) {} func VarS(input Float64Data) (sdev float64, err error) {} func Variance(input Float64Data) (sdev float64, err error) {} +func WeightedMean(data, weights Float64Data) (float64, error) {} +func Winsorize(input Float64Data, percent float64) ([]float64, error) {} +func ZScore(input Float64Data) ([]float64, error) {} +func ZTest(data1, data2 Float64Data, populationMean, populationStdDev float64) (z float64, pvalue float64, err error) {} func ProbGeom(a int, b int, p float64) (prob float64, err error) {} func ExpGeom(p float64) (exp float64, err error) {} func VarGeom(p float64) (exp float64, err error) {} @@ -178,20 +215,21 @@ Pull request are always welcome no matter how big or small. I've included a [Mak To make things as seamless as possible please also consider the following steps: -- Update `examples/main.go` with a simple example of the new feature +- Update `examples/functions/main.go` with a simple example of the new feature - Update `README.md` documentation section with any new exported API - Keep 100% code coverage (you can check with `make coverage`) - Squash commits into single units of work with `git rebase -i new-feature` ## Releasing -Releases are automated with [GoReleaser](https://goreleaser.com/) via GitHub Actions. To create a new release, push a version tag: +Releases are automated with [GoReleaser](https://goreleaser.com/) via GitHub Actions. To create a new release, run the release target with the next version tag: ``` -git tag v0.x.x -git push origin v0.x.x +make release TAG=v0.x.x ``` +This updates `CHANGELOG.md`, commits it, creates an annotated tag, and pushes the commit and tag to GitHub, where the release workflow publishes the release. + ## MIT License Copyright (c) 2014-2026 Montana Flynn (https://montanaflynn.com) @@ -200,7 +238,7 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORpublicS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. [action-url]: https://github.com/montanaflynn/stats/actions [action-svg]: https://img.shields.io/github/actions/workflow/status/montanaflynn/stats/go.yml @@ -208,12 +246,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI [codecov-url]: https://app.codecov.io/gh/montanaflynn/stats [codecov-svg]: https://img.shields.io/codecov/c/github/montanaflynn/stats?token=wnw8dActnH -[goreport-url]: https://goreportcard.com/report/github.com/montanaflynn/stats -[goreport-svg]: https://goreportcard.com/badge/github.com/montanaflynn/stats - -[godoc-url]: https://godoc.org/github.com/montanaflynn/stats -[godoc-svg]: https://godoc.org/github.com/montanaflynn/stats?status.svg - [pkggodev-url]: https://pkg.go.dev/github.com/montanaflynn/stats [pkggodev-svg]: https://gistcdn.githack.com/montanaflynn/b02f1d78d8c0de8435895d7e7cd0d473/raw/17f2a5a69f1323ecd42c00e0683655da96d9ecc8/badge.svg diff --git a/vendor/github.com/montanaflynn/stats/clip.go b/vendor/github.com/montanaflynn/stats/clip.go new file mode 100644 index 0000000..94b625a --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/clip.go @@ -0,0 +1,32 @@ +package stats + +// Clip clamps each value in the input slice into the +// inclusive range between min and max. +func Clip(input Float64Data, min, max float64) ([]float64, error) { + if input.Len() == 0 { + return nil, ErrEmptyInput + } + + if min > max { + return nil, ErrBounds + } + + c := make([]float64, len(input)) + for i, v := range input { + switch { + case v < min: + c[i] = min + case v > max: + c[i] = max + default: + c[i] = v + } + } + return c, nil +} + +// Clip clamps each value in the input slice into the +// inclusive range between min and max. +func (f Float64Data) Clip(min, max float64) ([]float64, error) { + return Clip(f, min, max) +} diff --git a/vendor/github.com/montanaflynn/stats/coefficient_of_variation.go b/vendor/github.com/montanaflynn/stats/coefficient_of_variation.go new file mode 100644 index 0000000..25789cd --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/coefficient_of_variation.go @@ -0,0 +1,31 @@ +package stats + +import "math" + +// CoefficientOfVariation finds the coefficient of variation of a slice +// of floats, defined as the sample standard deviation divided by the +// mean. This matches the behavior of Python's scipy.stats.variation +// with ddof=1. +// +// The input must not be empty and its mean must not be zero. +func CoefficientOfVariation(input Float64Data) (float64, error) { + if input.Len() == 0 { + return math.NaN(), ErrEmptyInput + } + + // Input is known to be non-empty so the mean and sample + // standard deviation cannot return an error + m, _ := Mean(input) + if m == 0 { + return math.NaN(), ErrZero + } + + sd, _ := StandardDeviationSample(input) + + return sd / m, nil +} + +// CoefficientOfVariation finds the sample standard deviation divided by the mean +func (f Float64Data) CoefficientOfVariation() (float64, error) { + return CoefficientOfVariation(f) +} diff --git a/vendor/github.com/montanaflynn/stats/correlation.go b/vendor/github.com/montanaflynn/stats/correlation.go index 4acab94..1289063 100644 --- a/vendor/github.com/montanaflynn/stats/correlation.go +++ b/vendor/github.com/montanaflynn/stats/correlation.go @@ -2,6 +2,7 @@ package stats import ( "math" + "sort" ) // Correlation describes the degree of relationship between two sets of data @@ -34,27 +35,95 @@ func Pearson(data1, data2 Float64Data) (float64, error) { return Correlation(data1, data2) } +// Spearman calculates the Spearman rank correlation coefficient between two variables. +// It works by ranking the data and then computing the Pearson correlation of the ranks. +// This method handles tied values using fractional (average) ranking. +func Spearman(data1, data2 Float64Data) (float64, error) { + + l1 := data1.Len() + l2 := data2.Len() + + if l1 == 0 || l2 == 0 { + return math.NaN(), EmptyInputErr + } + + if l1 != l2 { + return math.NaN(), SizeErr + } + + ranks1 := rankData(data1) + ranks2 := rankData(data2) + + return Correlation(ranks1, ranks2) +} + +// rankData assigns fractional (average) ranks to the data values. +// Tied values receive the average of the ranks they would have been assigned. +func rankData(data Float64Data) Float64Data { + n := len(data) + + // Create index-value pairs and sort by value + type indexedValue struct { + index int + value float64 + } + + sorted := make([]indexedValue, n) + for i, v := range data { + sorted[i] = indexedValue{i, v} + } + + sort.SliceStable(sorted, func(i, j int) bool { + return sorted[i].value < sorted[j].value + }) + + ranks := make(Float64Data, n) + + // Assign fractional ranks handling ties + for i := 0; i < n; { + j := i + 1 + for j < n && sorted[j].value == sorted[i].value { + j++ + } + + // Average rank for tied values (ranks are 1-based) + avgRank := float64(i+j+1) / 2.0 + for k := i; k < j; k++ { + ranks[sorted[k].index] = avgRank + } + + i = j + } + + return ranks +} + // AutoCorrelation is the correlation of a signal with a delayed copy of itself as a function of delay func AutoCorrelation(data Float64Data, lags int) (float64, error) { if len(data) < 1 { return 0, EmptyInputErr } + if lags < 0 || lags >= len(data) { + return 0, BoundsErr + } + mean, _ := Mean(data) - var result, q float64 + var variance float64 + for _, v := range data { + delta := v - mean + variance += delta * delta + } - for i := 0; i < lags; i++ { - v := (data[0] - mean) * (data[0] - mean) - for i := 1; i < len(data); i++ { - delta0 := data[i-1] - mean - delta1 := data[i] - mean - q += (delta0*delta1 - q) / float64(i+1) - v += (delta1*delta1 - v) / float64(i+1) - } + if variance == 0 { + return 0, nil + } - result = q / v + var covariance float64 + for i := lags; i < len(data); i++ { + covariance += (data[i] - mean) * (data[i-lags] - mean) } - return result, nil + return covariance / variance, nil } diff --git a/vendor/github.com/montanaflynn/stats/cumulative.go b/vendor/github.com/montanaflynn/stats/cumulative.go new file mode 100644 index 0000000..897f827 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/cumulative.go @@ -0,0 +1,76 @@ +package stats + +// CumulativeProduct calculates the cumulative product of the input slice +func CumulativeProduct(input Float64Data) ([]float64, error) { + + if input.Len() == 0 { + return Float64Data{}, ErrEmptyInput + } + + cumProduct := make([]float64, input.Len()) + + for i, val := range input { + if i == 0 { + cumProduct[i] = val + } else { + cumProduct[i] = cumProduct[i-1] * val + } + } + + return cumProduct, nil +} + +// CumulativeMax calculates the cumulative maximum of the input slice +func CumulativeMax(input Float64Data) ([]float64, error) { + + if input.Len() == 0 { + return Float64Data{}, ErrEmptyInput + } + + cumMax := make([]float64, input.Len()) + + for i, val := range input { + if i == 0 || val > cumMax[i-1] { + cumMax[i] = val + } else { + cumMax[i] = cumMax[i-1] + } + } + + return cumMax, nil +} + +// CumulativeMin calculates the cumulative minimum of the input slice +func CumulativeMin(input Float64Data) ([]float64, error) { + + if input.Len() == 0 { + return Float64Data{}, ErrEmptyInput + } + + cumMin := make([]float64, input.Len()) + + for i, val := range input { + if i == 0 || val < cumMin[i-1] { + cumMin[i] = val + } else { + cumMin[i] = cumMin[i-1] + } + } + + return cumMin, nil +} + +// CumulativeProduct calculates the cumulative product of the data +func (f Float64Data) CumulativeProduct() ([]float64, error) { + return CumulativeProduct(f) +} + +// CumulativeMax calculates the cumulative maximum of the data +func (f Float64Data) CumulativeMax() ([]float64, error) { + return CumulativeMax(f) +} + +// CumulativeMin calculates the cumulative minimum of the data +func (f Float64Data) CumulativeMin() ([]float64, error) { + return CumulativeMin(f) +} diff --git a/vendor/github.com/montanaflynn/stats/data.go b/vendor/github.com/montanaflynn/stats/data.go index b86f0d8..1e249b9 100644 --- a/vendor/github.com/montanaflynn/stats/data.go +++ b/vendor/github.com/montanaflynn/stats/data.go @@ -36,10 +36,10 @@ func (f Float64Data) Median() (float64, error) { return Median(f) } // Mode returns the mode of the data func (f Float64Data) Mode() ([]float64, error) { return Mode(f) } -// GeometricMean returns the median of the data +// GeometricMean returns the geometric mean of the data func (f Float64Data) GeometricMean() (float64, error) { return GeometricMean(f) } -// HarmonicMean returns the mode of the data +// HarmonicMean returns the harmonic mean of the data func (f Float64Data) HarmonicMean() (float64, error) { return HarmonicMean(f) } // MedianAbsoluteDeviation the median of the absolute deviations from the dataset median @@ -97,6 +97,11 @@ func (f Float64Data) Pearson(d Float64Data) (float64, error) { return Pearson(f, d) } +// Spearman calculates the Spearman rank correlation coefficient between two variables. +func (f Float64Data) Spearman(d Float64Data) (float64, error) { + return Spearman(f, d) +} + // Quartile returns the three quartile points from a slice of data func (f Float64Data) Quartile(d Float64Data) (Quartiles, error) { return Quartile(d) diff --git a/vendor/github.com/montanaflynn/stats/describe.go b/vendor/github.com/montanaflynn/stats/describe.go index 86b7242..3904ac0 100644 --- a/vendor/github.com/montanaflynn/stats/describe.go +++ b/vendor/github.com/montanaflynn/stats/describe.go @@ -9,6 +9,7 @@ type Description struct { Std float64 Max float64 Min float64 + Range float64 DescriptionPercentiles []descriptionPercentile AllowedNaN bool } @@ -39,6 +40,7 @@ func DescribePercentileFunc(input Float64Data, allowNaN bool, percentiles *[]flo description.Std, _ = StandardDeviation(input) description.Max, _ = Max(input) description.Min, _ = Min(input) + description.Range, _ = Range(input) description.Mean, _ = Mean(input) if percentiles != nil { @@ -60,6 +62,7 @@ Represents the Description instance in a string format with specified number of std 0.82 max 3.00 min 1.00 + range 2.00 25.00% NaN 50.00% 1.50 75.00% 2.50 @@ -73,6 +76,7 @@ func (d *Description) String(decimals int) string { str += fmt.Sprintf("std\t%.*f\n", decimals, d.Std) str += fmt.Sprintf("max\t%.*f\n", decimals, d.Max) str += fmt.Sprintf("min\t%.*f\n", decimals, d.Min) + str += fmt.Sprintf("range\t%.*f\n", decimals, d.Range) for _, percentile := range d.DescriptionPercentiles { str += fmt.Sprintf("%.2f%%\t%.*f\n", percentile.Percentile, decimals, percentile.Value) } diff --git a/vendor/github.com/montanaflynn/stats/diff.go b/vendor/github.com/montanaflynn/stats/diff.go new file mode 100644 index 0000000..7a9bdfc --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/diff.go @@ -0,0 +1,48 @@ +package stats + +// Diff calculates the successive differences of the input slice, +// returning input[i] - input[i-1] for each i in 1..len(input)-1. +// The output has length len(input) - 1; a single-element input +// returns an empty slice. +func Diff(input Float64Data) ([]float64, error) { + + if input.Len() == 0 { + return nil, ErrEmptyInput + } + + diff := make([]float64, input.Len()-1) + + for i := 1; i < input.Len(); i++ { + diff[i-1] = input[i] - input[i-1] + } + + return diff, nil +} + +// PercentChange calculates the fractional change between successive +// elements of the input slice, returning +// (input[i] - input[i-1]) / input[i-1] for each i in 1..len(input)-1. +// The output has length len(input) - 1; a single-element input +// returns an empty slice. A zero denominator follows IEEE 754 +// semantics, yielding +Inf, -Inf, or NaN (for 0/0), matching the +// behavior of pandas pct_change. +func PercentChange(input Float64Data) ([]float64, error) { + + if input.Len() == 0 { + return nil, ErrEmptyInput + } + + change := make([]float64, input.Len()-1) + + for i := 1; i < input.Len(); i++ { + change[i-1] = (input[i] - input[i-1]) / input[i-1] + } + + return change, nil +} + +// Diff returns the successive differences of the data +func (f Float64Data) Diff() ([]float64, error) { return Diff(f) } + +// PercentChange returns the fractional change between successive elements of the data +func (f Float64Data) PercentChange() ([]float64, error) { return PercentChange(f) } diff --git a/vendor/github.com/montanaflynn/stats/entropy.go b/vendor/github.com/montanaflynn/stats/entropy.go index 95263b0..b38b1dd 100644 --- a/vendor/github.com/montanaflynn/stats/entropy.go +++ b/vendor/github.com/montanaflynn/stats/entropy.go @@ -19,13 +19,16 @@ func Entropy(input Float64Data) (float64, error) { return -result, nil } +// normalize divides each value by the sum of all values, +// leaving the input itself unchanged. func normalize(input Float64Data) (Float64Data, error) { sum, err := input.Sum() if err != nil { return Float64Data{}, err } - for i := 0; i < input.Len(); i++ { - input[i] = input[i] / sum + c := copyslice(input) + for i := 0; i < c.Len(); i++ { + c[i] = c[i] / sum } - return input, nil + return c, nil } diff --git a/vendor/github.com/montanaflynn/stats/ewma.go b/vendor/github.com/montanaflynn/stats/ewma.go new file mode 100644 index 0000000..a0b59a5 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/ewma.go @@ -0,0 +1,32 @@ +package stats + +// EWMA calculates the exponentially weighted moving average of the input +// with smoothing factor alpha. The first output equals the first input and +// each subsequent entry is alpha*input[i] + (1-alpha)*output[i-1], so the +// result has the same length as the input. The alpha must satisfy +// 0 < alpha <= 1 or ErrBounds is returned. An empty input returns +// ErrEmptyInput. +func EWMA(input Float64Data, alpha float64) ([]float64, error) { + + if input.Len() == 0 { + return nil, ErrEmptyInput + } + + if alpha <= 0 || alpha > 1 { + return nil, ErrBounds + } + + output := make([]float64, input.Len()) + + output[0] = input[0] + for i := 1; i < input.Len(); i++ { + output[i] = alpha*input[i] + (1-alpha)*output[i-1] + } + + return output, nil +} + +// EWMA returns the exponentially weighted moving average of the data with smoothing factor alpha +func (f Float64Data) EWMA(alpha float64) ([]float64, error) { + return EWMA(f, alpha) +} diff --git a/vendor/github.com/montanaflynn/stats/extremes.go b/vendor/github.com/montanaflynn/stats/extremes.go new file mode 100644 index 0000000..dd31581 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/extremes.go @@ -0,0 +1,72 @@ +package stats + +// ArgMax finds the index of the highest number in a slice, +// returning the first occurrence in the case of ties +func ArgMax(input Float64Data) (int, error) { + + // Return an error if there are no numbers + if input.Len() == 0 { + return -1, ErrEmptyInput + } + + // Track the index of the highest value seen so far + index := 0 + + // Loop and replace with strictly higher values only, + // which keeps the first occurrence on ties + for i := 1; i < input.Len(); i++ { + if input.Get(i) > input.Get(index) { + index = i + } + } + + return index, nil +} + +// ArgMin finds the index of the lowest number in a slice, +// returning the first occurrence in the case of ties +func ArgMin(input Float64Data) (int, error) { + + // Return an error if there are no numbers + if input.Len() == 0 { + return -1, ErrEmptyInput + } + + // Track the index of the lowest value seen so far + index := 0 + + // Loop and replace with strictly lower values only, + // which keeps the first occurrence on ties + for i := 1; i < input.Len(); i++ { + if input.Get(i) < input.Get(index) { + index = i + } + } + + return index, nil +} + +// Range finds the difference between the highest and +// lowest numbers in a slice +func Range(input Float64Data) (float64, error) { + + // Return an error if there are no numbers + max, err := Max(input) + if err != nil { + return max, ErrEmptyInput + } + + // Disregard error, since Max would have already returned it + min, _ := Min(input) + + return max - min, nil +} + +// ArgMax returns the index of the highest number in the data +func (f Float64Data) ArgMax() (int, error) { return ArgMax(f) } + +// ArgMin returns the index of the lowest number in the data +func (f Float64Data) ArgMin() (int, error) { return ArgMin(f) } + +// Range returns the difference between the highest and lowest numbers in the data +func (f Float64Data) Range() (float64, error) { return Range(f) } diff --git a/vendor/github.com/montanaflynn/stats/histogram.go b/vendor/github.com/montanaflynn/stats/histogram.go new file mode 100644 index 0000000..c2ebb8d --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/histogram.go @@ -0,0 +1,57 @@ +package stats + +import "sort" + +// Histogram calculates the histogram of a slice using the given +// number of equal-width bins over [min, max], returning the count +// of values in each bin along with the bins+1 bin edges. Each bin +// is half-open [edges[i], edges[i+1]) except the last, which also +// includes the maximum value. +func Histogram(input Float64Data, bins int) ([]int, []float64, error) { + + if input.Len() == 0 { + return nil, nil, ErrEmptyInput + } + + if bins < 1 { + return nil, nil, ErrBounds + } + + // Disregard errors, since input is not empty + min, _ := Min(input) + max, _ := Max(input) + + // Expand the range by 0.5 on each side like + // numpy when all of the values are equal + if min == max { + min -= 0.5 + max += 0.5 + } + + // Build bins+1 equal-width bin edges from min to max + width := (max - min) / float64(bins) + edges := make([]float64, bins+1) + for i := range edges { + edges[i] = min + width*float64(i) + } + edges[bins] = max + + // Count each value into the last bin whose left + // edge does not exceed it, so the maximum value + // lands in the final closed bin + counts := make([]int, bins) + for _, v := range input { + i := sort.Search(len(edges), func(i int) bool { return edges[i] > v }) - 1 + if i == bins { + i-- + } + counts[i]++ + } + + return counts, edges, nil +} + +// Histogram returns the counts and equal-width bin edges of the data +func (f Float64Data) Histogram(bins int) ([]int, []float64, error) { + return Histogram(f, bins) +} diff --git a/vendor/github.com/montanaflynn/stats/interp.go b/vendor/github.com/montanaflynn/stats/interp.go new file mode 100644 index 0000000..86a4c3a --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/interp.go @@ -0,0 +1,46 @@ +package stats + +import "sort" + +// Interp calculates the one-dimensional piecewise-linear interpolant to a +// function with given discrete data points (xp, fp), evaluated at each x. +// Values of x below xp[0] return fp[0] and values above xp[len(xp)-1] return +// fp[len(xp)-1], so no extrapolation is performed. Unlike numpy's interp, +// which silently returns nonsense for unsorted coordinates, xp must be +// strictly increasing or ErrBounds is returned. An empty x or xp returns +// ErrEmptyInput and xp and fp of different lengths return ErrSize. +func Interp(x, xp, fp Float64Data) ([]float64, error) { + + if x.Len() == 0 || xp.Len() == 0 { + return nil, ErrEmptyInput + } + + if xp.Len() != fp.Len() { + return nil, ErrSize + } + + for i := 1; i < xp.Len(); i++ { + if xp[i] <= xp[i-1] { + return nil, ErrBounds + } + } + + output := make([]float64, x.Len()) + + for i, xv := range x { + switch { + case xv <= xp[0]: + output[i] = fp[0] + case xv >= xp[xp.Len()-1]: + output[i] = fp[fp.Len()-1] + default: + // The first index with xp[j] >= xv, which the clamping + // above guarantees is within [1, len(xp)-1] + j := sort.SearchFloat64s(xp, xv) + t := (xv - xp[j-1]) / (xp[j] - xp[j-1]) + output[i] = fp[j-1] + t*(fp[j]-fp[j-1]) + } + } + + return output, nil +} diff --git a/vendor/github.com/montanaflynn/stats/kendall.go b/vendor/github.com/montanaflynn/stats/kendall.go new file mode 100644 index 0000000..bba7928 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/kendall.go @@ -0,0 +1,57 @@ +package stats + +import "math" + +// KendallTau calculates Kendall's tau-b rank correlation coefficient +// between two variables. Tau-b corrects for ties, matching the values +// produced by SciPy's kendalltau and pandas' corr(method="kendall"). +// Pairs are compared with a simple O(n^2) loop for clarity. +func KendallTau(data1, data2 Float64Data) (float64, error) { + + l1 := data1.Len() + l2 := data2.Len() + + if l1 == 0 || l2 == 0 { + return math.NaN(), ErrEmptyInput + } + + if l1 != l2 { + return math.NaN(), ErrSize + } + + // Count concordant and discordant pairs along with pairs + // tied only in the first or only in the second variable. + var concordant, discordant, tiedX, tiedY float64 + for i := 0; i < l1; i++ { + for j := i + 1; j < l1; j++ { + dx := data1[i] - data1[j] + dy := data2[i] - data2[j] + switch { + case dx == 0 && dy == 0: + // Pairs tied in both variables are excluded + case dx == 0: + tiedX++ + case dy == 0: + tiedY++ + case (dx > 0) == (dy > 0): + concordant++ + default: + discordant++ + } + } + } + + // tau-b = (C - D) / sqrt((C + D + Tx) * (C + D + Ty)) + denominator := math.Sqrt((concordant + discordant + tiedX) * (concordant + discordant + tiedY)) + if denominator == 0 { + return 0, nil + } + + return (concordant - discordant) / denominator, nil +} + +// KendallTau calculates Kendall's tau-b rank correlation coefficient +// between two variables. +func (f Float64Data) KendallTau(d Float64Data) (float64, error) { + return KendallTau(f, d) +} diff --git a/vendor/github.com/montanaflynn/stats/kurtosis.go b/vendor/github.com/montanaflynn/stats/kurtosis.go new file mode 100644 index 0000000..4a83b20 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/kurtosis.go @@ -0,0 +1,70 @@ +package stats + +import "math" + +// Kurtosis computes the population excess kurtosis of the dataset +func Kurtosis(input Float64Data) (float64, error) { + return PopulationKurtosis(input) +} + +// PopulationKurtosis computes the population excess kurtosis (Fisher +// definition) using the fourth central moment normalized by the squared +// variance, so a normal distribution has a kurtosis of zero. +func PopulationKurtosis(input Float64Data) (float64, error) { + if input.Len() < 2 { + return math.NaN(), ErrEmptyInput + } + + mean, _ := Mean(input) + + // Compute sums of squared and fourth-power differences from the mean + var sumOfSquares, sumOfFourths float64 + for _, v := range input { + d := v - mean + d2 := d * d + sumOfSquares += d2 + sumOfFourths += d2 * d2 + } + + if sumOfSquares == 0 { + return math.NaN(), ErrZero + } + + n := float64(input.Len()) + variance := sumOfSquares / n + + return (sumOfFourths/n)/(variance*variance) - 3.0, nil +} + +// SampleKurtosis computes the bias-corrected sample excess kurtosis, +// matching pandas .kurt() and scipy.stats.kurtosis with bias=False. +func SampleKurtosis(input Float64Data) (float64, error) { + n := input.Len() + if n < 4 { + return math.NaN(), ErrEmptyInput + } + + g2, err := PopulationKurtosis(input) + if err != nil { + return math.NaN(), err + } + + // Bias-corrected: G2 = ((n+1)*g2 + 6) * (n-1) / ((n-2)*(n-3)) + nf := float64(n) + return ((nf+1)*g2 + 6) * (nf - 1) / ((nf - 2) * (nf - 3)), nil +} + +// Kurtosis finds the population excess kurtosis of a slice of floats +func (f Float64Data) Kurtosis() (float64, error) { + return Kurtosis(f) +} + +// PopulationKurtosis finds the population excess kurtosis of a slice of floats +func (f Float64Data) PopulationKurtosis() (float64, error) { + return PopulationKurtosis(f) +} + +// SampleKurtosis finds the bias-corrected sample excess kurtosis of a slice of floats +func (f Float64Data) SampleKurtosis() (float64, error) { + return SampleKurtosis(f) +} diff --git a/vendor/github.com/montanaflynn/stats/mean.go b/vendor/github.com/montanaflynn/stats/mean.go index a78d299..de4f6a6 100644 --- a/vendor/github.com/montanaflynn/stats/mean.go +++ b/vendor/github.com/montanaflynn/stats/mean.go @@ -22,18 +22,19 @@ func GeometricMean(input Float64Data) (float64, error) { return math.NaN(), EmptyInputErr } - // Get the product of all the numbers + // Get the sum of all the numbers natural logs and return an + // error for values that cannot be included in geometric mean var p float64 for _, n := range input { - if p == 0 { - p = n - } else { - p *= n + if n < 0 { + return math.NaN(), NegativeErr + } else if n == 0 { + return math.NaN(), ZeroErr } + p += math.Log(n) } - // Calculate the geometric mean - return math.Pow(p, 1/float64(l)), nil + return math.Exp(p / float64(l)), nil } // HarmonicMean gets the harmonic mean for a slice of numbers diff --git a/vendor/github.com/montanaflynn/stats/moving.go b/vendor/github.com/montanaflynn/stats/moving.go new file mode 100644 index 0000000..ce18f88 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/moving.go @@ -0,0 +1,125 @@ +package stats + +// MovingMedian calculates the rolling median of the input over a trailing +// window. Only fully-populated windows produce output, so the result has +// len(input)-window+1 entries and entry i is the median of input[i : i+window]. +// The window must satisfy 1 <= window <= len(input) or ErrBounds is +// returned. An empty input returns ErrEmptyInput. +func MovingMedian(input Float64Data, window int) ([]float64, error) { + + if input.Len() == 0 { + return nil, ErrEmptyInput + } + + if window < 1 || window > input.Len() { + return nil, ErrBounds + } + + output := make([]float64, input.Len()-window+1) + + for i := range output { + // Median cannot fail here since every window is non-empty + median, _ := Median(input[i : i+window]) + output[i] = median + } + + return output, nil +} + +// MovingMin calculates the rolling minimum of the input over a trailing +// window. Only fully-populated windows produce output, so the result has +// len(input)-window+1 entries and entry i is the minimum of +// input[i : i+window]. The window must satisfy 1 <= window <= len(input) or +// ErrBounds is returned. An empty input returns ErrEmptyInput. +func MovingMin(input Float64Data, window int) ([]float64, error) { + + if input.Len() == 0 { + return nil, ErrEmptyInput + } + + if window < 1 || window > input.Len() { + return nil, ErrBounds + } + + output := make([]float64, input.Len()-window+1) + + for i := range output { + // Min cannot fail here since every window is non-empty + min, _ := Min(input[i : i+window]) + output[i] = min + } + + return output, nil +} + +// MovingMax calculates the rolling maximum of the input over a trailing +// window. Only fully-populated windows produce output, so the result has +// len(input)-window+1 entries and entry i is the maximum of +// input[i : i+window]. The window must satisfy 1 <= window <= len(input) or +// ErrBounds is returned. An empty input returns ErrEmptyInput. +func MovingMax(input Float64Data, window int) ([]float64, error) { + + if input.Len() == 0 { + return nil, ErrEmptyInput + } + + if window < 1 || window > input.Len() { + return nil, ErrBounds + } + + output := make([]float64, input.Len()-window+1) + + for i := range output { + // Max cannot fail here since every window is non-empty + max, _ := Max(input[i : i+window]) + output[i] = max + } + + return output, nil +} + +// MovingSum calculates the rolling sum of the input over a trailing +// window. Only fully-populated windows produce output, so the result has +// len(input)-window+1 entries and entry i is the sum of input[i : i+window]. +// The window must satisfy 1 <= window <= len(input) or ErrBounds is +// returned. An empty input returns ErrEmptyInput. +func MovingSum(input Float64Data, window int) ([]float64, error) { + + if input.Len() == 0 { + return nil, ErrEmptyInput + } + + if window < 1 || window > input.Len() { + return nil, ErrBounds + } + + output := make([]float64, input.Len()-window+1) + + for i := range output { + // Sum cannot fail here since every window is non-empty + sum, _ := Sum(input[i : i+window]) + output[i] = sum + } + + return output, nil +} + +// MovingMedian returns the rolling median of the data over a trailing window +func (f Float64Data) MovingMedian(window int) ([]float64, error) { + return MovingMedian(f, window) +} + +// MovingMin returns the rolling minimum of the data over a trailing window +func (f Float64Data) MovingMin(window int) ([]float64, error) { + return MovingMin(f, window) +} + +// MovingMax returns the rolling maximum of the data over a trailing window +func (f Float64Data) MovingMax(window int) ([]float64, error) { + return MovingMax(f, window) +} + +// MovingSum returns the rolling sum of the data over a trailing window +func (f Float64Data) MovingSum(window int) ([]float64, error) { + return MovingSum(f, window) +} diff --git a/vendor/github.com/montanaflynn/stats/norm.go b/vendor/github.com/montanaflynn/stats/norm.go index 4eb8eb8..620c5ac 100644 --- a/vendor/github.com/montanaflynn/stats/norm.go +++ b/vendor/github.com/montanaflynn/stats/norm.go @@ -7,6 +7,12 @@ import ( "time" ) +// NormSample generates random samples from a normal distribution +// with the given mean (loc) and standard deviation (scale). +func NormSample(loc float64, scale float64, size int) []float64 { + return NormBoxMullerRvs(loc, scale, size) +} + // NormPpfRvs generates random variates using the Point Percentile Function. // For more information please visit: https://demonstrations.wolfram.com/TheMethodOfInverseTransforms/ func NormPpfRvs(loc float64, scale float64, size int) []float64 { diff --git a/vendor/github.com/montanaflynn/stats/percentile_of_score.go b/vendor/github.com/montanaflynn/stats/percentile_of_score.go new file mode 100644 index 0000000..a958c95 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/percentile_of_score.go @@ -0,0 +1,31 @@ +package stats + +import "math" + +// PercentileOfScore calculates the percentile rank of a score +// relative to a slice of floats, defined as the percentage of +// values strictly below the score plus half the percentage of +// values equal to the score. The result is between 0 and 100. +// This matches the behavior of Python's +// scipy.stats.percentileofscore with kind="rank". +func PercentileOfScore(input Float64Data, score float64) (float64, error) { + if input.Len() == 0 { + return math.NaN(), ErrEmptyInput + } + + var below, equal float64 + for _, v := range input { + if v < score { + below++ + } else if v == score { + equal++ + } + } + + return 100 * (below + 0.5*equal) / float64(input.Len()), nil +} + +// PercentileOfScore calculates the percentile rank of a score relative to the data +func (f Float64Data) PercentileOfScore(score float64) (float64, error) { + return PercentileOfScore(f, score) +} diff --git a/vendor/github.com/montanaflynn/stats/percentile_weighted.go b/vendor/github.com/montanaflynn/stats/percentile_weighted.go new file mode 100644 index 0000000..beb09a0 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/percentile_weighted.go @@ -0,0 +1,69 @@ +package stats + +import ( + "math" + "sort" +) + +// PercentileWeighted finds the weighted percentile of a slice of floats +// using the weighted empirical CDF (inverse CDF / nearest-rank method). +// +// For a given percent p, it returns the smallest data value x such that +// the cumulative weight of all values <= x is at least p% of the total +// weight. This matches the behavior of Python's statsmodels +// DescrStatsW.quantile. +// +// The data and weights slices must be the same length. Weights must be +// non-negative and at least one weight must be positive. The percent +// parameter must be between 0 and 100 (exclusive). +func PercentileWeighted(data, weights Float64Data, percent float64) (percentile float64, err error) { + l := data.Len() + if l == 0 { + return math.NaN(), ErrEmptyInput + } + + if weights.Len() != l { + return math.NaN(), ErrSize + } + + if percent <= 0 || percent > 100 { + return math.NaN(), ErrBounds + } + + // Build sorted pairs by data value + type pair struct { + value float64 + weight float64 + } + pairs := make([]pair, l) + totalWeight := 0.0 + for i := 0; i < l; i++ { + if weights[i] < 0 { + return math.NaN(), ErrNegative + } + pairs[i] = pair{data[i], weights[i]} + totalWeight += weights[i] + } + + if totalWeight == 0 { + return math.NaN(), ErrBounds + } + + sort.Slice(pairs, func(i, j int) bool { + return pairs[i].value < pairs[j].value + }) + + // Find the smallest value where cumulative weight >= target + target := (percent / 100) * totalWeight + cumWeight := 0.0 + result := pairs[l-1].value + for i := 0; i < l; i++ { + cumWeight += pairs[i].weight + if cumWeight >= target { + result = pairs[i].value + break + } + } + + return result, nil +} diff --git a/vendor/github.com/montanaflynn/stats/product.go b/vendor/github.com/montanaflynn/stats/product.go new file mode 100644 index 0000000..d5ded82 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/product.go @@ -0,0 +1,26 @@ +package stats + +import "math" + +// Product calculates the product of a slice of floats by +// multiplying the values from left to right. It is the scalar +// counterpart of CumulativeProduct. Large inputs can overflow +// to Inf; use GeometricMean for an overflow-safe summary of +// multiplicative data. +func Product(input Float64Data) (float64, error) { + if input.Len() == 0 { + return math.NaN(), ErrEmptyInput + } + + product := 1.0 + for _, v := range input { + product *= v + } + + return product, nil +} + +// Product calculates the product of the data +func (f Float64Data) Product() (float64, error) { + return Product(f) +} diff --git a/vendor/github.com/montanaflynn/stats/rank.go b/vendor/github.com/montanaflynn/stats/rank.go new file mode 100644 index 0000000..0e9f861 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/rank.go @@ -0,0 +1,16 @@ +package stats + +// Rank assigns fractional (average) ranks to the input values. +// Ranks are 1-based and tied values receive the average of the +// ranks they would have been assigned. +func Rank(input Float64Data) ([]float64, error) { + if input.Len() == 0 { + return nil, ErrEmptyInput + } + return rankData(input), nil +} + +// Rank assigns fractional (average) ranks to the input values +func (f Float64Data) Rank() ([]float64, error) { + return Rank(f) +} diff --git a/vendor/github.com/montanaflynn/stats/regression.go b/vendor/github.com/montanaflynn/stats/regression.go index c883cd6..dde5f1c 100644 --- a/vendor/github.com/montanaflynn/stats/regression.go +++ b/vendor/github.com/montanaflynn/stats/regression.go @@ -10,101 +10,130 @@ type Coordinate struct { X, Y float64 } -// LinearRegression finds the least squares linear regression on data series +// LinearRegression finds the least squares linear regression on data series. +// A series without at least two distinct X values returns ErrBounds. func LinearRegression(s Series) (regressions Series, err error) { if len(s) == 0 { return nil, EmptyInputErr } - // Placeholder for the math to be done - var sum [4]float64 - - // Loop over data keeping index in place - i := 0 - for ; i < len(s); i++ { - sum[0] += s[i].X - sum[1] += s[i].Y - sum[2] += s[i].X * s[i].X - sum[3] += s[i].X * s[i].Y + var sumX, sumY float64 + for _, coordinate := range s { + sumX += coordinate.X + sumY += coordinate.Y } - - // Find gradient and intercept - f := float64(i) - gradient := (f*sum[3] - sum[0]*sum[1]) / (f*sum[2] - sum[0]*sum[0]) - intercept := (sum[1] / f) - (gradient * sum[0] / f) + meanX := sumX / float64(len(s)) + meanY := sumY / float64(len(s)) + + var covariance, variance float64 + for _, coordinate := range s { + dx := coordinate.X - meanX + covariance += dx * (coordinate.Y - meanY) + variance += dx * dx + } + if variance == 0 { + return nil, ErrBounds + } + gradient := covariance / variance // Create the new regression series for j := 0; j < len(s); j++ { regressions = append(regressions, Coordinate{ X: s[j].X, - Y: s[j].X*gradient + intercept, + Y: meanY + gradient*(s[j].X-meanX), }) } return regressions, nil } -// ExponentialRegression returns an exponential regression on data series +// ExponentialRegression returns an exponential regression on data series. +// A non-positive Y value returns ErrYCoord, and a series without at least two +// distinct X values returns ErrBounds. func ExponentialRegression(s Series) (regressions Series, err error) { if len(s) == 0 { return nil, EmptyInputErr } - var sum [6]float64 + var sumY, sumDeltaXY, sumYLogY float64 + referenceX := s[0].X for i := 0; i < len(s); i++ { - if s[i].Y < 0 { - return nil, YCoordErr + if s[i].Y <= 0 { + return nil, ErrYCoord } - sum[0] += s[i].X - sum[1] += s[i].Y - sum[2] += s[i].X * s[i].X * s[i].Y - sum[3] += s[i].Y * math.Log(s[i].Y) - sum[4] += s[i].X * s[i].Y * math.Log(s[i].Y) - sum[5] += s[i].X * s[i].Y + sumY += s[i].Y + sumDeltaXY += (s[i].X - referenceX) * s[i].Y + sumYLogY += s[i].Y * math.Log(s[i].Y) } - - denominator := (sum[1]*sum[2] - sum[5]*sum[5]) - a := math.Pow(math.E, (sum[2]*sum[3]-sum[5]*sum[4])/denominator) - b := (sum[1]*sum[4] - sum[5]*sum[3]) / denominator + meanDeltaX := sumDeltaXY / sumY + meanLogY := sumYLogY / sumY + + var covariance, variance float64 + for _, coordinate := range s { + dx := coordinate.X - referenceX - meanDeltaX + covariance += coordinate.Y * dx * (math.Log(coordinate.Y) - meanLogY) + variance += coordinate.Y * dx * dx + } + if variance == 0 { + return nil, ErrBounds + } + b := covariance / variance for j := 0; j < len(s); j++ { regressions = append(regressions, Coordinate{ X: s[j].X, - Y: a * math.Exp(b*s[j].X), + Y: math.Exp(meanLogY + b*(s[j].X-referenceX-meanDeltaX)), }) } return regressions, nil } -// LogarithmicRegression returns an logarithmic regression on data series +// LogarithmicRegression returns a logarithmic regression on data series. +// A non-positive X value or a series without at least two distinct X values +// returns ErrBounds. func LogarithmicRegression(s Series) (regressions Series, err error) { if len(s) == 0 { return nil, EmptyInputErr } + if s[0].X <= 0 { + return nil, ErrBounds + } - var sum [4]float64 + logX := make([]float64, len(s)) + referenceLogX := math.Log(s[0].X) + var sumDeltaLogX, sumY float64 - i := 0 - for ; i < len(s); i++ { - sum[0] += math.Log(s[i].X) - sum[1] += s[i].Y * math.Log(s[i].X) - sum[2] += s[i].Y - sum[3] += math.Pow(math.Log(s[i].X), 2) + for i := 0; i < len(s); i++ { + if s[i].X <= 0 { + return nil, ErrBounds + } + logX[i] = math.Log(s[i].X) + sumDeltaLogX += logX[i] - referenceLogX + sumY += s[i].Y } - - f := float64(i) - a := (f*sum[1] - sum[2]*sum[0]) / (f*sum[3] - sum[0]*sum[0]) - b := (sum[2] - a*sum[0]) / f + meanDeltaLogX := sumDeltaLogX / float64(len(s)) + meanY := sumY / float64(len(s)) + + var covariance, variance float64 + for i, coordinate := range s { + dx := logX[i] - referenceLogX - meanDeltaLogX + covariance += dx * (coordinate.Y - meanY) + variance += dx * dx + } + if variance == 0 { + return nil, ErrBounds + } + a := covariance / variance for j := 0; j < len(s); j++ { regressions = append(regressions, Coordinate{ X: s[j].X, - Y: b + a*math.Log(s[j].X), + Y: meanY + a*(logX[j]-referenceLogX-meanDeltaLogX), }) } diff --git a/vendor/github.com/montanaflynn/stats/rescale.go b/vendor/github.com/montanaflynn/stats/rescale.go new file mode 100644 index 0000000..814fce0 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/rescale.go @@ -0,0 +1,29 @@ +package stats + +// Rescale normalizes the input values to the range of 0 to 1 +// by subtracting the minimum and dividing by the range, +// also known as min-max normalization. +func Rescale(input Float64Data) ([]float64, error) { + if input.Len() == 0 { + return nil, ErrEmptyInput + } + + min, _ := Min(input) + max, _ := Max(input) + + if max == min { + return nil, ErrZero + } + + r := make([]float64, len(input)) + for i, v := range input { + r[i] = (v - min) / (max - min) + } + return r, nil +} + +// Rescale normalizes the input values to the range of 0 to 1 +// by subtracting the minimum and dividing by the range +func (f Float64Data) Rescale() ([]float64, error) { + return Rescale(f) +} diff --git a/vendor/github.com/montanaflynn/stats/rms.go b/vendor/github.com/montanaflynn/stats/rms.go new file mode 100644 index 0000000..7599e3d --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/rms.go @@ -0,0 +1,23 @@ +package stats + +import "math" + +// RMS calculates the root mean square of a slice of floats, +// defined as the square root of the mean of the squared values. +func RMS(input Float64Data) (float64, error) { + if input.Len() == 0 { + return math.NaN(), ErrEmptyInput + } + + var sumSquares float64 + for _, v := range input { + sumSquares += v * v + } + + return math.Sqrt(sumSquares / float64(input.Len())), nil +} + +// RMS calculates the root mean square of the data +func (f Float64Data) RMS() (float64, error) { + return RMS(f) +} diff --git a/vendor/github.com/montanaflynn/stats/rolling.go b/vendor/github.com/montanaflynn/stats/rolling.go new file mode 100644 index 0000000..571eb59 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/rolling.go @@ -0,0 +1,65 @@ +package stats + +// MovingAverage calculates the rolling mean of the input over a trailing +// window. Only fully-populated windows produce output, so the result has +// len(input)-window+1 entries and entry i is the mean of input[i : i+window]. +// The window must satisfy 1 <= window <= len(input) or ErrBounds is +// returned. An empty input returns ErrEmptyInput. +func MovingAverage(input Float64Data, window int) ([]float64, error) { + + if input.Len() == 0 { + return nil, ErrEmptyInput + } + + if window < 1 || window > input.Len() { + return nil, ErrBounds + } + + output := make([]float64, input.Len()-window+1) + + for i := range output { + // Mean cannot fail here since every window is non-empty + mean, _ := Mean(input[i : i+window]) + output[i] = mean + } + + return output, nil +} + +// MovingStdDev calculates the rolling sample standard deviation of the input +// over a trailing window. Only fully-populated windows produce output, so the +// result has len(input)-window+1 entries and entry i is the sample standard +// deviation of input[i : i+window]. The window must satisfy +// 2 <= window <= len(input) or ErrBounds is returned, since the sample +// standard deviation of a single value is undefined. An empty input returns +// ErrEmptyInput. +func MovingStdDev(input Float64Data, window int) ([]float64, error) { + + if input.Len() == 0 { + return nil, ErrEmptyInput + } + + if window < 2 || window > input.Len() { + return nil, ErrBounds + } + + output := make([]float64, input.Len()-window+1) + + for i := range output { + // StandardDeviationSample cannot fail here since every window is non-empty + sdev, _ := StandardDeviationSample(input[i : i+window]) + output[i] = sdev + } + + return output, nil +} + +// MovingAverage returns the rolling mean of the data over a trailing window +func (f Float64Data) MovingAverage(window int) ([]float64, error) { + return MovingAverage(f, window) +} + +// MovingStdDev returns the rolling sample standard deviation of the data over a trailing window +func (f Float64Data) MovingStdDev(window int) ([]float64, error) { + return MovingStdDev(f, window) +} diff --git a/vendor/github.com/montanaflynn/stats/round.go b/vendor/github.com/montanaflynn/stats/round.go index b66779c..7e122a6 100644 --- a/vendor/github.com/montanaflynn/stats/round.go +++ b/vendor/github.com/montanaflynn/stats/round.go @@ -4,35 +4,9 @@ import "math" // Round a float to a specific decimal place or precision func Round(input float64, places int) (rounded float64, err error) { - - // If the float is not a number if math.IsNaN(input) { return math.NaN(), NaNErr } - - // Find out the actual sign and correct the input for later - sign := 1.0 - if input < 0 { - sign = -1 - input *= -1 - } - - // Use the places arg to get the amount of precision wanted precision := math.Pow(10, float64(places)) - - // Find the decimal place we are looking to round - digit := input * precision - - // Get the actual decimal number as a fraction to be compared - _, decimal := math.Modf(digit) - - // If the decimal is less than .5 we round down otherwise up - if decimal >= 0.5 { - rounded = math.Ceil(digit) - } else { - rounded = math.Floor(digit) - } - - // Finally we do the math to actually create a rounded number - return rounded / precision * sign, nil + return math.Round(input*precision) / precision, nil } diff --git a/vendor/github.com/montanaflynn/stats/sem.go b/vendor/github.com/montanaflynn/stats/sem.go new file mode 100644 index 0000000..111b46e --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/sem.go @@ -0,0 +1,24 @@ +package stats + +import "math" + +// SEM calculates the standard error of the mean of a slice +// of floats, defined as the sample standard deviation divided +// by the square root of the sample size. This matches the +// behavior of Python's scipy.stats.sem with ddof=1. +func SEM(input Float64Data) (float64, error) { + if input.Len() == 0 { + return math.NaN(), ErrEmptyInput + } + + // Input is known to be non-empty so the sample standard + // deviation cannot return an error + sd, _ := StandardDeviationSample(input) + + return sd / math.Sqrt(float64(input.Len())), nil +} + +// SEM calculates the standard error of the mean of the data +func (f Float64Data) SEM() (float64, error) { + return SEM(f) +} diff --git a/vendor/github.com/montanaflynn/stats/trimmed_mean.go b/vendor/github.com/montanaflynn/stats/trimmed_mean.go new file mode 100644 index 0000000..0a99595 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/trimmed_mean.go @@ -0,0 +1,38 @@ +package stats + +import "math" + +// TrimmedMean finds the mean of a slice of floats after removing a +// fraction of the smallest and largest values. This matches the +// behavior of Python's scipy.stats.trim_mean. +// +// The percent parameter is the fraction removed from each tail and +// must be in the range [0, 0.5). The number of elements trimmed from +// each tail is floor(len(input) * percent). A percent of zero returns +// the same result as Mean. +func TrimmedMean(input Float64Data, percent float64) (float64, error) { + l := input.Len() + if l == 0 { + return math.NaN(), ErrEmptyInput + } + + // Reject percents outside [0, 0.5) including NaN. Since percent is + // strictly below 0.5 at least one element always remains after + // trimming floor(l * percent) elements from each tail. + if !(percent >= 0 && percent < 0.5) { + return math.NaN(), ErrBounds + } + + sorted := sortedCopy(input) + + // Number of elements removed from each tail + k := int(math.Floor(float64(l) * percent)) + + return Mean(sorted[k : l-k]) +} + +// TrimmedMean finds the mean of the data after removing a fraction of +// the smallest and largest values from each tail +func (f Float64Data) TrimmedMean(percent float64) (float64, error) { + return TrimmedMean(f, percent) +} diff --git a/vendor/github.com/montanaflynn/stats/ttest.go b/vendor/github.com/montanaflynn/stats/ttest.go new file mode 100644 index 0000000..f36ea32 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/ttest.go @@ -0,0 +1,128 @@ +package stats + +import "math" + +// TTest performs a one-sample or two-sample (independent) Student's t-test. +// +// For a one-sample t-test, pass the sample data as data1, nil for data2, +// and the expected population mean as populationMean. +// +// For a two-sample independent t-test (assuming equal variance), pass both +// sample datasets. The populationMean parameter is ignored in this case. +// +// Returns the t statistic and the two-tailed p-value. +// +// https://en.wikipedia.org/wiki/Student%27s_t-test +func TTest(data1, data2 Float64Data, populationMean float64) (t float64, pvalue float64, err error) { + + n1 := data1.Len() + if n1 == 0 { + return math.NaN(), math.NaN(), ErrEmptyInput + } + + mean1, _ := Mean(data1) + + // Two-sample independent t-test (equal variance) + if data2 != nil && data2.Len() > 0 { + n2 := data2.Len() + + if n1+n2 < 3 { + return math.NaN(), math.NaN(), ErrBounds + } + + mean2, _ := Mean(data2) + var1, _ := SampleVariance(data1) + var2, _ := SampleVariance(data2) + + df := float64(n1 + n2 - 2) + pooledVar := (float64(n1-1)*var1 + float64(n2-1)*var2) / df + se := math.Sqrt(pooledVar * (1.0/float64(n1) + 1.0/float64(n2))) + t = (mean1 - mean2) / se + pvalue = 2 * tSf(math.Abs(t), df) + } else { + // One-sample t-test + if n1 < 2 { + return math.NaN(), math.NaN(), ErrBounds + } + + sd, _ := StandardDeviationSample(data1) + if sd == 0 { + if mean1 == populationMean { + return 0, 1.0, nil + } + return math.NaN(), math.NaN(), ErrBounds + } + se := sd / math.Sqrt(float64(n1)) + t = (mean1 - populationMean) / se + df := float64(n1 - 1) + pvalue = 2 * tSf(math.Abs(t), df) + } + + return t, pvalue, nil +} + +// tSf is the survival function for Student's t-distribution. +// It computes 1 - CDF(t, df) using the regularized incomplete beta function. +func tSf(t float64, df float64) float64 { + x := df / (df + t*t) + return 0.5 * regIncBeta(df/2.0, 0.5, x) +} + +// regIncBeta computes the regularized incomplete beta function I_x(a, b) +// using a continued fraction approximation (Lentz's algorithm). +func regIncBeta(a, b, x float64) float64 { + if x == 0 || x == 1 { + return x + } + + lbeta := lgammaBeta(a, b) + front := math.Exp(math.Log(x)*a + math.Log(1-x)*b - lbeta) / a + + // Use Lentz's continued fraction algorithm + f := 1.0 + c := 1.0 + d := clampTiny(1.0 - (a+b)*x/(a+1)) + d = 1.0 / d + f = d + + for i := 1; i <= 200; i++ { + m := float64(i) + // Numerator for even step + num := m * (b - m) * x / ((a + 2*m - 1) * (a + 2*m)) + d = clampTiny(1.0 + num*d) + c = clampTiny(1.0 + num/c) + d = 1.0 / d + f *= c * d + + // Numerator for odd step + num = -(a + m) * (a + b + m) * x / ((a + 2*m) * (a + 2*m + 1)) + d = clampTiny(1.0 + num*d) + c = clampTiny(1.0 + num/c) + d = 1.0 / d + delta := c * d + f *= delta + + if math.Abs(delta-1.0) < 1e-10 { + break + } + } + + return front * f +} + +// clampTiny prevents division by zero in Lentz's continued fraction +// algorithm by replacing near-zero values with a small constant. +func clampTiny(v float64) float64 { + if math.Abs(v) < 1e-30 { + return 1e-30 + } + return v +} + +// lgammaBeta computes log(Beta(a, b)) = log(Gamma(a)) + log(Gamma(b)) - log(Gamma(a+b)) +func lgammaBeta(a, b float64) float64 { + la, _ := math.Lgamma(a) + lb, _ := math.Lgamma(b) + lab, _ := math.Lgamma(a + b) + return la + lb - lab +} diff --git a/vendor/github.com/montanaflynn/stats/weighted_mean.go b/vendor/github.com/montanaflynn/stats/weighted_mean.go new file mode 100644 index 0000000..b2c20c3 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/weighted_mean.go @@ -0,0 +1,42 @@ +package stats + +import "math" + +// WeightedMean finds the weighted mean of a slice of floats, defined as +// the sum of each data value multiplied by its weight divided by the sum +// of all the weights. This matches the behavior of Python's +// numpy.average with the weights argument. +// +// The data and weights slices must be the same length. Weights must be +// non-negative and at least one weight must be positive. +func WeightedMean(data, weights Float64Data) (float64, error) { + l := data.Len() + if l == 0 { + return math.NaN(), ErrEmptyInput + } + + if weights.Len() != l { + return math.NaN(), ErrSize + } + + weightedSum := 0.0 + totalWeight := 0.0 + for i := 0; i < l; i++ { + if weights[i] < 0 { + return math.NaN(), ErrNegative + } + weightedSum += data[i] * weights[i] + totalWeight += weights[i] + } + + if totalWeight == 0 { + return math.NaN(), ErrZero + } + + return weightedSum / totalWeight, nil +} + +// WeightedMean finds the weighted mean of the data using the given weights +func (f Float64Data) WeightedMean(weights Float64Data) (float64, error) { + return WeightedMean(f, weights) +} diff --git a/vendor/github.com/montanaflynn/stats/winsorize.go b/vendor/github.com/montanaflynn/stats/winsorize.go new file mode 100644 index 0000000..87776be --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/winsorize.go @@ -0,0 +1,50 @@ +package stats + +import "math" + +// Winsorize limits the effect of outliers in a slice of floats by +// clamping a fraction of the smallest and largest values. This matches +// the behavior of Python's scipy.stats.mstats.winsorize with symmetric +// limits. +// +// The percent parameter is the fraction clamped in each tail and must +// be in the range [0, 0.5). With k = floor(len(input) * percent), +// values below the k-th smallest value are set to it and values above +// the k-th largest value are set to it. The returned slice preserves +// the original element order and a percent of zero returns a copy of +// the input. +func Winsorize(input Float64Data, percent float64) ([]float64, error) { + l := input.Len() + if l == 0 { + return nil, ErrEmptyInput + } + + // Reject percents outside [0, 0.5) including NaN + if !(percent >= 0 && percent < 0.5) { + return nil, ErrBounds + } + + sorted := sortedCopy(input) + + // Number of elements clamped in each tail + k := int(math.Floor(float64(l) * percent)) + lower := sorted[k] + upper := sorted[l-1-k] + + output := copyslice(input) + for i, v := range output { + if v < lower { + output[i] = lower + } else if v > upper { + output[i] = upper + } + } + + return output, nil +} + +// Winsorize returns a copy of the data with a fraction of the smallest +// and largest values in each tail clamped +func (f Float64Data) Winsorize(percent float64) ([]float64, error) { + return Winsorize(f, percent) +} diff --git a/vendor/github.com/montanaflynn/stats/zscore.go b/vendor/github.com/montanaflynn/stats/zscore.go new file mode 100644 index 0000000..bc256f3 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/zscore.go @@ -0,0 +1,29 @@ +package stats + +// ZScore standardizes the input values by subtracting the mean +// and dividing by the sample standard deviation, returning the +// number of standard deviations each value is from the mean. +func ZScore(input Float64Data) ([]float64, error) { + if input.Len() == 0 { + return nil, ErrEmptyInput + } + + m, _ := Mean(input) + sd, _ := StandardDeviationSample(input) + + if sd == 0 { + return nil, ErrZero + } + + z := make([]float64, len(input)) + for i, v := range input { + z[i] = (v - m) / sd + } + return z, nil +} + +// ZScore standardizes the input values by subtracting the mean +// and dividing by the sample standard deviation +func (f Float64Data) ZScore() ([]float64, error) { + return ZScore(f) +} diff --git a/vendor/github.com/montanaflynn/stats/ztest.go b/vendor/github.com/montanaflynn/stats/ztest.go new file mode 100644 index 0000000..eb85553 --- /dev/null +++ b/vendor/github.com/montanaflynn/stats/ztest.go @@ -0,0 +1,50 @@ +package stats + +import "math" + +// ZTest performs a one-sample or two-sample Z-test. +// +// For a one-sample Z-test, pass the sample data as data1, nil for data2, +// the known population mean as populationMean, and the known population +// standard deviation as populationStdDev. +// +// For a two-sample Z-test, pass both sample datasets and the known population +// standard deviations. The populationMean parameter is ignored in this case. +// +// Returns the Z statistic and the two-tailed p-value. +// +// https://en.wikipedia.org/wiki/Z-test +func ZTest(data1, data2 Float64Data, populationMean, populationStdDev float64) (z float64, pvalue float64, err error) { + + n1 := data1.Len() + if n1 == 0 { + return math.NaN(), math.NaN(), ErrEmptyInput + } + + mean1, _ := Mean(data1) + + // Two-sample Z-test + if data2 != nil && data2.Len() > 0 { + n2 := data2.Len() + mean2, _ := Mean(data2) + + if populationStdDev <= 0 { + return math.NaN(), math.NaN(), ErrBounds + } + + se := populationStdDev * math.Sqrt(1.0/float64(n1)+1.0/float64(n2)) + z = (mean1 - mean2) / se + } else { + // One-sample Z-test + if populationStdDev <= 0 { + return math.NaN(), math.NaN(), ErrBounds + } + + se := populationStdDev / math.Sqrt(float64(n1)) + z = (mean1 - populationMean) / se + } + + pvalue = 2 * NormSf(math.Abs(z), 0, 1) + + return z, pvalue, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index f93f686..152942a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -55,7 +55,7 @@ github.com/mdlayher/socket # github.com/mdlayher/vsock v1.2.1 ## explicit; go 1.20 github.com/mdlayher/vsock -# github.com/montanaflynn/stats v0.9.0 +# github.com/montanaflynn/stats v0.12.2 ## explicit; go 1.13 github.com/montanaflynn/stats # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822