Browse code

libnetwork/networkdb: test quality of mRandomNodes

TestNetworkDBAlwaysConverges will occasionally find a failure where one
entry is missing on one node even after waiting a full five minutes. One
possible explanation is that the selection of nodes to gossip with is
biased in some way. Test that the mRandomNodes function picks a
uniformly distributed sample of node IDs of sufficient length.

The new test reveals that mRandomNodes may sometimes pick out a sample
of fewer than m nodes even when the number of nodes to pick from
(excluding the local node) is >= m. Put the test behind an xfail tag so
it is opt-in to run, without interfering with CI or bisecting.

Signed-off-by: Cory Snider <csnider@mirantis.com>

Cory Snider authored on 2025/06/25 06:01:30
Showing 40 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,155 @@
0
+//go:build xfail
1
+
2
+package networkdb
3
+
4
+import (
5
+	"maps"
6
+	"math"
7
+	"math/bits"
8
+	"slices"
9
+	"strings"
10
+	"testing"
11
+
12
+	"github.com/montanaflynn/stats"
13
+	"gotest.tools/v3/assert"
14
+	is "gotest.tools/v3/assert/cmp"
15
+	"pgregory.net/rapid"
16
+)
17
+
18
+func TestMRandomNodes(t *testing.T) {
19
+	cfg := DefaultConfig()
20
+	// The easiest way to ensure that we don't accidentally generate node
21
+	// IDs that match the local one is to include runes that the generator
22
+	// will never emit.
23
+	cfg.NodeID = "_thisnode"
24
+	uut := newNetworkDB(cfg)
25
+
26
+	t.Run("EmptySlice", func(t *testing.T) {
27
+		sample := uut.mRandomNodes(3, nil)
28
+		assert.Check(t, is.Len(sample, 0))
29
+	})
30
+
31
+	t.Run("OnlyLocalNode", func(t *testing.T) {
32
+		sample := uut.mRandomNodes(3, []string{cfg.NodeID})
33
+		assert.Check(t, is.Len(sample, 0))
34
+	})
35
+
36
+	gen := rapid.Custom(func(t *rapid.T) []string {
37
+		s := rapid.SliceOfNDistinct(rapid.StringMatching(`[a-z]{10}`), 0, 100, rapid.ID).Draw(t, "node-names")
38
+		insertPoint := rapid.IntRange(0, len(s)).Draw(t, "insertPoint")
39
+		return slices.Insert(s, insertPoint, cfg.NodeID)
40
+	})
41
+
42
+	rapid.Check(t, func(t *rapid.T) {
43
+		nodes := gen.Draw(t, "nodes")
44
+		m := rapid.IntRange(0, len(nodes)).Draw(t, "m")
45
+
46
+		takeSample := func() []string {
47
+			sample := uut.mRandomNodes(m, nodes)
48
+			assert.Check(t, is.Len(sample, min(m, len(nodes)-1)))
49
+			assert.Check(t, is.Equal(slices.Index(sample, cfg.NodeID), -1), "sample contains local node ID\n%v", sample)
50
+			assertUniqueElements(t, sample)
51
+			return sample
52
+		}
53
+
54
+		p := kpermutations(uint64(len(nodes)-1), uint64(m))
55
+		switch {
56
+		case p <= 1:
57
+			// Only one permutation is possible, so cannot test randomness.
58
+			// Assert the other properties by taking a few samples.
59
+			for range 100 {
60
+				_ = takeSample()
61
+			}
62
+			return
63
+		case p <= 10:
64
+			// With a small number of possible k-permutations, we
65
+			// can feasibly test how many samples it takes to get
66
+			// all of them.
67
+			seen := make(map[string]bool)
68
+			var i int
69
+			for i = range 10000 {
70
+				sample := takeSample()
71
+				seen[strings.Join(sample, ",")] = true
72
+				if len(seen) == int(p) {
73
+					break
74
+				}
75
+			}
76
+			assert.Check(t, is.Len(seen, int(p)), "did not see all %d permutations after %d trials", p, i+1)
77
+			t.Logf("saw all %d permutations after %d samples", p, i+1)
78
+		default:
79
+			uniques := 0
80
+			sample1 := takeSample()
81
+			for range 10 {
82
+				sample2 := takeSample()
83
+				if !slices.Equal(sample1, sample2) {
84
+					uniques++
85
+				}
86
+			}
87
+			assert.Check(t, uniques > 0, "mRandomNodes returned the same sample multiple times")
88
+		}
89
+
90
+		// We are testing randomness so statistical outliers are
91
+		// occasionally expected even when the probability
92
+		// distribution is uniform. Run multiple trials to make
93
+		// test flakes unlikely in practice.
94
+		extremes := 0
95
+		for range 10 {
96
+			counts := make(map[string]int)
97
+			for _, n := range nodes {
98
+				if n != cfg.NodeID {
99
+					counts[n] = 0
100
+				}
101
+			}
102
+			const samples = 10000
103
+			for range samples {
104
+				for _, n := range uut.mRandomNodes(m, nodes) {
105
+					counts[n]++
106
+				}
107
+			}
108
+			// Adding multiple samples together should yield a normal distribution
109
+			// if the samples are unbiased.
110
+			countsf := stats.LoadRawData(slices.Collect(maps.Values(counts)))
111
+			nf := stats.NormFit(countsf)
112
+			mean, stdev := nf[0], nf[1]
113
+			minv, _ := countsf.Min()
114
+			maxv, _ := countsf.Max()
115
+			if minv < mean-4*stdev || maxv > mean+4*stdev {
116
+				extremes++
117
+				t.Logf("Mean: %f, StdDev: %f, Min: %f, Max: %f", mean, stdev, minv, maxv)
118
+			}
119
+		}
120
+		assert.Check(t, extremes <= 2, "outliers in distribution: %d/10 trials, expected <2/10", extremes)
121
+	})
122
+}
123
+
124
+func assertUniqueElements[S ~[]E, E comparable](t rapid.TB, s S) {
125
+	t.Helper()
126
+	counts := make(map[E]int)
127
+	for _, e := range s {
128
+		counts[e]++
129
+	}
130
+	for e, c := range counts {
131
+		assert.Equal(t, c, 1, "element %v appears more than once in the slice", e)
132
+	}
133
+}
134
+
135
+// kpermutations returns P(n,k), the number of permutations of k elements chosen
136
+// from a set of size n. The calculation is saturating: if the result is larger than
137
+// can be represented by a uint64, math.MaxUint64 is returned.
138
+func kpermutations(n, k uint64) uint64 {
139
+	if k > n {
140
+		return 0
141
+	}
142
+	if k == 0 || n == 0 {
143
+		return 1
144
+	}
145
+	p := uint64(1)
146
+	for i := range k {
147
+		var hi uint64
148
+		hi, p = bits.Mul64(p, n-i)
149
+		if hi != 0 {
150
+			return math.MaxUint64
151
+		}
152
+	}
153
+	return p
154
+}
... ...
@@ -80,6 +80,7 @@ require (
80 80
 	github.com/moby/sys/user v0.4.0
81 81
 	github.com/moby/sys/userns v0.1.0
82 82
 	github.com/moby/term v0.5.2
83
+	github.com/montanaflynn/stats v0.7.1
83 84
 	github.com/morikuni/aec v1.0.0
84 85
 	github.com/opencontainers/cgroups v0.0.4
85 86
 	github.com/opencontainers/go-digest v1.0.0
... ...
@@ -423,6 +423,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
423 423
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
424 424
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
425 425
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
426
+github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
427
+github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
426 428
 github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
427 429
 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
428 430
 github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
429 431
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+coverage.out
1
+coverage.txt
2
+release-notes.txt
3
+.directory
4
+.chglog
5
+.vscode
6
+.DS_Store
0 7
\ No newline at end of file
1 8
new file mode 100644
... ...
@@ -0,0 +1,534 @@
0
+<a name="unreleased"></a>
1
+## [Unreleased]
2
+
3
+
4
+<a name="v0.7.1"></a>
5
+## [v0.7.1] - 2023-05-11
6
+### Add
7
+- Add describe functions ([#77](https://github.com/montanaflynn/stats/issues/77))
8
+
9
+### Update
10
+- Update .gitignore
11
+- Update README.md, LICENSE and DOCUMENTATION.md files
12
+- Update github action go workflow to run on push
13
+
14
+
15
+<a name="v0.7.0"></a>
16
+## [v0.7.0] - 2023-01-08
17
+### Add
18
+- Add geometric distribution functions ([#75](https://github.com/montanaflynn/stats/issues/75))
19
+- Add GitHub action go workflow
20
+
21
+### Remove
22
+- Remove travis CI config
23
+
24
+### Update
25
+- Update changelog with v0.7.0 changes
26
+- Update changelog with v0.7.0 changes
27
+- Update github action go workflow
28
+- Update geometric distribution tests
29
+
30
+
31
+<a name="v0.6.6"></a>
32
+## [v0.6.6] - 2021-04-26
33
+### Add
34
+- Add support for string and io.Reader in LoadRawData (pr [#68](https://github.com/montanaflynn/stats/issues/68))
35
+- Add latest versions of Go to test against
36
+
37
+### Update
38
+- Update changelog with v0.6.6 changes
39
+
40
+### Use
41
+- Use math.Sqrt in StandardDeviation (PR [#64](https://github.com/montanaflynn/stats/issues/64))
42
+
43
+
44
+<a name="v0.6.5"></a>
45
+## [v0.6.5] - 2021-02-21
46
+### Add
47
+- Add Float64Data.Quartiles documentation
48
+- Add Quartiles method to Float64Data type (issue [#60](https://github.com/montanaflynn/stats/issues/60))
49
+
50
+### Fix
51
+- Fix make release changelog command and add changelog history
52
+
53
+### Update
54
+- Update changelog with v0.6.5 changes
55
+- Update changelog with v0.6.4 changes
56
+- Update README.md links to CHANGELOG.md and DOCUMENTATION.md
57
+- Update README.md and Makefile with new release commands
58
+
59
+
60
+<a name="v0.6.4"></a>
61
+## [v0.6.4] - 2021-01-13
62
+### Fix
63
+- Fix failing tests due to precision errors on arm64 ([#58](https://github.com/montanaflynn/stats/issues/58))
64
+
65
+### Update
66
+- Update changelog with v0.6.4 changes
67
+- Update examples directory to include a README.md used for synopsis
68
+- Update go.mod to include go version where modules are enabled by default
69
+- Update changelog with v0.6.3 changes
70
+
71
+
72
+<a name="v0.6.3"></a>
73
+## [v0.6.3] - 2020-02-18
74
+### Add
75
+- Add creating and committing changelog to Makefile release directive
76
+- Add release-notes.txt and .chglog directory to .gitignore
77
+
78
+### Update
79
+- Update exported tests to use import for better example documentation
80
+- Update documentation using godoc2md
81
+- Update changelog with v0.6.2 release
82
+
83
+
84
+<a name="v0.6.2"></a>
85
+## [v0.6.2] - 2020-02-18
86
+### Fix
87
+- Fix linting errcheck warnings in go benchmarks
88
+
89
+### Update
90
+- Update Makefile release directive to use correct release name
91
+
92
+
93
+<a name="v0.6.1"></a>
94
+## [v0.6.1] - 2020-02-18
95
+### Add
96
+- Add StableSample function signature to readme
97
+
98
+### Fix
99
+- Fix linting warnings for normal distribution functions formatting and tests
100
+
101
+### Update
102
+- Update documentation links and rename DOC.md to DOCUMENTATION.md
103
+- Update README with link to pkg.go.dev reference and release section
104
+- Update Makefile with new changelog, docs, and release directives
105
+- Update DOC.md links to GitHub source code
106
+- Update doc.go comment and add DOC.md package reference file
107
+- Update changelog using git-chglog
108
+
109
+
110
+<a name="v0.6.0"></a>
111
+## [v0.6.0] - 2020-02-17
112
+### Add
113
+- Add Normal Distribution Functions ([#56](https://github.com/montanaflynn/stats/issues/56))
114
+- Add previous versions of Go to travis CI config
115
+- Add check for distinct values in Mode function ([#51](https://github.com/montanaflynn/stats/issues/51))
116
+- Add StableSample function ([#48](https://github.com/montanaflynn/stats/issues/48))
117
+- Add doc.go file to show description and usage on godoc.org
118
+- Add comments to new error and legacy error variables
119
+- Add ExampleRound function to tests
120
+- Add go.mod file for module support
121
+- Add Sigmoid, SoftMax and Entropy methods and tests
122
+- Add Entropy documentation, example and benchmarks
123
+- Add Entropy function ([#44](https://github.com/montanaflynn/stats/issues/44))
124
+
125
+### Fix
126
+- Fix percentile when only one element ([#47](https://github.com/montanaflynn/stats/issues/47))
127
+- Fix AutoCorrelation name in comments and remove unneeded Sprintf
128
+
129
+### Improve
130
+- Improve documentation section with command comments
131
+
132
+### Remove
133
+- Remove very old versions of Go in travis CI config
134
+- Remove boolean comparison to get rid of gometalinter warning
135
+
136
+### Update
137
+- Update license dates
138
+- Update Distance functions signatures to use Float64Data
139
+- Update Sigmoid examples
140
+- Update error names with backward compatibility
141
+
142
+### Use
143
+- Use relative link to examples/main.go
144
+- Use a single var block for exported errors
145
+
146
+
147
+<a name="v0.5.0"></a>
148
+## [v0.5.0] - 2019-01-16
149
+### Add
150
+- Add Sigmoid and Softmax functions
151
+
152
+### Fix
153
+- Fix syntax highlighting and add CumulativeSum func
154
+
155
+
156
+<a name="v0.4.0"></a>
157
+## [v0.4.0] - 2019-01-14
158
+### Add
159
+- Add goreport badge and documentation section to README.md
160
+- Add Examples to test files
161
+- Add AutoCorrelation and nist tests
162
+- Add String method to statsErr type
163
+- Add Y coordinate error for ExponentialRegression
164
+- Add syntax highlighting ([#43](https://github.com/montanaflynn/stats/issues/43))
165
+- Add CumulativeSum ([#40](https://github.com/montanaflynn/stats/issues/40))
166
+- Add more tests and rename distance files
167
+- Add coverage and benchmarks to azure pipeline
168
+- Add go tests to azure pipeline
169
+
170
+### Change
171
+- Change travis tip alias to master
172
+- Change codecov to coveralls for code coverage
173
+
174
+### Fix
175
+- Fix a few lint warnings
176
+- Fix example error
177
+
178
+### Improve
179
+- Improve test coverage of distance functions
180
+
181
+### Only
182
+- Only run travis on stable and tip versions
183
+- Only check code coverage on tip
184
+
185
+### Remove
186
+- Remove azure CI pipeline
187
+- Remove unnecessary type conversions
188
+
189
+### Return
190
+- Return EmptyInputErr instead of EmptyInput
191
+
192
+### Set
193
+- Set up CI with Azure Pipelines
194
+
195
+
196
+<a name="0.3.0"></a>
197
+## [0.3.0] - 2017-12-02
198
+### Add
199
+- Add Chebyshev, Manhattan, Euclidean and Minkowski distance functions ([#35](https://github.com/montanaflynn/stats/issues/35))
200
+- Add function for computing chebyshev distance. ([#34](https://github.com/montanaflynn/stats/issues/34))
201
+- Add support for time.Duration
202
+- Add LoadRawData to docs and examples
203
+- Add unit test for edge case that wasn't covered
204
+- Add unit tests for edge cases that weren't covered
205
+- Add pearson alias delegating to correlation
206
+- Add CovariancePopulation to Float64Data
207
+- Add pearson product-moment correlation coefficient
208
+- Add population covariance
209
+- Add random slice benchmarks
210
+- Add all applicable functions as methods to Float64Data type
211
+- Add MIT license badge
212
+- Add link to examples/methods.go
213
+- Add Protips for usage and documentation sections
214
+- Add tests for rounding up
215
+- Add webdoc target and remove linting from test target
216
+- Add example usage and consolidate contributing information
217
+
218
+### Added
219
+- Added MedianAbsoluteDeviation
220
+
221
+### Annotation
222
+- Annotation spelling error
223
+
224
+### Auto
225
+- auto commit
226
+- auto commit
227
+
228
+### Calculate
229
+- Calculate correlation with sdev and covp
230
+
231
+### Clean
232
+- Clean up README.md and add info for offline docs
233
+
234
+### Consolidated
235
+- Consolidated all error values.
236
+
237
+### Fix
238
+- Fix Percentile logic
239
+- Fix InterQuartileRange method test
240
+- Fix zero percent bug and add test
241
+- Fix usage example output typos
242
+
243
+### Improve
244
+- Improve bounds checking in Percentile
245
+- Improve error log messaging
246
+
247
+### Imput
248
+- Imput -> Input
249
+
250
+### Include
251
+- Include alternative way to set Float64Data in example
252
+
253
+### Make
254
+- Make various changes to README.md
255
+
256
+### Merge
257
+- Merge branch 'master' of github.com:montanaflynn/stats
258
+- Merge master
259
+
260
+### Mode
261
+- Mode calculation fix and tests
262
+
263
+### Realized
264
+- Realized the obvious efficiency gains of ignoring the unique numbers at the beginning of the slice.  Benchmark joy ensued.
265
+
266
+### Refactor
267
+- Refactor testing of Round()
268
+- Refactor setting Coordinate y field using Exp in place of Pow
269
+- Refactor Makefile and add docs target
270
+
271
+### Remove
272
+- Remove deep links to types and functions
273
+
274
+### Rename
275
+- Rename file from types to data
276
+
277
+### Retrieve
278
+- Retrieve InterQuartileRange for the Float64Data.
279
+
280
+### Split
281
+- Split up stats.go into separate files
282
+
283
+### Support
284
+- Support more types on LoadRawData() ([#36](https://github.com/montanaflynn/stats/issues/36))
285
+
286
+### Switch
287
+- Switch default and check targets
288
+
289
+### Update
290
+- Update Readme
291
+- Update example methods and some text
292
+- Update README and include Float64Data type method examples
293
+
294
+### Pull Requests
295
+- Merge pull request [#32](https://github.com/montanaflynn/stats/issues/32) from a-robinson/percentile
296
+- Merge pull request [#30](https://github.com/montanaflynn/stats/issues/30) from montanaflynn/fix-test
297
+- Merge pull request [#29](https://github.com/montanaflynn/stats/issues/29) from edupsousa/master
298
+- Merge pull request [#27](https://github.com/montanaflynn/stats/issues/27) from andrey-yantsen/fix-percentile-out-of-bounds
299
+- Merge pull request [#25](https://github.com/montanaflynn/stats/issues/25) from kazhuravlev/patch-1
300
+- Merge pull request [#22](https://github.com/montanaflynn/stats/issues/22) from JanBerktold/time-duration
301
+- Merge pull request [#24](https://github.com/montanaflynn/stats/issues/24) from alouche/master
302
+- Merge pull request [#21](https://github.com/montanaflynn/stats/issues/21) from brydavis/master
303
+- Merge pull request [#19](https://github.com/montanaflynn/stats/issues/19) from ginodeis/mode-bug
304
+- Merge pull request [#17](https://github.com/montanaflynn/stats/issues/17) from Kunde21/master
305
+- Merge pull request [#3](https://github.com/montanaflynn/stats/issues/3) from montanaflynn/master
306
+- Merge pull request [#2](https://github.com/montanaflynn/stats/issues/2) from montanaflynn/master
307
+- Merge pull request [#13](https://github.com/montanaflynn/stats/issues/13) from toashd/pearson
308
+- Merge pull request [#12](https://github.com/montanaflynn/stats/issues/12) from alixaxel/MAD
309
+- Merge pull request [#1](https://github.com/montanaflynn/stats/issues/1) from montanaflynn/master
310
+- Merge pull request [#11](https://github.com/montanaflynn/stats/issues/11) from Kunde21/modeMemReduce
311
+- Merge pull request [#10](https://github.com/montanaflynn/stats/issues/10) from Kunde21/ModeRewrite
312
+
313
+
314
+<a name="0.2.0"></a>
315
+## [0.2.0] - 2015-10-14
316
+### Add
317
+- Add Makefile with gometalinter, testing, benchmarking and coverage report targets
318
+- Add comments describing functions and structs
319
+- Add Correlation func
320
+- Add Covariance func
321
+- Add tests for new function shortcuts
322
+- Add StandardDeviation function as a shortcut to StandardDeviationPopulation
323
+- Add Float64Data and Series types
324
+
325
+### Change
326
+- Change Sample to return a standard []float64 type
327
+
328
+### Fix
329
+- Fix broken link to Makefile
330
+- Fix broken link and simplify code coverage reporting command
331
+- Fix go vet warning about printf type placeholder
332
+- Fix failing codecov test coverage reporting
333
+- Fix link to CHANGELOG.md
334
+
335
+### Fixed
336
+- Fixed typographical error, changed accomdate to accommodate in README.
337
+
338
+### Include
339
+- Include Variance and StandardDeviation shortcuts
340
+
341
+### Pass
342
+- Pass gometalinter
343
+
344
+### Refactor
345
+- Refactor Variance function to be the same as population variance
346
+
347
+### Release
348
+- Release version 0.2.0
349
+
350
+### Remove
351
+- Remove unneeded do packages and update cover URL
352
+- Remove sudo from pip install
353
+
354
+### Reorder
355
+- Reorder functions and sections
356
+
357
+### Revert
358
+- Revert to legacy containers to preserve go1.1 testing
359
+
360
+### Switch
361
+- Switch from legacy to container-based CI infrastructure
362
+
363
+### Update
364
+- Update contributing instructions and mention Makefile
365
+
366
+### Pull Requests
367
+- Merge pull request [#5](https://github.com/montanaflynn/stats/issues/5) from orthographic-pedant/spell_check/accommodate
368
+
369
+
370
+<a name="0.1.0"></a>
371
+## [0.1.0] - 2015-08-19
372
+### Add
373
+- Add CONTRIBUTING.md
374
+
375
+### Rename
376
+- Rename functions while preserving backwards compatibility
377
+
378
+
379
+<a name="0.0.9"></a>
380
+## 0.0.9 - 2015-08-18
381
+### Add
382
+- Add HarmonicMean func
383
+- Add GeometricMean func
384
+- Add .gitignore to avoid commiting test coverage report
385
+- Add Outliers stuct and QuantileOutliers func
386
+- Add Interquartile Range, Midhinge and Trimean examples
387
+- Add Trimean
388
+- Add Midhinge
389
+- Add Inter Quartile Range
390
+- Add a unit test to check for an empty slice error
391
+- Add Quantiles struct and Quantile func
392
+- Add more tests and fix a typo
393
+- Add Golang 1.5 to build tests
394
+- Add a standard MIT license file
395
+- Add basic benchmarking
396
+- Add regression models
397
+- Add codecov token
398
+- Add codecov
399
+- Add check for slices with a single item
400
+- Add coverage tests
401
+- Add back previous Go versions to Travis CI
402
+- Add Travis CI
403
+- Add GoDoc badge
404
+- Add Percentile and Float64ToInt functions
405
+- Add another rounding test for whole numbers
406
+- Add build status badge
407
+- Add code coverage badge
408
+- Add test for NaN, achieving 100% code coverage
409
+- Add round function
410
+- Add standard deviation function
411
+- Add sum function
412
+
413
+### Add
414
+- add tests for sample
415
+- add sample
416
+
417
+### Added
418
+- Added sample and population variance and deviation functions
419
+- Added README
420
+
421
+### Adjust
422
+- Adjust API ordering
423
+
424
+### Avoid
425
+- Avoid unintended consequence of using sort
426
+
427
+### Better
428
+- Better performing min/max
429
+- Better description
430
+
431
+### Change
432
+- Change package path to potentially fix a bug in earlier versions of Go
433
+
434
+### Clean
435
+- Clean up README and add some more information
436
+- Clean up test error
437
+
438
+### Consistent
439
+- Consistent empty slice error messages
440
+- Consistent var naming
441
+- Consistent func declaration
442
+
443
+### Convert
444
+- Convert ints to floats
445
+
446
+### Duplicate
447
+- Duplicate packages for all versions
448
+
449
+### Export
450
+- Export Coordinate struct fields
451
+
452
+### First
453
+- First commit
454
+
455
+### Fix
456
+- Fix copy pasta mistake testing the wrong function
457
+- Fix error message
458
+- Fix usage output and edit API doc section
459
+- Fix testing edgecase where map was in wrong order
460
+- Fix usage example
461
+- Fix usage examples
462
+
463
+### Include
464
+- Include the Nearest Rank method of calculating percentiles
465
+
466
+### More
467
+- More commenting
468
+
469
+### Move
470
+- Move GoDoc link to top
471
+
472
+### Redirect
473
+- Redirect kills newer versions of Go
474
+
475
+### Refactor
476
+- Refactor code and error checking
477
+
478
+### Remove
479
+- Remove unnecassary typecasting in sum func
480
+- Remove cover since it doesn't work for later versions of go
481
+- Remove golint and gocoveralls
482
+
483
+### Rename
484
+- Rename StandardDev to StdDev
485
+- Rename StandardDev to StdDev
486
+
487
+### Return
488
+- Return errors for all functions
489
+
490
+### Run
491
+- Run go fmt to clean up formatting
492
+
493
+### Simplify
494
+- Simplify min/max function
495
+
496
+### Start
497
+- Start with minimal tests
498
+
499
+### Switch
500
+- Switch wercker to travis and update todos
501
+
502
+### Table
503
+- table testing style
504
+
505
+### Update
506
+- Update README and move the example main.go into it's own file
507
+- Update TODO list
508
+- Update README
509
+- Update usage examples and todos
510
+
511
+### Use
512
+- Use codecov the recommended way
513
+- Use correct string formatting types
514
+
515
+### Pull Requests
516
+- Merge pull request [#4](https://github.com/montanaflynn/stats/issues/4) from saromanov/sample
517
+
518
+
519
+[Unreleased]: https://github.com/montanaflynn/stats/compare/v0.7.1...HEAD
520
+[v0.7.1]: https://github.com/montanaflynn/stats/compare/v0.7.0...v0.7.1
521
+[v0.7.0]: https://github.com/montanaflynn/stats/compare/v0.6.6...v0.7.0
522
+[v0.6.6]: https://github.com/montanaflynn/stats/compare/v0.6.5...v0.6.6
523
+[v0.6.5]: https://github.com/montanaflynn/stats/compare/v0.6.4...v0.6.5
524
+[v0.6.4]: https://github.com/montanaflynn/stats/compare/v0.6.3...v0.6.4
525
+[v0.6.3]: https://github.com/montanaflynn/stats/compare/v0.6.2...v0.6.3
526
+[v0.6.2]: https://github.com/montanaflynn/stats/compare/v0.6.1...v0.6.2
527
+[v0.6.1]: https://github.com/montanaflynn/stats/compare/v0.6.0...v0.6.1
528
+[v0.6.0]: https://github.com/montanaflynn/stats/compare/v0.5.0...v0.6.0
529
+[v0.5.0]: https://github.com/montanaflynn/stats/compare/v0.4.0...v0.5.0
530
+[v0.4.0]: https://github.com/montanaflynn/stats/compare/0.3.0...v0.4.0
531
+[0.3.0]: https://github.com/montanaflynn/stats/compare/0.2.0...0.3.0
532
+[0.2.0]: https://github.com/montanaflynn/stats/compare/0.1.0...0.2.0
533
+[0.1.0]: https://github.com/montanaflynn/stats/compare/0.0.9...0.1.0
0 534
new file mode 100644
... ...
@@ -0,0 +1,1271 @@
0
+
1
+
2
+# stats
3
+`import "github.com/montanaflynn/stats"`
4
+
5
+* [Overview](#pkg-overview)
6
+* [Index](#pkg-index)
7
+* [Examples](#pkg-examples)
8
+* [Subdirectories](#pkg-subdirectories)
9
+
10
+## <a name="pkg-overview">Overview</a>
11
+Package stats is a well tested and comprehensive
12
+statistics library package with no dependencies.
13
+
14
+Example Usage:
15
+
16
+
17
+	// start with some source data to use
18
+	data := []float64{1.0, 2.1, 3.2, 4.823, 4.1, 5.8}
19
+	
20
+	// you could also use different types like this
21
+	// data := stats.LoadRawData([]int{1, 2, 3, 4, 5})
22
+	// data := stats.LoadRawData([]interface{}{1.1, "2", 3})
23
+	// etc...
24
+	
25
+	median, _ := stats.Median(data)
26
+	fmt.Println(median) // 3.65
27
+	
28
+	roundedMedian, _ := stats.Round(median, 0)
29
+	fmt.Println(roundedMedian) // 4
30
+
31
+MIT License Copyright (c) 2014-2020 Montana Flynn (<a href="https://montanaflynn.com">https://montanaflynn.com</a>)
32
+
33
+
34
+
35
+
36
+## <a name="pkg-index">Index</a>
37
+* [Variables](#pkg-variables)
38
+* [func AutoCorrelation(data Float64Data, lags int) (float64, error)](#AutoCorrelation)
39
+* [func ChebyshevDistance(dataPointX, dataPointY Float64Data) (distance float64, err error)](#ChebyshevDistance)
40
+* [func Correlation(data1, data2 Float64Data) (float64, error)](#Correlation)
41
+* [func Covariance(data1, data2 Float64Data) (float64, error)](#Covariance)
42
+* [func CovariancePopulation(data1, data2 Float64Data) (float64, error)](#CovariancePopulation)
43
+* [func CumulativeSum(input Float64Data) ([]float64, error)](#CumulativeSum)
44
+* [func Entropy(input Float64Data) (float64, error)](#Entropy)
45
+* [func EuclideanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error)](#EuclideanDistance)
46
+* [func ExpGeom(p float64) (exp float64, err error)](#ExpGeom)
47
+* [func GeometricMean(input Float64Data) (float64, error)](#GeometricMean)
48
+* [func HarmonicMean(input Float64Data) (float64, error)](#HarmonicMean)
49
+* [func InterQuartileRange(input Float64Data) (float64, error)](#InterQuartileRange)
50
+* [func ManhattanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error)](#ManhattanDistance)
51
+* [func Max(input Float64Data) (max float64, err error)](#Max)
52
+* [func Mean(input Float64Data) (float64, error)](#Mean)
53
+* [func Median(input Float64Data) (median float64, err error)](#Median)
54
+* [func MedianAbsoluteDeviation(input Float64Data) (mad float64, err error)](#MedianAbsoluteDeviation)
55
+* [func MedianAbsoluteDeviationPopulation(input Float64Data) (mad float64, err error)](#MedianAbsoluteDeviationPopulation)
56
+* [func Midhinge(input Float64Data) (float64, error)](#Midhinge)
57
+* [func Min(input Float64Data) (min float64, err error)](#Min)
58
+* [func MinkowskiDistance(dataPointX, dataPointY Float64Data, lambda float64) (distance float64, err error)](#MinkowskiDistance)
59
+* [func Mode(input Float64Data) (mode []float64, err error)](#Mode)
60
+* [func Ncr(n, r int) int](#Ncr)
61
+* [func NormBoxMullerRvs(loc float64, scale float64, size int) []float64](#NormBoxMullerRvs)
62
+* [func NormCdf(x float64, loc float64, scale float64) float64](#NormCdf)
63
+* [func NormEntropy(loc float64, scale float64) float64](#NormEntropy)
64
+* [func NormFit(data []float64) [2]float64](#NormFit)
65
+* [func NormInterval(alpha float64, loc float64, scale float64) [2]float64](#NormInterval)
66
+* [func NormIsf(p float64, loc float64, scale float64) (x float64)](#NormIsf)
67
+* [func NormLogCdf(x float64, loc float64, scale float64) float64](#NormLogCdf)
68
+* [func NormLogPdf(x float64, loc float64, scale float64) float64](#NormLogPdf)
69
+* [func NormLogSf(x float64, loc float64, scale float64) float64](#NormLogSf)
70
+* [func NormMean(loc float64, scale float64) float64](#NormMean)
71
+* [func NormMedian(loc float64, scale float64) float64](#NormMedian)
72
+* [func NormMoment(n int, loc float64, scale float64) float64](#NormMoment)
73
+* [func NormPdf(x float64, loc float64, scale float64) float64](#NormPdf)
74
+* [func NormPpf(p float64, loc float64, scale float64) (x float64)](#NormPpf)
75
+* [func NormPpfRvs(loc float64, scale float64, size int) []float64](#NormPpfRvs)
76
+* [func NormSf(x float64, loc float64, scale float64) float64](#NormSf)
77
+* [func NormStats(loc float64, scale float64, moments string) []float64](#NormStats)
78
+* [func NormStd(loc float64, scale float64) float64](#NormStd)
79
+* [func NormVar(loc float64, scale float64) float64](#NormVar)
80
+* [func Pearson(data1, data2 Float64Data) (float64, error)](#Pearson)
81
+* [func Percentile(input Float64Data, percent float64) (percentile float64, err error)](#Percentile)
82
+* [func PercentileNearestRank(input Float64Data, percent float64) (percentile float64, err error)](#PercentileNearestRank)
83
+* [func PopulationVariance(input Float64Data) (pvar float64, err error)](#PopulationVariance)
84
+* [func ProbGeom(a int, b int, p float64) (prob float64, err error)](#ProbGeom)
85
+* [func Round(input float64, places int) (rounded float64, err error)](#Round)
86
+* [func Sample(input Float64Data, takenum int, replacement bool) ([]float64, error)](#Sample)
87
+* [func SampleVariance(input Float64Data) (svar float64, err error)](#SampleVariance)
88
+* [func Sigmoid(input Float64Data) ([]float64, error)](#Sigmoid)
89
+* [func SoftMax(input Float64Data) ([]float64, error)](#SoftMax)
90
+* [func StableSample(input Float64Data, takenum int) ([]float64, error)](#StableSample)
91
+* [func StandardDeviation(input Float64Data) (sdev float64, err error)](#StandardDeviation)
92
+* [func StandardDeviationPopulation(input Float64Data) (sdev float64, err error)](#StandardDeviationPopulation)
93
+* [func StandardDeviationSample(input Float64Data) (sdev float64, err error)](#StandardDeviationSample)
94
+* [func StdDevP(input Float64Data) (sdev float64, err error)](#StdDevP)
95
+* [func StdDevS(input Float64Data) (sdev float64, err error)](#StdDevS)
96
+* [func Sum(input Float64Data) (sum float64, err error)](#Sum)
97
+* [func Trimean(input Float64Data) (float64, error)](#Trimean)
98
+* [func VarGeom(p float64) (exp float64, err error)](#VarGeom)
99
+* [func VarP(input Float64Data) (sdev float64, err error)](#VarP)
100
+* [func VarS(input Float64Data) (sdev float64, err error)](#VarS)
101
+* [func Variance(input Float64Data) (sdev float64, err error)](#Variance)
102
+* [type Coordinate](#Coordinate)
103
+  * [func ExpReg(s []Coordinate) (regressions []Coordinate, err error)](#ExpReg)
104
+  * [func LinReg(s []Coordinate) (regressions []Coordinate, err error)](#LinReg)
105
+  * [func LogReg(s []Coordinate) (regressions []Coordinate, err error)](#LogReg)
106
+* [type Float64Data](#Float64Data)
107
+  * [func LoadRawData(raw interface{}) (f Float64Data)](#LoadRawData)
108
+  * [func (f Float64Data) AutoCorrelation(lags int) (float64, error)](#Float64Data.AutoCorrelation)
109
+  * [func (f Float64Data) Correlation(d Float64Data) (float64, error)](#Float64Data.Correlation)
110
+  * [func (f Float64Data) Covariance(d Float64Data) (float64, error)](#Float64Data.Covariance)
111
+  * [func (f Float64Data) CovariancePopulation(d Float64Data) (float64, error)](#Float64Data.CovariancePopulation)
112
+  * [func (f Float64Data) CumulativeSum() ([]float64, error)](#Float64Data.CumulativeSum)
113
+  * [func (f Float64Data) Entropy() (float64, error)](#Float64Data.Entropy)
114
+  * [func (f Float64Data) GeometricMean() (float64, error)](#Float64Data.GeometricMean)
115
+  * [func (f Float64Data) Get(i int) float64](#Float64Data.Get)
116
+  * [func (f Float64Data) HarmonicMean() (float64, error)](#Float64Data.HarmonicMean)
117
+  * [func (f Float64Data) InterQuartileRange() (float64, error)](#Float64Data.InterQuartileRange)
118
+  * [func (f Float64Data) Len() int](#Float64Data.Len)
119
+  * [func (f Float64Data) Less(i, j int) bool](#Float64Data.Less)
120
+  * [func (f Float64Data) Max() (float64, error)](#Float64Data.Max)
121
+  * [func (f Float64Data) Mean() (float64, error)](#Float64Data.Mean)
122
+  * [func (f Float64Data) Median() (float64, error)](#Float64Data.Median)
123
+  * [func (f Float64Data) MedianAbsoluteDeviation() (float64, error)](#Float64Data.MedianAbsoluteDeviation)
124
+  * [func (f Float64Data) MedianAbsoluteDeviationPopulation() (float64, error)](#Float64Data.MedianAbsoluteDeviationPopulation)
125
+  * [func (f Float64Data) Midhinge(d Float64Data) (float64, error)](#Float64Data.Midhinge)
126
+  * [func (f Float64Data) Min() (float64, error)](#Float64Data.Min)
127
+  * [func (f Float64Data) Mode() ([]float64, error)](#Float64Data.Mode)
128
+  * [func (f Float64Data) Pearson(d Float64Data) (float64, error)](#Float64Data.Pearson)
129
+  * [func (f Float64Data) Percentile(p float64) (float64, error)](#Float64Data.Percentile)
130
+  * [func (f Float64Data) PercentileNearestRank(p float64) (float64, error)](#Float64Data.PercentileNearestRank)
131
+  * [func (f Float64Data) PopulationVariance() (float64, error)](#Float64Data.PopulationVariance)
132
+  * [func (f Float64Data) Quartile(d Float64Data) (Quartiles, error)](#Float64Data.Quartile)
133
+  * [func (f Float64Data) QuartileOutliers() (Outliers, error)](#Float64Data.QuartileOutliers)
134
+  * [func (f Float64Data) Quartiles() (Quartiles, error)](#Float64Data.Quartiles)
135
+  * [func (f Float64Data) Sample(n int, r bool) ([]float64, error)](#Float64Data.Sample)
136
+  * [func (f Float64Data) SampleVariance() (float64, error)](#Float64Data.SampleVariance)
137
+  * [func (f Float64Data) Sigmoid() ([]float64, error)](#Float64Data.Sigmoid)
138
+  * [func (f Float64Data) SoftMax() ([]float64, error)](#Float64Data.SoftMax)
139
+  * [func (f Float64Data) StandardDeviation() (float64, error)](#Float64Data.StandardDeviation)
140
+  * [func (f Float64Data) StandardDeviationPopulation() (float64, error)](#Float64Data.StandardDeviationPopulation)
141
+  * [func (f Float64Data) StandardDeviationSample() (float64, error)](#Float64Data.StandardDeviationSample)
142
+  * [func (f Float64Data) Sum() (float64, error)](#Float64Data.Sum)
143
+  * [func (f Float64Data) Swap(i, j int)](#Float64Data.Swap)
144
+  * [func (f Float64Data) Trimean(d Float64Data) (float64, error)](#Float64Data.Trimean)
145
+  * [func (f Float64Data) Variance() (float64, error)](#Float64Data.Variance)
146
+* [type Outliers](#Outliers)
147
+  * [func QuartileOutliers(input Float64Data) (Outliers, error)](#QuartileOutliers)
148
+* [type Quartiles](#Quartiles)
149
+  * [func Quartile(input Float64Data) (Quartiles, error)](#Quartile)
150
+* [type Series](#Series)
151
+  * [func ExponentialRegression(s Series) (regressions Series, err error)](#ExponentialRegression)
152
+  * [func LinearRegression(s Series) (regressions Series, err error)](#LinearRegression)
153
+  * [func LogarithmicRegression(s Series) (regressions Series, err error)](#LogarithmicRegression)
154
+
155
+#### <a name="pkg-examples">Examples</a>
156
+* [AutoCorrelation](#example_AutoCorrelation)
157
+* [ChebyshevDistance](#example_ChebyshevDistance)
158
+* [Correlation](#example_Correlation)
159
+* [CumulativeSum](#example_CumulativeSum)
160
+* [Entropy](#example_Entropy)
161
+* [ExpGeom](#example_ExpGeom)
162
+* [LinearRegression](#example_LinearRegression)
163
+* [LoadRawData](#example_LoadRawData)
164
+* [Max](#example_Max)
165
+* [Median](#example_Median)
166
+* [Min](#example_Min)
167
+* [ProbGeom](#example_ProbGeom)
168
+* [Round](#example_Round)
169
+* [Sigmoid](#example_Sigmoid)
170
+* [SoftMax](#example_SoftMax)
171
+* [Sum](#example_Sum)
172
+* [VarGeom](#example_VarGeom)
173
+
174
+#### <a name="pkg-files">Package files</a>
175
+[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) [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) 
176
+
177
+
178
+
179
+## <a name="pkg-variables">Variables</a>
180
+``` go
181
+var (
182
+    // ErrEmptyInput Input must not be empty
183
+    ErrEmptyInput = statsError{"Input must not be empty."}
184
+    // ErrNaN Not a number
185
+    ErrNaN = statsError{"Not a number."}
186
+    // ErrNegative Must not contain negative values
187
+    ErrNegative = statsError{"Must not contain negative values."}
188
+    // ErrZero Must not contain zero values
189
+    ErrZero = statsError{"Must not contain zero values."}
190
+    // ErrBounds Input is outside of range
191
+    ErrBounds = statsError{"Input is outside of range."}
192
+    // ErrSize Must be the same length
193
+    ErrSize = statsError{"Must be the same length."}
194
+    // ErrInfValue Value is infinite
195
+    ErrInfValue = statsError{"Value is infinite."}
196
+    // ErrYCoord Y Value must be greater than zero
197
+    ErrYCoord = statsError{"Y Value must be greater than zero."}
198
+)
199
+```
200
+These are the package-wide error values.
201
+All error identification should use these values.
202
+<a href="https://github.com/golang/go/wiki/Errors#naming">https://github.com/golang/go/wiki/Errors#naming</a>
203
+
204
+``` go
205
+var (
206
+    EmptyInputErr = ErrEmptyInput
207
+    NaNErr        = ErrNaN
208
+    NegativeErr   = ErrNegative
209
+    ZeroErr       = ErrZero
210
+    BoundsErr     = ErrBounds
211
+    SizeErr       = ErrSize
212
+    InfValue      = ErrInfValue
213
+    YCoordErr     = ErrYCoord
214
+    EmptyInput    = ErrEmptyInput
215
+)
216
+```
217
+Legacy error names that didn't start with Err
218
+
219
+
220
+
221
+## <a name="AutoCorrelation">func</a> [AutoCorrelation](/correlation.go?s=853:918#L38)
222
+``` go
223
+func AutoCorrelation(data Float64Data, lags int) (float64, error)
224
+```
225
+AutoCorrelation is the correlation of a signal with a delayed copy of itself as a function of delay
226
+
227
+
228
+
229
+## <a name="ChebyshevDistance">func</a> [ChebyshevDistance](/distances.go?s=368:456#L20)
230
+``` go
231
+func ChebyshevDistance(dataPointX, dataPointY Float64Data) (distance float64, err error)
232
+```
233
+ChebyshevDistance computes the Chebyshev distance between two data sets
234
+
235
+
236
+
237
+## <a name="Correlation">func</a> [Correlation](/correlation.go?s=112:171#L8)
238
+``` go
239
+func Correlation(data1, data2 Float64Data) (float64, error)
240
+```
241
+Correlation describes the degree of relationship between two sets of data
242
+
243
+
244
+
245
+## <a name="Covariance">func</a> [Covariance](/variance.go?s=1284:1342#L53)
246
+``` go
247
+func Covariance(data1, data2 Float64Data) (float64, error)
248
+```
249
+Covariance is a measure of how much two sets of data change
250
+
251
+
252
+
253
+## <a name="CovariancePopulation">func</a> [CovariancePopulation](/variance.go?s=1864:1932#L81)
254
+``` go
255
+func CovariancePopulation(data1, data2 Float64Data) (float64, error)
256
+```
257
+CovariancePopulation computes covariance for entire population between two variables.
258
+
259
+
260
+
261
+## <a name="CumulativeSum">func</a> [CumulativeSum](/cumulative_sum.go?s=81:137#L4)
262
+``` go
263
+func CumulativeSum(input Float64Data) ([]float64, error)
264
+```
265
+CumulativeSum calculates the cumulative sum of the input slice
266
+
267
+
268
+
269
+## <a name="Entropy">func</a> [Entropy](/entropy.go?s=77:125#L6)
270
+``` go
271
+func Entropy(input Float64Data) (float64, error)
272
+```
273
+Entropy provides calculation of the entropy
274
+
275
+
276
+
277
+## <a name="EuclideanDistance">func</a> [EuclideanDistance](/distances.go?s=836:924#L36)
278
+``` go
279
+func EuclideanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error)
280
+```
281
+EuclideanDistance computes the Euclidean distance between two data sets
282
+
283
+
284
+
285
+## <a name="ExpGeom">func</a> [ExpGeom](/geometric_distribution.go?s=652:700#L27)
286
+``` go
287
+func ExpGeom(p float64) (exp float64, err error)
288
+```
289
+ProbGeom generates the expectation or average number of trials
290
+for a geometric random variable with parameter p
291
+
292
+
293
+
294
+## <a name="GeometricMean">func</a> [GeometricMean](/mean.go?s=319:373#L18)
295
+``` go
296
+func GeometricMean(input Float64Data) (float64, error)
297
+```
298
+GeometricMean gets the geometric mean for a slice of numbers
299
+
300
+
301
+
302
+## <a name="HarmonicMean">func</a> [HarmonicMean](/mean.go?s=717:770#L40)
303
+``` go
304
+func HarmonicMean(input Float64Data) (float64, error)
305
+```
306
+HarmonicMean gets the harmonic mean for a slice of numbers
307
+
308
+
309
+
310
+## <a name="InterQuartileRange">func</a> [InterQuartileRange](/quartile.go?s=821:880#L45)
311
+``` go
312
+func InterQuartileRange(input Float64Data) (float64, error)
313
+```
314
+InterQuartileRange finds the range between Q1 and Q3
315
+
316
+
317
+
318
+## <a name="ManhattanDistance">func</a> [ManhattanDistance](/distances.go?s=1277:1365#L50)
319
+``` go
320
+func ManhattanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error)
321
+```
322
+ManhattanDistance computes the Manhattan distance between two data sets
323
+
324
+
325
+
326
+## <a name="Max">func</a> [Max](/max.go?s=78:130#L8)
327
+``` go
328
+func Max(input Float64Data) (max float64, err error)
329
+```
330
+Max finds the highest number in a slice
331
+
332
+
333
+
334
+## <a name="Mean">func</a> [Mean](/mean.go?s=77:122#L6)
335
+``` go
336
+func Mean(input Float64Data) (float64, error)
337
+```
338
+Mean gets the average of a slice of numbers
339
+
340
+
341
+
342
+## <a name="Median">func</a> [Median](/median.go?s=85:143#L6)
343
+``` go
344
+func Median(input Float64Data) (median float64, err error)
345
+```
346
+Median gets the median number in a slice of numbers
347
+
348
+
349
+
350
+## <a name="MedianAbsoluteDeviation">func</a> [MedianAbsoluteDeviation](/deviation.go?s=125:197#L6)
351
+``` go
352
+func MedianAbsoluteDeviation(input Float64Data) (mad float64, err error)
353
+```
354
+MedianAbsoluteDeviation finds the median of the absolute deviations from the dataset median
355
+
356
+
357
+
358
+## <a name="MedianAbsoluteDeviationPopulation">func</a> [MedianAbsoluteDeviationPopulation](/deviation.go?s=360:442#L11)
359
+``` go
360
+func MedianAbsoluteDeviationPopulation(input Float64Data) (mad float64, err error)
361
+```
362
+MedianAbsoluteDeviationPopulation finds the median of the absolute deviations from the population median
363
+
364
+
365
+
366
+## <a name="Midhinge">func</a> [Midhinge](/quartile.go?s=1075:1124#L55)
367
+``` go
368
+func Midhinge(input Float64Data) (float64, error)
369
+```
370
+Midhinge finds the average of the first and third quartiles
371
+
372
+
373
+
374
+## <a name="Min">func</a> [Min](/min.go?s=78:130#L6)
375
+``` go
376
+func Min(input Float64Data) (min float64, err error)
377
+```
378
+Min finds the lowest number in a set of data
379
+
380
+
381
+
382
+## <a name="MinkowskiDistance">func</a> [MinkowskiDistance](/distances.go?s=2152:2256#L75)
383
+``` go
384
+func MinkowskiDistance(dataPointX, dataPointY Float64Data, lambda float64) (distance float64, err error)
385
+```
386
+MinkowskiDistance computes the Minkowski distance between two data sets
387
+
388
+Arguments:
389
+
390
+
391
+	dataPointX: First set of data points
392
+	dataPointY: Second set of data points. Length of both data
393
+	            sets must be equal.
394
+	lambda:     aka p or city blocks; With lambda = 1
395
+	            returned distance is manhattan distance and
396
+	            lambda = 2; it is euclidean distance. Lambda
397
+	            reaching to infinite - distance would be chebysev
398
+	            distance.
399
+
400
+Return:
401
+
402
+
403
+	Distance or error
404
+
405
+
406
+
407
+## <a name="Mode">func</a> [Mode](/mode.go?s=85:141#L4)
408
+``` go
409
+func Mode(input Float64Data) (mode []float64, err error)
410
+```
411
+Mode gets the mode [most frequent value(s)] of a slice of float64s
412
+
413
+
414
+
415
+## <a name="Ncr">func</a> [Ncr](/norm.go?s=7384:7406#L239)
416
+``` go
417
+func Ncr(n, r int) int
418
+```
419
+Ncr is an N choose R algorithm.
420
+Aaron Cannon's algorithm.
421
+
422
+
423
+
424
+## <a name="NormBoxMullerRvs">func</a> [NormBoxMullerRvs](/norm.go?s=667:736#L23)
425
+``` go
426
+func NormBoxMullerRvs(loc float64, scale float64, size int) []float64
427
+```
428
+NormBoxMullerRvs generates random variates using the Box–Muller transform.
429
+For more information please visit: <a href="http://mathworld.wolfram.com/Box-MullerTransformation.html">http://mathworld.wolfram.com/Box-MullerTransformation.html</a>
430
+
431
+
432
+
433
+## <a name="NormCdf">func</a> [NormCdf](/norm.go?s=1826:1885#L52)
434
+``` go
435
+func NormCdf(x float64, loc float64, scale float64) float64
436
+```
437
+NormCdf is the cumulative distribution function.
438
+
439
+
440
+
441
+## <a name="NormEntropy">func</a> [NormEntropy](/norm.go?s=5773:5825#L180)
442
+``` go
443
+func NormEntropy(loc float64, scale float64) float64
444
+```
445
+NormEntropy is the differential entropy of the RV.
446
+
447
+
448
+
449
+## <a name="NormFit">func</a> [NormFit](/norm.go?s=6058:6097#L187)
450
+``` go
451
+func NormFit(data []float64) [2]float64
452
+```
453
+NormFit returns the maximum likelihood estimators for the Normal Distribution.
454
+Takes array of float64 values.
455
+Returns array of Mean followed by Standard Deviation.
456
+
457
+
458
+
459
+## <a name="NormInterval">func</a> [NormInterval](/norm.go?s=6976:7047#L221)
460
+``` go
461
+func NormInterval(alpha float64, loc float64, scale float64) [2]float64
462
+```
463
+NormInterval finds endpoints of the range that contains alpha percent of the distribution.
464
+
465
+
466
+
467
+## <a name="NormIsf">func</a> [NormIsf](/norm.go?s=4330:4393#L137)
468
+``` go
469
+func NormIsf(p float64, loc float64, scale float64) (x float64)
470
+```
471
+NormIsf is the inverse survival function (inverse of sf).
472
+
473
+
474
+
475
+## <a name="NormLogCdf">func</a> [NormLogCdf](/norm.go?s=2016:2078#L57)
476
+``` go
477
+func NormLogCdf(x float64, loc float64, scale float64) float64
478
+```
479
+NormLogCdf is the log of the cumulative distribution function.
480
+
481
+
482
+
483
+## <a name="NormLogPdf">func</a> [NormLogPdf](/norm.go?s=1590:1652#L47)
484
+``` go
485
+func NormLogPdf(x float64, loc float64, scale float64) float64
486
+```
487
+NormLogPdf is the log of the probability density function.
488
+
489
+
490
+
491
+## <a name="NormLogSf">func</a> [NormLogSf](/norm.go?s=2423:2484#L67)
492
+``` go
493
+func NormLogSf(x float64, loc float64, scale float64) float64
494
+```
495
+NormLogSf is the log of the survival function.
496
+
497
+
498
+
499
+## <a name="NormMean">func</a> [NormMean](/norm.go?s=6560:6609#L206)
500
+``` go
501
+func NormMean(loc float64, scale float64) float64
502
+```
503
+NormMean is the mean/expected value of the distribution.
504
+
505
+
506
+
507
+## <a name="NormMedian">func</a> [NormMedian](/norm.go?s=6431:6482#L201)
508
+``` go
509
+func NormMedian(loc float64, scale float64) float64
510
+```
511
+NormMedian is the median of the distribution.
512
+
513
+
514
+
515
+## <a name="NormMoment">func</a> [NormMoment](/norm.go?s=4694:4752#L146)
516
+``` go
517
+func NormMoment(n int, loc float64, scale float64) float64
518
+```
519
+NormMoment approximates the non-central (raw) moment of order n.
520
+For more information please visit: <a href="https://math.stackexchange.com/questions/1945448/methods-for-finding-raw-moments-of-the-normal-distribution">https://math.stackexchange.com/questions/1945448/methods-for-finding-raw-moments-of-the-normal-distribution</a>
521
+
522
+
523
+
524
+## <a name="NormPdf">func</a> [NormPdf](/norm.go?s=1357:1416#L42)
525
+``` go
526
+func NormPdf(x float64, loc float64, scale float64) float64
527
+```
528
+NormPdf is the probability density function.
529
+
530
+
531
+
532
+## <a name="NormPpf">func</a> [NormPpf](/norm.go?s=2854:2917#L75)
533
+``` go
534
+func NormPpf(p float64, loc float64, scale float64) (x float64)
535
+```
536
+NormPpf is the point percentile function.
537
+This is based on Peter John Acklam's inverse normal CDF.
538
+algorithm: <a href="http://home.online.no/~pjacklam/notes/invnorm/">http://home.online.no/~pjacklam/notes/invnorm/</a> (no longer visible).
539
+For more information please visit: <a href="https://stackedboxes.org/2017/05/01/acklams-normal-quantile-function/">https://stackedboxes.org/2017/05/01/acklams-normal-quantile-function/</a>
540
+
541
+
542
+
543
+## <a name="NormPpfRvs">func</a> [NormPpfRvs](/norm.go?s=247:310#L12)
544
+``` go
545
+func NormPpfRvs(loc float64, scale float64, size int) []float64
546
+```
547
+NormPpfRvs generates random variates using the Point Percentile Function.
548
+For more information please visit: <a href="https://demonstrations.wolfram.com/TheMethodOfInverseTransforms/">https://demonstrations.wolfram.com/TheMethodOfInverseTransforms/</a>
549
+
550
+
551
+
552
+## <a name="NormSf">func</a> [NormSf](/norm.go?s=2250:2308#L62)
553
+``` go
554
+func NormSf(x float64, loc float64, scale float64) float64
555
+```
556
+NormSf is the survival function (also defined as 1 - cdf, but sf is sometimes more accurate).
557
+
558
+
559
+
560
+## <a name="NormStats">func</a> [NormStats](/norm.go?s=5277:5345#L162)
561
+``` go
562
+func NormStats(loc float64, scale float64, moments string) []float64
563
+```
564
+NormStats returns the mean, variance, skew, and/or kurtosis.
565
+Mean(‘m’), variance(‘v’), skew(‘s’), and/or kurtosis(‘k’).
566
+Takes string containing any of 'mvsk'.
567
+Returns array of m v s k in that order.
568
+
569
+
570
+
571
+## <a name="NormStd">func</a> [NormStd](/norm.go?s=6814:6862#L216)
572
+``` go
573
+func NormStd(loc float64, scale float64) float64
574
+```
575
+NormStd is the standard deviation of the distribution.
576
+
577
+
578
+
579
+## <a name="NormVar">func</a> [NormVar](/norm.go?s=6675:6723#L211)
580
+``` go
581
+func NormVar(loc float64, scale float64) float64
582
+```
583
+NormVar is the variance of the distribution.
584
+
585
+
586
+
587
+## <a name="Pearson">func</a> [Pearson](/correlation.go?s=655:710#L33)
588
+``` go
589
+func Pearson(data1, data2 Float64Data) (float64, error)
590
+```
591
+Pearson calculates the Pearson product-moment correlation coefficient between two variables
592
+
593
+
594
+
595
+## <a name="Percentile">func</a> [Percentile](/percentile.go?s=98:181#L8)
596
+``` go
597
+func Percentile(input Float64Data, percent float64) (percentile float64, err error)
598
+```
599
+Percentile finds the relative standing in a slice of floats
600
+
601
+
602
+
603
+## <a name="PercentileNearestRank">func</a> [PercentileNearestRank](/percentile.go?s=1079:1173#L54)
604
+``` go
605
+func PercentileNearestRank(input Float64Data, percent float64) (percentile float64, err error)
606
+```
607
+PercentileNearestRank finds the relative standing in a slice of floats using the Nearest Rank method
608
+
609
+
610
+
611
+## <a name="PopulationVariance">func</a> [PopulationVariance](/variance.go?s=828:896#L31)
612
+``` go
613
+func PopulationVariance(input Float64Data) (pvar float64, err error)
614
+```
615
+PopulationVariance finds the amount of variance within a population
616
+
617
+
618
+
619
+## <a name="ProbGeom">func</a> [ProbGeom](/geometric_distribution.go?s=258:322#L10)
620
+``` go
621
+func ProbGeom(a int, b int, p float64) (prob float64, err error)
622
+```
623
+ProbGeom generates the probability for a geometric random variable
624
+with parameter p to achieve success in the interval of [a, b] trials
625
+See <a href="https://en.wikipedia.org/wiki/Geometric_distribution">https://en.wikipedia.org/wiki/Geometric_distribution</a> for more information
626
+
627
+
628
+
629
+## <a name="Round">func</a> [Round](/round.go?s=88:154#L6)
630
+``` go
631
+func Round(input float64, places int) (rounded float64, err error)
632
+```
633
+Round a float to a specific decimal place or precision
634
+
635
+
636
+
637
+## <a name="Sample">func</a> [Sample](/sample.go?s=112:192#L9)
638
+``` go
639
+func Sample(input Float64Data, takenum int, replacement bool) ([]float64, error)
640
+```
641
+Sample returns sample from input with replacement or without
642
+
643
+
644
+
645
+## <a name="SampleVariance">func</a> [SampleVariance](/variance.go?s=1058:1122#L42)
646
+``` go
647
+func SampleVariance(input Float64Data) (svar float64, err error)
648
+```
649
+SampleVariance finds the amount of variance within a sample
650
+
651
+
652
+
653
+## <a name="Sigmoid">func</a> [Sigmoid](/sigmoid.go?s=228:278#L9)
654
+``` go
655
+func Sigmoid(input Float64Data) ([]float64, error)
656
+```
657
+Sigmoid returns the input values in the range of -1 to 1
658
+along the sigmoid or s-shaped curve, commonly used in
659
+machine learning while training neural networks as an
660
+activation function.
661
+
662
+
663
+
664
+## <a name="SoftMax">func</a> [SoftMax](/softmax.go?s=206:256#L8)
665
+``` go
666
+func SoftMax(input Float64Data) ([]float64, error)
667
+```
668
+SoftMax returns the input values in the range of 0 to 1
669
+with sum of all the probabilities being equal to one. It
670
+is commonly used in machine learning neural networks.
671
+
672
+
673
+
674
+## <a name="StableSample">func</a> [StableSample](/sample.go?s=974:1042#L50)
675
+``` go
676
+func StableSample(input Float64Data, takenum int) ([]float64, error)
677
+```
678
+StableSample like stable sort, it returns samples from input while keeps the order of original data.
679
+
680
+
681
+
682
+## <a name="StandardDeviation">func</a> [StandardDeviation](/deviation.go?s=695:762#L27)
683
+``` go
684
+func StandardDeviation(input Float64Data) (sdev float64, err error)
685
+```
686
+StandardDeviation the amount of variation in the dataset
687
+
688
+
689
+
690
+## <a name="StandardDeviationPopulation">func</a> [StandardDeviationPopulation](/deviation.go?s=892:969#L32)
691
+``` go
692
+func StandardDeviationPopulation(input Float64Data) (sdev float64, err error)
693
+```
694
+StandardDeviationPopulation finds the amount of variation from the population
695
+
696
+
697
+
698
+## <a name="StandardDeviationSample">func</a> [StandardDeviationSample](/deviation.go?s=1250:1323#L46)
699
+``` go
700
+func StandardDeviationSample(input Float64Data) (sdev float64, err error)
701
+```
702
+StandardDeviationSample finds the amount of variation from a sample
703
+
704
+
705
+
706
+## <a name="StdDevP">func</a> [StdDevP](/legacy.go?s=339:396#L14)
707
+``` go
708
+func StdDevP(input Float64Data) (sdev float64, err error)
709
+```
710
+StdDevP is a shortcut to StandardDeviationPopulation
711
+
712
+
713
+
714
+## <a name="StdDevS">func</a> [StdDevS](/legacy.go?s=497:554#L19)
715
+``` go
716
+func StdDevS(input Float64Data) (sdev float64, err error)
717
+```
718
+StdDevS is a shortcut to StandardDeviationSample
719
+
720
+
721
+
722
+## <a name="Sum">func</a> [Sum](/sum.go?s=78:130#L6)
723
+``` go
724
+func Sum(input Float64Data) (sum float64, err error)
725
+```
726
+Sum adds all the numbers of a slice together
727
+
728
+
729
+
730
+## <a name="Trimean">func</a> [Trimean](/quartile.go?s=1320:1368#L65)
731
+``` go
732
+func Trimean(input Float64Data) (float64, error)
733
+```
734
+Trimean finds the average of the median and the midhinge
735
+
736
+
737
+
738
+## <a name="VarGeom">func</a> [VarGeom](/geometric_distribution.go?s=885:933#L37)
739
+``` go
740
+func VarGeom(p float64) (exp float64, err error)
741
+```
742
+ProbGeom generates the variance for number for a
743
+geometric random variable with parameter p
744
+
745
+
746
+
747
+## <a name="VarP">func</a> [VarP](/legacy.go?s=59:113#L4)
748
+``` go
749
+func VarP(input Float64Data) (sdev float64, err error)
750
+```
751
+VarP is a shortcut to PopulationVariance
752
+
753
+
754
+
755
+## <a name="VarS">func</a> [VarS](/legacy.go?s=193:247#L9)
756
+``` go
757
+func VarS(input Float64Data) (sdev float64, err error)
758
+```
759
+VarS is a shortcut to SampleVariance
760
+
761
+
762
+
763
+## <a name="Variance">func</a> [Variance](/variance.go?s=659:717#L26)
764
+``` go
765
+func Variance(input Float64Data) (sdev float64, err error)
766
+```
767
+Variance the amount of variation in the dataset
768
+
769
+
770
+
771
+
772
+## <a name="Coordinate">type</a> [Coordinate](/regression.go?s=143:183#L9)
773
+``` go
774
+type Coordinate struct {
775
+    X, Y float64
776
+}
777
+
778
+```
779
+Coordinate holds the data in a series
780
+
781
+
782
+
783
+
784
+
785
+
786
+
787
+### <a name="ExpReg">func</a> [ExpReg](/legacy.go?s=791:856#L29)
788
+``` go
789
+func ExpReg(s []Coordinate) (regressions []Coordinate, err error)
790
+```
791
+ExpReg is a shortcut to ExponentialRegression
792
+
793
+
794
+### <a name="LinReg">func</a> [LinReg](/legacy.go?s=643:708#L24)
795
+``` go
796
+func LinReg(s []Coordinate) (regressions []Coordinate, err error)
797
+```
798
+LinReg is a shortcut to LinearRegression
799
+
800
+
801
+### <a name="LogReg">func</a> [LogReg](/legacy.go?s=944:1009#L34)
802
+``` go
803
+func LogReg(s []Coordinate) (regressions []Coordinate, err error)
804
+```
805
+LogReg is a shortcut to LogarithmicRegression
806
+
807
+
808
+
809
+
810
+
811
+## <a name="Float64Data">type</a> [Float64Data](/data.go?s=80:106#L4)
812
+``` go
813
+type Float64Data []float64
814
+```
815
+Float64Data is a named type for []float64 with helper methods
816
+
817
+
818
+
819
+
820
+
821
+
822
+
823
+### <a name="LoadRawData">func</a> [LoadRawData](/load.go?s=145:194#L12)
824
+``` go
825
+func LoadRawData(raw interface{}) (f Float64Data)
826
+```
827
+LoadRawData parses and converts a slice of mixed data types to floats
828
+
829
+
830
+
831
+
832
+
833
+### <a name="Float64Data.AutoCorrelation">func</a> (Float64Data) [AutoCorrelation](/data.go?s=3257:3320#L91)
834
+``` go
835
+func (f Float64Data) AutoCorrelation(lags int) (float64, error)
836
+```
837
+AutoCorrelation is the correlation of a signal with a delayed copy of itself as a function of delay
838
+
839
+
840
+
841
+
842
+### <a name="Float64Data.Correlation">func</a> (Float64Data) [Correlation](/data.go?s=3058:3122#L86)
843
+``` go
844
+func (f Float64Data) Correlation(d Float64Data) (float64, error)
845
+```
846
+Correlation describes the degree of relationship between two sets of data
847
+
848
+
849
+
850
+
851
+### <a name="Float64Data.Covariance">func</a> (Float64Data) [Covariance](/data.go?s=4801:4864#L141)
852
+``` go
853
+func (f Float64Data) Covariance(d Float64Data) (float64, error)
854
+```
855
+Covariance is a measure of how much two sets of data change
856
+
857
+
858
+
859
+
860
+### <a name="Float64Data.CovariancePopulation">func</a> (Float64Data) [CovariancePopulation](/data.go?s=4983:5056#L146)
861
+``` go
862
+func (f Float64Data) CovariancePopulation(d Float64Data) (float64, error)
863
+```
864
+CovariancePopulation computes covariance for entire population between two variables
865
+
866
+
867
+
868
+
869
+### <a name="Float64Data.CumulativeSum">func</a> (Float64Data) [CumulativeSum](/data.go?s=883:938#L28)
870
+``` go
871
+func (f Float64Data) CumulativeSum() ([]float64, error)
872
+```
873
+CumulativeSum returns the cumulative sum of the data
874
+
875
+
876
+
877
+
878
+### <a name="Float64Data.Entropy">func</a> (Float64Data) [Entropy](/data.go?s=5480:5527#L162)
879
+``` go
880
+func (f Float64Data) Entropy() (float64, error)
881
+```
882
+Entropy provides calculation of the entropy
883
+
884
+
885
+
886
+
887
+### <a name="Float64Data.GeometricMean">func</a> (Float64Data) [GeometricMean](/data.go?s=1332:1385#L40)
888
+``` go
889
+func (f Float64Data) GeometricMean() (float64, error)
890
+```
891
+GeometricMean returns the median of the data
892
+
893
+
894
+
895
+
896
+### <a name="Float64Data.Get">func</a> (Float64Data) [Get](/data.go?s=129:168#L7)
897
+``` go
898
+func (f Float64Data) Get(i int) float64
899
+```
900
+Get item in slice
901
+
902
+
903
+
904
+
905
+### <a name="Float64Data.HarmonicMean">func</a> (Float64Data) [HarmonicMean](/data.go?s=1460:1512#L43)
906
+``` go
907
+func (f Float64Data) HarmonicMean() (float64, error)
908
+```
909
+HarmonicMean returns the mode of the data
910
+
911
+
912
+
913
+
914
+### <a name="Float64Data.InterQuartileRange">func</a> (Float64Data) [InterQuartileRange](/data.go?s=3755:3813#L106)
915
+``` go
916
+func (f Float64Data) InterQuartileRange() (float64, error)
917
+```
918
+InterQuartileRange finds the range between Q1 and Q3
919
+
920
+
921
+
922
+
923
+### <a name="Float64Data.Len">func</a> (Float64Data) [Len](/data.go?s=217:247#L10)
924
+``` go
925
+func (f Float64Data) Len() int
926
+```
927
+Len returns length of slice
928
+
929
+
930
+
931
+
932
+### <a name="Float64Data.Less">func</a> (Float64Data) [Less](/data.go?s=318:358#L13)
933
+``` go
934
+func (f Float64Data) Less(i, j int) bool
935
+```
936
+Less returns if one number is less than another
937
+
938
+
939
+
940
+
941
+### <a name="Float64Data.Max">func</a> (Float64Data) [Max](/data.go?s=645:688#L22)
942
+``` go
943
+func (f Float64Data) Max() (float64, error)
944
+```
945
+Max returns the maximum number in the data
946
+
947
+
948
+
949
+
950
+### <a name="Float64Data.Mean">func</a> (Float64Data) [Mean](/data.go?s=1005:1049#L31)
951
+``` go
952
+func (f Float64Data) Mean() (float64, error)
953
+```
954
+Mean returns the mean of the data
955
+
956
+
957
+
958
+
959
+### <a name="Float64Data.Median">func</a> (Float64Data) [Median](/data.go?s=1111:1157#L34)
960
+``` go
961
+func (f Float64Data) Median() (float64, error)
962
+```
963
+Median returns the median of the data
964
+
965
+
966
+
967
+
968
+### <a name="Float64Data.MedianAbsoluteDeviation">func</a> (Float64Data) [MedianAbsoluteDeviation](/data.go?s=1630:1693#L46)
969
+``` go
970
+func (f Float64Data) MedianAbsoluteDeviation() (float64, error)
971
+```
972
+MedianAbsoluteDeviation the median of the absolute deviations from the dataset median
973
+
974
+
975
+
976
+
977
+### <a name="Float64Data.MedianAbsoluteDeviationPopulation">func</a> (Float64Data) [MedianAbsoluteDeviationPopulation](/data.go?s=1842:1915#L51)
978
+``` go
979
+func (f Float64Data) MedianAbsoluteDeviationPopulation() (float64, error)
980
+```
981
+MedianAbsoluteDeviationPopulation finds the median of the absolute deviations from the population median
982
+
983
+
984
+
985
+
986
+### <a name="Float64Data.Midhinge">func</a> (Float64Data) [Midhinge](/data.go?s=3912:3973#L111)
987
+``` go
988
+func (f Float64Data) Midhinge(d Float64Data) (float64, error)
989
+```
990
+Midhinge finds the average of the first and third quartiles
991
+
992
+
993
+
994
+
995
+### <a name="Float64Data.Min">func</a> (Float64Data) [Min](/data.go?s=536:579#L19)
996
+``` go
997
+func (f Float64Data) Min() (float64, error)
998
+```
999
+Min returns the minimum number in the data
1000
+
1001
+
1002
+
1003
+
1004
+### <a name="Float64Data.Mode">func</a> (Float64Data) [Mode](/data.go?s=1217:1263#L37)
1005
+``` go
1006
+func (f Float64Data) Mode() ([]float64, error)
1007
+```
1008
+Mode returns the mode of the data
1009
+
1010
+
1011
+
1012
+
1013
+### <a name="Float64Data.Pearson">func</a> (Float64Data) [Pearson](/data.go?s=3455:3515#L96)
1014
+``` go
1015
+func (f Float64Data) Pearson(d Float64Data) (float64, error)
1016
+```
1017
+Pearson calculates the Pearson product-moment correlation coefficient between two variables.
1018
+
1019
+
1020
+
1021
+
1022
+### <a name="Float64Data.Percentile">func</a> (Float64Data) [Percentile](/data.go?s=2696:2755#L76)
1023
+``` go
1024
+func (f Float64Data) Percentile(p float64) (float64, error)
1025
+```
1026
+Percentile finds the relative standing in a slice of floats
1027
+
1028
+
1029
+
1030
+
1031
+### <a name="Float64Data.PercentileNearestRank">func</a> (Float64Data) [PercentileNearestRank](/data.go?s=2869:2939#L81)
1032
+``` go
1033
+func (f Float64Data) PercentileNearestRank(p float64) (float64, error)
1034
+```
1035
+PercentileNearestRank finds the relative standing using the Nearest Rank method
1036
+
1037
+
1038
+
1039
+
1040
+### <a name="Float64Data.PopulationVariance">func</a> (Float64Data) [PopulationVariance](/data.go?s=4495:4553#L131)
1041
+``` go
1042
+func (f Float64Data) PopulationVariance() (float64, error)
1043
+```
1044
+PopulationVariance finds the amount of variance within a population
1045
+
1046
+
1047
+
1048
+
1049
+### <a name="Float64Data.Quartile">func</a> (Float64Data) [Quartile](/data.go?s=3610:3673#L101)
1050
+``` go
1051
+func (f Float64Data) Quartile(d Float64Data) (Quartiles, error)
1052
+```
1053
+Quartile returns the three quartile points from a slice of data
1054
+
1055
+
1056
+
1057
+
1058
+### <a name="Float64Data.QuartileOutliers">func</a> (Float64Data) [QuartileOutliers](/data.go?s=2542:2599#L71)
1059
+``` go
1060
+func (f Float64Data) QuartileOutliers() (Outliers, error)
1061
+```
1062
+QuartileOutliers finds the mild and extreme outliers
1063
+
1064
+
1065
+
1066
+
1067
+### <a name="Float64Data.Quartiles">func</a> (Float64Data) [Quartiles](/data.go?s=5628:5679#L167)
1068
+``` go
1069
+func (f Float64Data) Quartiles() (Quartiles, error)
1070
+```
1071
+Quartiles returns the three quartile points from instance of Float64Data
1072
+
1073
+
1074
+
1075
+
1076
+### <a name="Float64Data.Sample">func</a> (Float64Data) [Sample](/data.go?s=4208:4269#L121)
1077
+``` go
1078
+func (f Float64Data) Sample(n int, r bool) ([]float64, error)
1079
+```
1080
+Sample returns sample from input with replacement or without
1081
+
1082
+
1083
+
1084
+
1085
+### <a name="Float64Data.SampleVariance">func</a> (Float64Data) [SampleVariance](/data.go?s=4652:4706#L136)
1086
+``` go
1087
+func (f Float64Data) SampleVariance() (float64, error)
1088
+```
1089
+SampleVariance finds the amount of variance within a sample
1090
+
1091
+
1092
+
1093
+
1094
+### <a name="Float64Data.Sigmoid">func</a> (Float64Data) [Sigmoid](/data.go?s=5169:5218#L151)
1095
+``` go
1096
+func (f Float64Data) Sigmoid() ([]float64, error)
1097
+```
1098
+Sigmoid returns the input values along the sigmoid or s-shaped curve
1099
+
1100
+
1101
+
1102
+
1103
+### <a name="Float64Data.SoftMax">func</a> (Float64Data) [SoftMax](/data.go?s=5359:5408#L157)
1104
+``` go
1105
+func (f Float64Data) SoftMax() ([]float64, error)
1106
+```
1107
+SoftMax returns the input values in the range of 0 to 1
1108
+with sum of all the probabilities being equal to one.
1109
+
1110
+
1111
+
1112
+
1113
+### <a name="Float64Data.StandardDeviation">func</a> (Float64Data) [StandardDeviation](/data.go?s=2026:2083#L56)
1114
+``` go
1115
+func (f Float64Data) StandardDeviation() (float64, error)
1116
+```
1117
+StandardDeviation the amount of variation in the dataset
1118
+
1119
+
1120
+
1121
+
1122
+### <a name="Float64Data.StandardDeviationPopulation">func</a> (Float64Data) [StandardDeviationPopulation](/data.go?s=2199:2266#L61)
1123
+``` go
1124
+func (f Float64Data) StandardDeviationPopulation() (float64, error)
1125
+```
1126
+StandardDeviationPopulation finds the amount of variation from the population
1127
+
1128
+
1129
+
1130
+
1131
+### <a name="Float64Data.StandardDeviationSample">func</a> (Float64Data) [StandardDeviationSample](/data.go?s=2382:2445#L66)
1132
+``` go
1133
+func (f Float64Data) StandardDeviationSample() (float64, error)
1134
+```
1135
+StandardDeviationSample finds the amount of variation from a sample
1136
+
1137
+
1138
+
1139
+
1140
+### <a name="Float64Data.Sum">func</a> (Float64Data) [Sum](/data.go?s=764:807#L25)
1141
+``` go
1142
+func (f Float64Data) Sum() (float64, error)
1143
+```
1144
+Sum returns the total of all the numbers in the data
1145
+
1146
+
1147
+
1148
+
1149
+### <a name="Float64Data.Swap">func</a> (Float64Data) [Swap](/data.go?s=425:460#L16)
1150
+``` go
1151
+func (f Float64Data) Swap(i, j int)
1152
+```
1153
+Swap switches out two numbers in slice
1154
+
1155
+
1156
+
1157
+
1158
+### <a name="Float64Data.Trimean">func</a> (Float64Data) [Trimean](/data.go?s=4059:4119#L116)
1159
+``` go
1160
+func (f Float64Data) Trimean(d Float64Data) (float64, error)
1161
+```
1162
+Trimean finds the average of the median and the midhinge
1163
+
1164
+
1165
+
1166
+
1167
+### <a name="Float64Data.Variance">func</a> (Float64Data) [Variance](/data.go?s=4350:4398#L126)
1168
+``` go
1169
+func (f Float64Data) Variance() (float64, error)
1170
+```
1171
+Variance the amount of variation in the dataset
1172
+
1173
+
1174
+
1175
+
1176
+## <a name="Outliers">type</a> [Outliers](/outlier.go?s=73:139#L4)
1177
+``` go
1178
+type Outliers struct {
1179
+    Mild    Float64Data
1180
+    Extreme Float64Data
1181
+}
1182
+
1183
+```
1184
+Outliers holds mild and extreme outliers found in data
1185
+
1186
+
1187
+
1188
+
1189
+
1190
+
1191
+
1192
+### <a name="QuartileOutliers">func</a> [QuartileOutliers](/outlier.go?s=197:255#L10)
1193
+``` go
1194
+func QuartileOutliers(input Float64Data) (Outliers, error)
1195
+```
1196
+QuartileOutliers finds the mild and extreme outliers
1197
+
1198
+
1199
+
1200
+
1201
+
1202
+## <a name="Quartiles">type</a> [Quartiles](/quartile.go?s=75:136#L6)
1203
+``` go
1204
+type Quartiles struct {
1205
+    Q1 float64
1206
+    Q2 float64
1207
+    Q3 float64
1208
+}
1209
+
1210
+```
1211
+Quartiles holds the three quartile points
1212
+
1213
+
1214
+
1215
+
1216
+
1217
+
1218
+
1219
+### <a name="Quartile">func</a> [Quartile](/quartile.go?s=205:256#L13)
1220
+``` go
1221
+func Quartile(input Float64Data) (Quartiles, error)
1222
+```
1223
+Quartile returns the three quartile points from a slice of data
1224
+
1225
+
1226
+
1227
+
1228
+
1229
+## <a name="Series">type</a> [Series](/regression.go?s=76:100#L6)
1230
+``` go
1231
+type Series []Coordinate
1232
+```
1233
+Series is a container for a series of data
1234
+
1235
+
1236
+
1237
+
1238
+
1239
+
1240
+
1241
+### <a name="ExponentialRegression">func</a> [ExponentialRegression](/regression.go?s=1089:1157#L50)
1242
+``` go
1243
+func ExponentialRegression(s Series) (regressions Series, err error)
1244
+```
1245
+ExponentialRegression returns an exponential regression on data series
1246
+
1247
+
1248
+### <a name="LinearRegression">func</a> [LinearRegression](/regression.go?s=262:325#L14)
1249
+``` go
1250
+func LinearRegression(s Series) (regressions Series, err error)
1251
+```
1252
+LinearRegression finds the least squares linear regression on data series
1253
+
1254
+
1255
+### <a name="LogarithmicRegression">func</a> [LogarithmicRegression](/regression.go?s=1903:1971#L85)
1256
+``` go
1257
+func LogarithmicRegression(s Series) (regressions Series, err error)
1258
+```
1259
+LogarithmicRegression returns an logarithmic regression on data series
1260
+
1261
+
1262
+
1263
+
1264
+
1265
+
1266
+
1267
+
1268
+
1269
+- - -
1270
+Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
0 1271
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+The MIT License (MIT)
1
+
2
+Copyright (c) 2014-2023 Montana Flynn (https://montanaflynn.com)
3
+
4
+Permission is hereby granted, free of charge, to any person obtaining a copy
5
+of this software and associated documentation files (the "Software"), to deal
6
+in the Software without restriction, including without limitation the rights
7
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+copies of the Software, and to permit persons to whom the Software is
9
+furnished to do so, subject to the following conditions:
10
+
11
+The above copyright notice and this permission notice shall be included in all
12
+copies or substantial portions of the Software.
13
+
14
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+SOFTWARE.
0 21
new file mode 100644
... ...
@@ -0,0 +1,34 @@
0
+.PHONY: all
1
+
2
+default: test lint
3
+
4
+format: 
5
+	go fmt .
6
+
7
+test:
8
+	go test -race 
9
+	
10
+check: format test
11
+
12
+benchmark:
13
+	go test -bench=. -benchmem
14
+
15
+coverage:
16
+	go test -coverprofile=coverage.out
17
+	go tool cover -html="coverage.out"
18
+
19
+lint: format
20
+	golangci-lint run .
21
+
22
+docs:
23
+	godoc2md github.com/montanaflynn/stats | sed -e s#src/target/##g > DOCUMENTATION.md
24
+
25
+release:
26
+	git-chglog --output CHANGELOG.md --next-tag ${TAG}
27
+	git add CHANGELOG.md
28
+	git commit -m "Update changelog with ${TAG} changes"
29
+	git tag ${TAG}
30
+	git-chglog $(TAG) | tail -n +4 | gsed '1s/^/$(TAG)\n/gm' > release-notes.txt
31
+	git push origin master ${TAG}
32
+	hub release create --copy -F release-notes.txt ${TAG}
33
+
0 34
new file mode 100644
... ...
@@ -0,0 +1,237 @@
0
+# Stats - Golang Statistics Package
1
+
2
+[![][action-svg]][action-url] [![][codecov-svg]][codecov-url] [![][goreport-svg]][goreport-url] [![][godoc-svg]][godoc-url] [![][pkggodev-svg]][pkggodev-url] [![][license-svg]][license-url]
3
+
4
+A well tested and comprehensive Golang statistics library / package / module with no dependencies.
5
+
6
+If you have any suggestions, problems or bug reports please [create an issue](https://github.com/montanaflynn/stats/issues) and I'll do my best to accommodate you. In addition simply starring the repo would show your support for the project and be very much appreciated!
7
+
8
+## Installation
9
+
10
+```
11
+go get github.com/montanaflynn/stats
12
+```
13
+
14
+## Example Usage
15
+
16
+All the functions can be seen in [examples/main.go](examples/main.go) but here's a little taste:
17
+
18
+```go
19
+// start with some source data to use
20
+data := []float64{1.0, 2.1, 3.2, 4.823, 4.1, 5.8}
21
+
22
+// you could also use different types like this
23
+// data := stats.LoadRawData([]int{1, 2, 3, 4, 5})
24
+// data := stats.LoadRawData([]interface{}{1.1, "2", 3})
25
+// etc...
26
+
27
+median, _ := stats.Median(data)
28
+fmt.Println(median) // 3.65
29
+
30
+roundedMedian, _ := stats.Round(median, 0)
31
+fmt.Println(roundedMedian) // 4
32
+```
33
+
34
+## Documentation
35
+
36
+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).
37
+
38
+You can also view docs offline with the following commands:
39
+
40
+```
41
+# Command line
42
+godoc .              # show all exported apis
43
+godoc . Median       # show a single function
44
+godoc -ex . Round    # show function with example
45
+godoc . Float64Data  # show the type and methods
46
+
47
+# Local website
48
+godoc -http=:4444    # start the godoc server on port 4444
49
+open http://localhost:4444/pkg/github.com/montanaflynn/stats/
50
+```
51
+
52
+The exported API is as follows:
53
+
54
+```go
55
+var (
56
+    ErrEmptyInput = statsError{"Input must not be empty."}
57
+    ErrNaN        = statsError{"Not a number."}
58
+    ErrNegative   = statsError{"Must not contain negative values."}
59
+    ErrZero       = statsError{"Must not contain zero values."}
60
+    ErrBounds     = statsError{"Input is outside of range."}
61
+    ErrSize       = statsError{"Must be the same length."}
62
+    ErrInfValue   = statsError{"Value is infinite."}
63
+    ErrYCoord     = statsError{"Y Value must be greater than zero."}
64
+)
65
+
66
+func Round(input float64, places int) (rounded float64, err error) {}
67
+
68
+type Float64Data []float64
69
+
70
+func LoadRawData(raw interface{}) (f Float64Data) {}
71
+
72
+func AutoCorrelation(data Float64Data, lags int) (float64, error) {}
73
+func ChebyshevDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {}
74
+func Correlation(data1, data2 Float64Data) (float64, error) {}
75
+func Covariance(data1, data2 Float64Data) (float64, error) {}
76
+func CovariancePopulation(data1, data2 Float64Data) (float64, error) {}
77
+func CumulativeSum(input Float64Data) ([]float64, error) {}
78
+func Describe(input Float64Data, allowNaN bool, percentiles *[]float64) (*Description, error) {}
79
+func DescribePercentileFunc(input Float64Data, allowNaN bool, percentiles *[]float64, percentileFunc func(Float64Data, float64) (float64, error)) (*Description, error) {}
80
+func Entropy(input Float64Data) (float64, error) {}
81
+func EuclideanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {}
82
+func GeometricMean(input Float64Data) (float64, error) {}
83
+func HarmonicMean(input Float64Data) (float64, error) {}
84
+func InterQuartileRange(input Float64Data) (float64, error) {}
85
+func ManhattanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {}
86
+func Max(input Float64Data) (max float64, err error) {}
87
+func Mean(input Float64Data) (float64, error) {}
88
+func Median(input Float64Data) (median float64, err error) {}
89
+func MedianAbsoluteDeviation(input Float64Data) (mad float64, err error) {}
90
+func MedianAbsoluteDeviationPopulation(input Float64Data) (mad float64, err error) {}
91
+func Midhinge(input Float64Data) (float64, error) {}
92
+func Min(input Float64Data) (min float64, err error) {}
93
+func MinkowskiDistance(dataPointX, dataPointY Float64Data, lambda float64) (distance float64, err error) {}
94
+func Mode(input Float64Data) (mode []float64, err error) {}
95
+func NormBoxMullerRvs(loc float64, scale float64, size int) []float64 {}
96
+func NormCdf(x float64, loc float64, scale float64) float64 {}
97
+func NormEntropy(loc float64, scale float64) float64 {}
98
+func NormFit(data []float64) [2]float64{}
99
+func NormInterval(alpha float64, loc float64,  scale float64 ) [2]float64 {}
100
+func NormIsf(p float64, loc float64, scale float64) (x float64) {}
101
+func NormLogCdf(x float64, loc float64, scale float64) float64 {}
102
+func NormLogPdf(x float64, loc float64, scale float64) float64 {}
103
+func NormLogSf(x float64, loc float64, scale float64) float64 {}
104
+func NormMean(loc float64, scale float64) float64 {}
105
+func NormMedian(loc float64, scale float64) float64 {}
106
+func NormMoment(n int, loc float64, scale float64) float64 {}
107
+func NormPdf(x float64, loc float64, scale float64) float64 {}
108
+func NormPpf(p float64, loc float64, scale float64) (x float64) {}
109
+func NormPpfRvs(loc float64, scale float64, size int) []float64 {}
110
+func NormSf(x float64, loc float64, scale float64) float64 {}
111
+func NormStats(loc float64, scale float64, moments string) []float64 {}
112
+func NormStd(loc float64, scale float64) float64 {}
113
+func NormVar(loc float64, scale float64) float64 {}
114
+func Pearson(data1, data2 Float64Data) (float64, error) {}
115
+func Percentile(input Float64Data, percent float64) (percentile float64, err error) {}
116
+func PercentileNearestRank(input Float64Data, percent float64) (percentile float64, err error) {}
117
+func PopulationVariance(input Float64Data) (pvar float64, err error) {}
118
+func Sample(input Float64Data, takenum int, replacement bool) ([]float64, error) {}
119
+func SampleVariance(input Float64Data) (svar float64, err error) {}
120
+func Sigmoid(input Float64Data) ([]float64, error) {}
121
+func SoftMax(input Float64Data) ([]float64, error) {}
122
+func StableSample(input Float64Data, takenum int) ([]float64, error) {}
123
+func StandardDeviation(input Float64Data) (sdev float64, err error) {}
124
+func StandardDeviationPopulation(input Float64Data) (sdev float64, err error) {}
125
+func StandardDeviationSample(input Float64Data) (sdev float64, err error) {}
126
+func StdDevP(input Float64Data) (sdev float64, err error) {}
127
+func StdDevS(input Float64Data) (sdev float64, err error) {}
128
+func Sum(input Float64Data) (sum float64, err error) {}
129
+func Trimean(input Float64Data) (float64, error) {}
130
+func VarP(input Float64Data) (sdev float64, err error) {}
131
+func VarS(input Float64Data) (sdev float64, err error) {}
132
+func Variance(input Float64Data) (sdev float64, err error) {}
133
+func ProbGeom(a int, b int, p float64) (prob float64, err error) {}
134
+func ExpGeom(p float64) (exp float64, err error) {}
135
+func VarGeom(p float64) (exp float64, err error) {}
136
+
137
+type Coordinate struct {
138
+    X, Y float64
139
+}
140
+
141
+type Series []Coordinate
142
+
143
+func ExponentialRegression(s Series) (regressions Series, err error) {}
144
+func LinearRegression(s Series) (regressions Series, err error) {}
145
+func LogarithmicRegression(s Series) (regressions Series, err error) {}
146
+
147
+type Outliers struct {
148
+    Mild    Float64Data
149
+    Extreme Float64Data
150
+}
151
+
152
+type Quartiles struct {
153
+    Q1 float64
154
+    Q2 float64
155
+    Q3 float64
156
+}
157
+
158
+func Quartile(input Float64Data) (Quartiles, error) {}
159
+func QuartileOutliers(input Float64Data) (Outliers, error) {}
160
+```
161
+
162
+## Contributing
163
+
164
+Pull request are always welcome no matter how big or small. I've included a [Makefile](https://github.com/montanaflynn/stats/blob/master/Makefile) that has a lot of helper targets for common actions such as linting, testing, code coverage reporting and more.
165
+
166
+1. Fork the repo and clone your fork
167
+2. Create new branch (`git checkout -b some-thing`)
168
+3. Make the desired changes
169
+4. Ensure tests pass (`go test -cover` or `make test`)
170
+5. Run lint and fix problems (`go vet .` or `make lint`)
171
+6. Commit changes (`git commit -am 'Did something'`)
172
+7. Push branch (`git push origin some-thing`)
173
+8. Submit pull request
174
+
175
+To make things as seamless as possible please also consider the following steps:
176
+
177
+- Update `examples/main.go` with a simple example of the new feature
178
+- Update `README.md` documentation section with any new exported API
179
+- Keep 100% code coverage (you can check with `make coverage`)
180
+- Squash commits into single units of work with `git rebase -i new-feature`
181
+
182
+## Releasing
183
+
184
+This is not required by contributors and mostly here as a reminder to myself as the maintainer of this repo. To release a new version we should update the [CHANGELOG.md](/CHANGELOG.md) and [DOCUMENTATION.md](/DOCUMENTATION.md).
185
+
186
+First install the tools used to generate the markdown files and release:
187
+
188
+```
189
+go install github.com/davecheney/godoc2md@latest
190
+go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
191
+brew tap git-chglog/git-chglog
192
+brew install gnu-sed hub git-chglog
193
+```
194
+
195
+Then you can run these `make` directives:
196
+
197
+```
198
+# Generate DOCUMENTATION.md
199
+make docs
200
+```
201
+
202
+Then we can create a [CHANGELOG.md](/CHANGELOG.md) a new git tag and a github release:
203
+
204
+```
205
+make release TAG=v0.x.x
206
+```
207
+
208
+To authenticate `hub` for the release you will need to create a personal access token and use it as the password when it's requested.
209
+
210
+## MIT License
211
+
212
+Copyright (c) 2014-2023 Montana Flynn (https://montanaflynn.com)
213
+
214
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
215
+
216
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
217
+
218
+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.
219
+
220
+[action-url]: https://github.com/montanaflynn/stats/actions
221
+[action-svg]: https://img.shields.io/github/actions/workflow/status/montanaflynn/stats/go.yml
222
+
223
+[codecov-url]: https://app.codecov.io/gh/montanaflynn/stats
224
+[codecov-svg]: https://img.shields.io/codecov/c/github/montanaflynn/stats?token=wnw8dActnH
225
+
226
+[goreport-url]: https://goreportcard.com/report/github.com/montanaflynn/stats
227
+[goreport-svg]: https://goreportcard.com/badge/github.com/montanaflynn/stats
228
+
229
+[godoc-url]: https://godoc.org/github.com/montanaflynn/stats
230
+[godoc-svg]: https://godoc.org/github.com/montanaflynn/stats?status.svg
231
+
232
+[pkggodev-url]: https://pkg.go.dev/github.com/montanaflynn/stats
233
+[pkggodev-svg]: https://gistcdn.githack.com/montanaflynn/b02f1d78d8c0de8435895d7e7cd0d473/raw/17f2a5a69f1323ecd42c00e0683655da96d9ecc8/badge.svg
234
+
235
+[license-url]: https://github.com/montanaflynn/stats/blob/master/LICENSE
236
+[license-svg]: https://img.shields.io/badge/license-MIT-blue.svg
0 237
new file mode 100644
... ...
@@ -0,0 +1,60 @@
0
+package stats
1
+
2
+import (
3
+	"math"
4
+)
5
+
6
+// Correlation describes the degree of relationship between two sets of data
7
+func Correlation(data1, data2 Float64Data) (float64, error) {
8
+
9
+	l1 := data1.Len()
10
+	l2 := data2.Len()
11
+
12
+	if l1 == 0 || l2 == 0 {
13
+		return math.NaN(), EmptyInputErr
14
+	}
15
+
16
+	if l1 != l2 {
17
+		return math.NaN(), SizeErr
18
+	}
19
+
20
+	sdev1, _ := StandardDeviationPopulation(data1)
21
+	sdev2, _ := StandardDeviationPopulation(data2)
22
+
23
+	if sdev1 == 0 || sdev2 == 0 {
24
+		return 0, nil
25
+	}
26
+
27
+	covp, _ := CovariancePopulation(data1, data2)
28
+	return covp / (sdev1 * sdev2), nil
29
+}
30
+
31
+// Pearson calculates the Pearson product-moment correlation coefficient between two variables
32
+func Pearson(data1, data2 Float64Data) (float64, error) {
33
+	return Correlation(data1, data2)
34
+}
35
+
36
+// AutoCorrelation is the correlation of a signal with a delayed copy of itself as a function of delay
37
+func AutoCorrelation(data Float64Data, lags int) (float64, error) {
38
+	if len(data) < 1 {
39
+		return 0, EmptyInputErr
40
+	}
41
+
42
+	mean, _ := Mean(data)
43
+
44
+	var result, q float64
45
+
46
+	for i := 0; i < lags; i++ {
47
+		v := (data[0] - mean) * (data[0] - mean)
48
+		for i := 1; i < len(data); i++ {
49
+			delta0 := data[i-1] - mean
50
+			delta1 := data[i] - mean
51
+			q += (delta0*delta1 - q) / float64(i+1)
52
+			v += (delta1*delta1 - v) / float64(i+1)
53
+		}
54
+
55
+		result = q / v
56
+	}
57
+
58
+	return result, nil
59
+}
0 60
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+package stats
1
+
2
+// CumulativeSum calculates the cumulative sum of the input slice
3
+func CumulativeSum(input Float64Data) ([]float64, error) {
4
+
5
+	if input.Len() == 0 {
6
+		return Float64Data{}, EmptyInput
7
+	}
8
+
9
+	cumSum := make([]float64, input.Len())
10
+
11
+	for i, val := range input {
12
+		if i == 0 {
13
+			cumSum[i] = val
14
+		} else {
15
+			cumSum[i] = cumSum[i-1] + val
16
+		}
17
+	}
18
+
19
+	return cumSum, nil
20
+}
0 21
new file mode 100644
... ...
@@ -0,0 +1,169 @@
0
+package stats
1
+
2
+// Float64Data is a named type for []float64 with helper methods
3
+type Float64Data []float64
4
+
5
+// Get item in slice
6
+func (f Float64Data) Get(i int) float64 { return f[i] }
7
+
8
+// Len returns length of slice
9
+func (f Float64Data) Len() int { return len(f) }
10
+
11
+// Less returns if one number is less than another
12
+func (f Float64Data) Less(i, j int) bool { return f[i] < f[j] }
13
+
14
+// Swap switches out two numbers in slice
15
+func (f Float64Data) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
16
+
17
+// Min returns the minimum number in the data
18
+func (f Float64Data) Min() (float64, error) { return Min(f) }
19
+
20
+// Max returns the maximum number in the data
21
+func (f Float64Data) Max() (float64, error) { return Max(f) }
22
+
23
+// Sum returns the total of all the numbers in the data
24
+func (f Float64Data) Sum() (float64, error) { return Sum(f) }
25
+
26
+// CumulativeSum returns the cumulative sum of the data
27
+func (f Float64Data) CumulativeSum() ([]float64, error) { return CumulativeSum(f) }
28
+
29
+// Mean returns the mean of the data
30
+func (f Float64Data) Mean() (float64, error) { return Mean(f) }
31
+
32
+// Median returns the median of the data
33
+func (f Float64Data) Median() (float64, error) { return Median(f) }
34
+
35
+// Mode returns the mode of the data
36
+func (f Float64Data) Mode() ([]float64, error) { return Mode(f) }
37
+
38
+// GeometricMean returns the median of the data
39
+func (f Float64Data) GeometricMean() (float64, error) { return GeometricMean(f) }
40
+
41
+// HarmonicMean returns the mode of the data
42
+func (f Float64Data) HarmonicMean() (float64, error) { return HarmonicMean(f) }
43
+
44
+// MedianAbsoluteDeviation the median of the absolute deviations from the dataset median
45
+func (f Float64Data) MedianAbsoluteDeviation() (float64, error) {
46
+	return MedianAbsoluteDeviation(f)
47
+}
48
+
49
+// MedianAbsoluteDeviationPopulation finds the median of the absolute deviations from the population median
50
+func (f Float64Data) MedianAbsoluteDeviationPopulation() (float64, error) {
51
+	return MedianAbsoluteDeviationPopulation(f)
52
+}
53
+
54
+// StandardDeviation the amount of variation in the dataset
55
+func (f Float64Data) StandardDeviation() (float64, error) {
56
+	return StandardDeviation(f)
57
+}
58
+
59
+// StandardDeviationPopulation finds the amount of variation from the population
60
+func (f Float64Data) StandardDeviationPopulation() (float64, error) {
61
+	return StandardDeviationPopulation(f)
62
+}
63
+
64
+// StandardDeviationSample finds the amount of variation from a sample
65
+func (f Float64Data) StandardDeviationSample() (float64, error) {
66
+	return StandardDeviationSample(f)
67
+}
68
+
69
+// QuartileOutliers finds the mild and extreme outliers
70
+func (f Float64Data) QuartileOutliers() (Outliers, error) {
71
+	return QuartileOutliers(f)
72
+}
73
+
74
+// Percentile finds the relative standing in a slice of floats
75
+func (f Float64Data) Percentile(p float64) (float64, error) {
76
+	return Percentile(f, p)
77
+}
78
+
79
+// PercentileNearestRank finds the relative standing using the Nearest Rank method
80
+func (f Float64Data) PercentileNearestRank(p float64) (float64, error) {
81
+	return PercentileNearestRank(f, p)
82
+}
83
+
84
+// Correlation describes the degree of relationship between two sets of data
85
+func (f Float64Data) Correlation(d Float64Data) (float64, error) {
86
+	return Correlation(f, d)
87
+}
88
+
89
+// AutoCorrelation is the correlation of a signal with a delayed copy of itself as a function of delay
90
+func (f Float64Data) AutoCorrelation(lags int) (float64, error) {
91
+	return AutoCorrelation(f, lags)
92
+}
93
+
94
+// Pearson calculates the Pearson product-moment correlation coefficient between two variables.
95
+func (f Float64Data) Pearson(d Float64Data) (float64, error) {
96
+	return Pearson(f, d)
97
+}
98
+
99
+// Quartile returns the three quartile points from a slice of data
100
+func (f Float64Data) Quartile(d Float64Data) (Quartiles, error) {
101
+	return Quartile(d)
102
+}
103
+
104
+// InterQuartileRange finds the range between Q1 and Q3
105
+func (f Float64Data) InterQuartileRange() (float64, error) {
106
+	return InterQuartileRange(f)
107
+}
108
+
109
+// Midhinge finds the average of the first and third quartiles
110
+func (f Float64Data) Midhinge(d Float64Data) (float64, error) {
111
+	return Midhinge(d)
112
+}
113
+
114
+// Trimean finds the average of the median and the midhinge
115
+func (f Float64Data) Trimean(d Float64Data) (float64, error) {
116
+	return Trimean(d)
117
+}
118
+
119
+// Sample returns sample from input with replacement or without
120
+func (f Float64Data) Sample(n int, r bool) ([]float64, error) {
121
+	return Sample(f, n, r)
122
+}
123
+
124
+// Variance the amount of variation in the dataset
125
+func (f Float64Data) Variance() (float64, error) {
126
+	return Variance(f)
127
+}
128
+
129
+// PopulationVariance finds the amount of variance within a population
130
+func (f Float64Data) PopulationVariance() (float64, error) {
131
+	return PopulationVariance(f)
132
+}
133
+
134
+// SampleVariance finds the amount of variance within a sample
135
+func (f Float64Data) SampleVariance() (float64, error) {
136
+	return SampleVariance(f)
137
+}
138
+
139
+// Covariance is a measure of how much two sets of data change
140
+func (f Float64Data) Covariance(d Float64Data) (float64, error) {
141
+	return Covariance(f, d)
142
+}
143
+
144
+// CovariancePopulation computes covariance for entire population between two variables
145
+func (f Float64Data) CovariancePopulation(d Float64Data) (float64, error) {
146
+	return CovariancePopulation(f, d)
147
+}
148
+
149
+// Sigmoid returns the input values along the sigmoid or s-shaped curve
150
+func (f Float64Data) Sigmoid() ([]float64, error) {
151
+	return Sigmoid(f)
152
+}
153
+
154
+// SoftMax returns the input values in the range of 0 to 1
155
+// with sum of all the probabilities being equal to one.
156
+func (f Float64Data) SoftMax() ([]float64, error) {
157
+	return SoftMax(f)
158
+}
159
+
160
+// Entropy provides calculation of the entropy
161
+func (f Float64Data) Entropy() (float64, error) {
162
+	return Entropy(f)
163
+}
164
+
165
+// Quartiles returns the three quartile points from instance of Float64Data
166
+func (f Float64Data) Quartiles() (Quartiles, error) {
167
+	return Quartile(f)
168
+}
0 169
new file mode 100644
... ...
@@ -0,0 +1,81 @@
0
+package stats
1
+
2
+import "fmt"
3
+
4
+// Holds information about the dataset provided to Describe
5
+type Description struct {
6
+	Count                  int
7
+	Mean                   float64
8
+	Std                    float64
9
+	Max                    float64
10
+	Min                    float64
11
+	DescriptionPercentiles []descriptionPercentile
12
+	AllowedNaN             bool
13
+}
14
+
15
+// Specifies percentiles to be computed
16
+type descriptionPercentile struct {
17
+	Percentile float64
18
+	Value      float64
19
+}
20
+
21
+// Describe generates descriptive statistics about a provided dataset, similar to python's pandas.describe()
22
+func Describe(input Float64Data, allowNaN bool, percentiles *[]float64) (*Description, error) {
23
+	return DescribePercentileFunc(input, allowNaN, percentiles, Percentile)
24
+}
25
+
26
+// Describe generates descriptive statistics about a provided dataset, similar to python's pandas.describe()
27
+// Takes in a function to use for percentile calculation
28
+func DescribePercentileFunc(input Float64Data, allowNaN bool, percentiles *[]float64, percentileFunc func(Float64Data, float64) (float64, error)) (*Description, error) {
29
+	var description Description
30
+	description.AllowedNaN = allowNaN
31
+	description.Count = input.Len()
32
+
33
+	if description.Count == 0 && !allowNaN {
34
+		return &description, ErrEmptyInput
35
+	}
36
+
37
+	// Disregard error, since it cannot be thrown if Count is > 0 and allowNaN is false, else NaN is accepted
38
+	description.Std, _ = StandardDeviation(input)
39
+	description.Max, _ = Max(input)
40
+	description.Min, _ = Min(input)
41
+	description.Mean, _ = Mean(input)
42
+
43
+	if percentiles != nil {
44
+		for _, percentile := range *percentiles {
45
+			if value, err := percentileFunc(input, percentile); err == nil || allowNaN {
46
+				description.DescriptionPercentiles = append(description.DescriptionPercentiles, descriptionPercentile{Percentile: percentile, Value: value})
47
+			}
48
+		}
49
+	}
50
+
51
+	return &description, nil
52
+}
53
+
54
+/*
55
+Represents the Description instance in a string format with specified number of decimals
56
+
57
+	count   3
58
+	mean    2.00
59
+	std     0.82
60
+	max     3.00
61
+	min     1.00
62
+	25.00%  NaN
63
+	50.00%  1.50
64
+	75.00%  2.50
65
+	NaN OK  true
66
+*/
67
+func (d *Description) String(decimals int) string {
68
+	var str string
69
+
70
+	str += fmt.Sprintf("count\t%d\n", d.Count)
71
+	str += fmt.Sprintf("mean\t%.*f\n", decimals, d.Mean)
72
+	str += fmt.Sprintf("std\t%.*f\n", decimals, d.Std)
73
+	str += fmt.Sprintf("max\t%.*f\n", decimals, d.Max)
74
+	str += fmt.Sprintf("min\t%.*f\n", decimals, d.Min)
75
+	for _, percentile := range d.DescriptionPercentiles {
76
+		str += fmt.Sprintf("%.2f%%\t%.*f\n", percentile.Percentile, decimals, percentile.Value)
77
+	}
78
+	str += fmt.Sprintf("NaN OK\t%t", d.AllowedNaN)
79
+	return str
80
+}
0 81
new file mode 100644
... ...
@@ -0,0 +1,57 @@
0
+package stats
1
+
2
+import "math"
3
+
4
+// MedianAbsoluteDeviation finds the median of the absolute deviations from the dataset median
5
+func MedianAbsoluteDeviation(input Float64Data) (mad float64, err error) {
6
+	return MedianAbsoluteDeviationPopulation(input)
7
+}
8
+
9
+// MedianAbsoluteDeviationPopulation finds the median of the absolute deviations from the population median
10
+func MedianAbsoluteDeviationPopulation(input Float64Data) (mad float64, err error) {
11
+	if input.Len() == 0 {
12
+		return math.NaN(), EmptyInputErr
13
+	}
14
+
15
+	i := copyslice(input)
16
+	m, _ := Median(i)
17
+
18
+	for key, value := range i {
19
+		i[key] = math.Abs(value - m)
20
+	}
21
+
22
+	return Median(i)
23
+}
24
+
25
+// StandardDeviation the amount of variation in the dataset
26
+func StandardDeviation(input Float64Data) (sdev float64, err error) {
27
+	return StandardDeviationPopulation(input)
28
+}
29
+
30
+// StandardDeviationPopulation finds the amount of variation from the population
31
+func StandardDeviationPopulation(input Float64Data) (sdev float64, err error) {
32
+
33
+	if input.Len() == 0 {
34
+		return math.NaN(), EmptyInputErr
35
+	}
36
+
37
+	// Get the population variance
38
+	vp, _ := PopulationVariance(input)
39
+
40
+	// Return the population standard deviation
41
+	return math.Sqrt(vp), nil
42
+}
43
+
44
+// StandardDeviationSample finds the amount of variation from a sample
45
+func StandardDeviationSample(input Float64Data) (sdev float64, err error) {
46
+
47
+	if input.Len() == 0 {
48
+		return math.NaN(), EmptyInputErr
49
+	}
50
+
51
+	// Get the sample variance
52
+	vs, _ := SampleVariance(input)
53
+
54
+	// Return the sample standard deviation
55
+	return math.Sqrt(vs), nil
56
+}
0 57
new file mode 100644
... ...
@@ -0,0 +1,91 @@
0
+package stats
1
+
2
+import (
3
+	"math"
4
+)
5
+
6
+// Validate data for distance calculation
7
+func validateData(dataPointX, dataPointY Float64Data) error {
8
+	if len(dataPointX) == 0 || len(dataPointY) == 0 {
9
+		return EmptyInputErr
10
+	}
11
+
12
+	if len(dataPointX) != len(dataPointY) {
13
+		return SizeErr
14
+	}
15
+	return nil
16
+}
17
+
18
+// ChebyshevDistance computes the Chebyshev distance between two data sets
19
+func ChebyshevDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {
20
+	err = validateData(dataPointX, dataPointY)
21
+	if err != nil {
22
+		return math.NaN(), err
23
+	}
24
+	var tempDistance float64
25
+	for i := 0; i < len(dataPointY); i++ {
26
+		tempDistance = math.Abs(dataPointX[i] - dataPointY[i])
27
+		if distance < tempDistance {
28
+			distance = tempDistance
29
+		}
30
+	}
31
+	return distance, nil
32
+}
33
+
34
+// EuclideanDistance computes the Euclidean distance between two data sets
35
+func EuclideanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {
36
+
37
+	err = validateData(dataPointX, dataPointY)
38
+	if err != nil {
39
+		return math.NaN(), err
40
+	}
41
+	distance = 0
42
+	for i := 0; i < len(dataPointX); i++ {
43
+		distance = distance + ((dataPointX[i] - dataPointY[i]) * (dataPointX[i] - dataPointY[i]))
44
+	}
45
+	return math.Sqrt(distance), nil
46
+}
47
+
48
+// ManhattanDistance computes the Manhattan distance between two data sets
49
+func ManhattanDistance(dataPointX, dataPointY Float64Data) (distance float64, err error) {
50
+	err = validateData(dataPointX, dataPointY)
51
+	if err != nil {
52
+		return math.NaN(), err
53
+	}
54
+	distance = 0
55
+	for i := 0; i < len(dataPointX); i++ {
56
+		distance = distance + math.Abs(dataPointX[i]-dataPointY[i])
57
+	}
58
+	return distance, nil
59
+}
60
+
61
+// MinkowskiDistance computes the Minkowski distance between two data sets
62
+//
63
+// Arguments:
64
+//
65
+//	dataPointX: First set of data points
66
+//	dataPointY: Second set of data points. Length of both data
67
+//	            sets must be equal.
68
+//	lambda:     aka p or city blocks; With lambda = 1
69
+//	            returned distance is manhattan distance and
70
+//	            lambda = 2; it is euclidean distance. Lambda
71
+//	            reaching to infinite - distance would be chebysev
72
+//	            distance.
73
+//
74
+// Return:
75
+//
76
+//	Distance or error
77
+func MinkowskiDistance(dataPointX, dataPointY Float64Data, lambda float64) (distance float64, err error) {
78
+	err = validateData(dataPointX, dataPointY)
79
+	if err != nil {
80
+		return math.NaN(), err
81
+	}
82
+	for i := 0; i < len(dataPointY); i++ {
83
+		distance = distance + math.Pow(math.Abs(dataPointX[i]-dataPointY[i]), lambda)
84
+	}
85
+	distance = math.Pow(distance, 1/lambda)
86
+	if math.IsInf(distance, 1) {
87
+		return math.NaN(), InfValue
88
+	}
89
+	return distance, nil
90
+}
0 91
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+/*
1
+Package stats is a well tested and comprehensive
2
+statistics library package with no dependencies.
3
+
4
+Example Usage:
5
+
6
+	// start with some source data to use
7
+	data := []float64{1.0, 2.1, 3.2, 4.823, 4.1, 5.8}
8
+
9
+	// you could also use different types like this
10
+	// data := stats.LoadRawData([]int{1, 2, 3, 4, 5})
11
+	// data := stats.LoadRawData([]interface{}{1.1, "2", 3})
12
+	// etc...
13
+
14
+	median, _ := stats.Median(data)
15
+	fmt.Println(median) // 3.65
16
+
17
+	roundedMedian, _ := stats.Round(median, 0)
18
+	fmt.Println(roundedMedian) // 4
19
+
20
+MIT License Copyright (c) 2014-2020 Montana Flynn (https://montanaflynn.com)
21
+*/
22
+package stats
0 23
new file mode 100644
... ...
@@ -0,0 +1,31 @@
0
+package stats
1
+
2
+import "math"
3
+
4
+// Entropy provides calculation of the entropy
5
+func Entropy(input Float64Data) (float64, error) {
6
+	input, err := normalize(input)
7
+	if err != nil {
8
+		return math.NaN(), err
9
+	}
10
+	var result float64
11
+	for i := 0; i < input.Len(); i++ {
12
+		v := input.Get(i)
13
+		if v == 0 {
14
+			continue
15
+		}
16
+		result += (v * math.Log(v))
17
+	}
18
+	return -result, nil
19
+}
20
+
21
+func normalize(input Float64Data) (Float64Data, error) {
22
+	sum, err := input.Sum()
23
+	if err != nil {
24
+		return Float64Data{}, err
25
+	}
26
+	for i := 0; i < input.Len(); i++ {
27
+		input[i] = input[i] / sum
28
+	}
29
+	return input, nil
30
+}
0 31
new file mode 100644
... ...
@@ -0,0 +1,35 @@
0
+package stats
1
+
2
+type statsError struct {
3
+	err string
4
+}
5
+
6
+func (s statsError) Error() string {
7
+	return s.err
8
+}
9
+
10
+func (s statsError) String() string {
11
+	return s.err
12
+}
13
+
14
+// These are the package-wide error values.
15
+// All error identification should use these values.
16
+// https://github.com/golang/go/wiki/Errors#naming
17
+var (
18
+	// ErrEmptyInput Input must not be empty
19
+	ErrEmptyInput = statsError{"Input must not be empty."}
20
+	// ErrNaN Not a number
21
+	ErrNaN = statsError{"Not a number."}
22
+	// ErrNegative Must not contain negative values
23
+	ErrNegative = statsError{"Must not contain negative values."}
24
+	// ErrZero Must not contain zero values
25
+	ErrZero = statsError{"Must not contain zero values."}
26
+	// ErrBounds Input is outside of range
27
+	ErrBounds = statsError{"Input is outside of range."}
28
+	// ErrSize Must be the same length
29
+	ErrSize = statsError{"Must be the same length."}
30
+	// ErrInfValue Value is infinite
31
+	ErrInfValue = statsError{"Value is infinite."}
32
+	// ErrYCoord Y Value must be greater than zero
33
+	ErrYCoord = statsError{"Y Value must be greater than zero."}
34
+)
0 35
new file mode 100644
... ...
@@ -0,0 +1,42 @@
0
+package stats
1
+
2
+import (
3
+	"math"
4
+)
5
+
6
+// ProbGeom generates the probability for a geometric random variable
7
+// with parameter p to achieve success in the interval of [a, b] trials
8
+// See https://en.wikipedia.org/wiki/Geometric_distribution for more information
9
+func ProbGeom(a int, b int, p float64) (prob float64, err error) {
10
+	if (a > b) || (a < 1) {
11
+		return math.NaN(), ErrBounds
12
+	}
13
+
14
+	prob = 0
15
+	q := 1 - p // probability of failure
16
+
17
+	for k := a + 1; k <= b; k++ {
18
+		prob = prob + p*math.Pow(q, float64(k-1))
19
+	}
20
+
21
+	return prob, nil
22
+}
23
+
24
+// ProbGeom generates the expectation or average number of trials
25
+// for a geometric random variable with parameter p
26
+func ExpGeom(p float64) (exp float64, err error) {
27
+	if (p > 1) || (p < 0) {
28
+		return math.NaN(), ErrNegative
29
+	}
30
+
31
+	return 1 / p, nil
32
+}
33
+
34
+// ProbGeom generates the variance for number for a
35
+// geometric random variable with parameter p
36
+func VarGeom(p float64) (exp float64, err error) {
37
+	if (p > 1) || (p < 0) {
38
+		return math.NaN(), ErrNegative
39
+	}
40
+	return (1 - p) / math.Pow(p, 2), nil
41
+}
0 42
new file mode 100644
... ...
@@ -0,0 +1,49 @@
0
+package stats
1
+
2
+// VarP is a shortcut to PopulationVariance
3
+func VarP(input Float64Data) (sdev float64, err error) {
4
+	return PopulationVariance(input)
5
+}
6
+
7
+// VarS is a shortcut to SampleVariance
8
+func VarS(input Float64Data) (sdev float64, err error) {
9
+	return SampleVariance(input)
10
+}
11
+
12
+// StdDevP is a shortcut to StandardDeviationPopulation
13
+func StdDevP(input Float64Data) (sdev float64, err error) {
14
+	return StandardDeviationPopulation(input)
15
+}
16
+
17
+// StdDevS is a shortcut to StandardDeviationSample
18
+func StdDevS(input Float64Data) (sdev float64, err error) {
19
+	return StandardDeviationSample(input)
20
+}
21
+
22
+// LinReg is a shortcut to LinearRegression
23
+func LinReg(s []Coordinate) (regressions []Coordinate, err error) {
24
+	return LinearRegression(s)
25
+}
26
+
27
+// ExpReg is a shortcut to ExponentialRegression
28
+func ExpReg(s []Coordinate) (regressions []Coordinate, err error) {
29
+	return ExponentialRegression(s)
30
+}
31
+
32
+// LogReg is a shortcut to LogarithmicRegression
33
+func LogReg(s []Coordinate) (regressions []Coordinate, err error) {
34
+	return LogarithmicRegression(s)
35
+}
36
+
37
+// Legacy error names that didn't start with Err
38
+var (
39
+	EmptyInputErr = ErrEmptyInput
40
+	NaNErr        = ErrNaN
41
+	NegativeErr   = ErrNegative
42
+	ZeroErr       = ErrZero
43
+	BoundsErr     = ErrBounds
44
+	SizeErr       = ErrSize
45
+	InfValue      = ErrInfValue
46
+	YCoordErr     = ErrYCoord
47
+	EmptyInput    = ErrEmptyInput
48
+)
0 49
new file mode 100644
... ...
@@ -0,0 +1,199 @@
0
+package stats
1
+
2
+import (
3
+	"bufio"
4
+	"io"
5
+	"strconv"
6
+	"strings"
7
+	"time"
8
+)
9
+
10
+// LoadRawData parses and converts a slice of mixed data types to floats
11
+func LoadRawData(raw interface{}) (f Float64Data) {
12
+	var r []interface{}
13
+	var s Float64Data
14
+
15
+	switch t := raw.(type) {
16
+	case []interface{}:
17
+		r = t
18
+	case []uint:
19
+		for _, v := range t {
20
+			s = append(s, float64(v))
21
+		}
22
+		return s
23
+	case []uint8:
24
+		for _, v := range t {
25
+			s = append(s, float64(v))
26
+		}
27
+		return s
28
+	case []uint16:
29
+		for _, v := range t {
30
+			s = append(s, float64(v))
31
+		}
32
+		return s
33
+	case []uint32:
34
+		for _, v := range t {
35
+			s = append(s, float64(v))
36
+		}
37
+		return s
38
+	case []uint64:
39
+		for _, v := range t {
40
+			s = append(s, float64(v))
41
+		}
42
+		return s
43
+	case []bool:
44
+		for _, v := range t {
45
+			if v {
46
+				s = append(s, 1.0)
47
+			} else {
48
+				s = append(s, 0.0)
49
+			}
50
+		}
51
+		return s
52
+	case []float64:
53
+		return Float64Data(t)
54
+	case []int:
55
+		for _, v := range t {
56
+			s = append(s, float64(v))
57
+		}
58
+		return s
59
+	case []int8:
60
+		for _, v := range t {
61
+			s = append(s, float64(v))
62
+		}
63
+		return s
64
+	case []int16:
65
+		for _, v := range t {
66
+			s = append(s, float64(v))
67
+		}
68
+		return s
69
+	case []int32:
70
+		for _, v := range t {
71
+			s = append(s, float64(v))
72
+		}
73
+		return s
74
+	case []int64:
75
+		for _, v := range t {
76
+			s = append(s, float64(v))
77
+		}
78
+		return s
79
+	case []string:
80
+		for _, v := range t {
81
+			r = append(r, v)
82
+		}
83
+	case []time.Duration:
84
+		for _, v := range t {
85
+			r = append(r, v)
86
+		}
87
+	case map[int]int:
88
+		for i := 0; i < len(t); i++ {
89
+			s = append(s, float64(t[i]))
90
+		}
91
+		return s
92
+	case map[int]int8:
93
+		for i := 0; i < len(t); i++ {
94
+			s = append(s, float64(t[i]))
95
+		}
96
+		return s
97
+	case map[int]int16:
98
+		for i := 0; i < len(t); i++ {
99
+			s = append(s, float64(t[i]))
100
+		}
101
+		return s
102
+	case map[int]int32:
103
+		for i := 0; i < len(t); i++ {
104
+			s = append(s, float64(t[i]))
105
+		}
106
+		return s
107
+	case map[int]int64:
108
+		for i := 0; i < len(t); i++ {
109
+			s = append(s, float64(t[i]))
110
+		}
111
+		return s
112
+	case map[int]string:
113
+		for i := 0; i < len(t); i++ {
114
+			r = append(r, t[i])
115
+		}
116
+	case map[int]uint:
117
+		for i := 0; i < len(t); i++ {
118
+			s = append(s, float64(t[i]))
119
+		}
120
+		return s
121
+	case map[int]uint8:
122
+		for i := 0; i < len(t); i++ {
123
+			s = append(s, float64(t[i]))
124
+		}
125
+		return s
126
+	case map[int]uint16:
127
+		for i := 0; i < len(t); i++ {
128
+			s = append(s, float64(t[i]))
129
+		}
130
+		return s
131
+	case map[int]uint32:
132
+		for i := 0; i < len(t); i++ {
133
+			s = append(s, float64(t[i]))
134
+		}
135
+		return s
136
+	case map[int]uint64:
137
+		for i := 0; i < len(t); i++ {
138
+			s = append(s, float64(t[i]))
139
+		}
140
+		return s
141
+	case map[int]bool:
142
+		for i := 0; i < len(t); i++ {
143
+			if t[i] {
144
+				s = append(s, 1.0)
145
+			} else {
146
+				s = append(s, 0.0)
147
+			}
148
+		}
149
+		return s
150
+	case map[int]float64:
151
+		for i := 0; i < len(t); i++ {
152
+			s = append(s, t[i])
153
+		}
154
+		return s
155
+	case map[int]time.Duration:
156
+		for i := 0; i < len(t); i++ {
157
+			r = append(r, t[i])
158
+		}
159
+	case string:
160
+		for _, v := range strings.Fields(t) {
161
+			r = append(r, v)
162
+		}
163
+	case io.Reader:
164
+		scanner := bufio.NewScanner(t)
165
+		for scanner.Scan() {
166
+			l := scanner.Text()
167
+			for _, v := range strings.Fields(l) {
168
+				r = append(r, v)
169
+			}
170
+		}
171
+	}
172
+
173
+	for _, v := range r {
174
+		switch t := v.(type) {
175
+		case int:
176
+			a := float64(t)
177
+			f = append(f, a)
178
+		case uint:
179
+			f = append(f, float64(t))
180
+		case float64:
181
+			f = append(f, t)
182
+		case string:
183
+			fl, err := strconv.ParseFloat(t, 64)
184
+			if err == nil {
185
+				f = append(f, fl)
186
+			}
187
+		case bool:
188
+			if t {
189
+				f = append(f, 1.0)
190
+			} else {
191
+				f = append(f, 0.0)
192
+			}
193
+		case time.Duration:
194
+			f = append(f, float64(t))
195
+		}
196
+	}
197
+	return f
198
+}
0 199
new file mode 100644
... ...
@@ -0,0 +1,26 @@
0
+package stats
1
+
2
+import (
3
+	"math"
4
+)
5
+
6
+// Max finds the highest number in a slice
7
+func Max(input Float64Data) (max float64, err error) {
8
+
9
+	// Return an error if there are no numbers
10
+	if input.Len() == 0 {
11
+		return math.NaN(), EmptyInputErr
12
+	}
13
+
14
+	// Get the first value as the starting point
15
+	max = input.Get(0)
16
+
17
+	// Loop and replace higher values
18
+	for i := 1; i < input.Len(); i++ {
19
+		if input.Get(i) > max {
20
+			max = input.Get(i)
21
+		}
22
+	}
23
+
24
+	return max, nil
25
+}
0 26
new file mode 100644
... ...
@@ -0,0 +1,60 @@
0
+package stats
1
+
2
+import "math"
3
+
4
+// Mean gets the average of a slice of numbers
5
+func Mean(input Float64Data) (float64, error) {
6
+
7
+	if input.Len() == 0 {
8
+		return math.NaN(), EmptyInputErr
9
+	}
10
+
11
+	sum, _ := input.Sum()
12
+
13
+	return sum / float64(input.Len()), nil
14
+}
15
+
16
+// GeometricMean gets the geometric mean for a slice of numbers
17
+func GeometricMean(input Float64Data) (float64, error) {
18
+
19
+	l := input.Len()
20
+	if l == 0 {
21
+		return math.NaN(), EmptyInputErr
22
+	}
23
+
24
+	// Get the product of all the numbers
25
+	var p float64
26
+	for _, n := range input {
27
+		if p == 0 {
28
+			p = n
29
+		} else {
30
+			p *= n
31
+		}
32
+	}
33
+
34
+	// Calculate the geometric mean
35
+	return math.Pow(p, 1/float64(l)), nil
36
+}
37
+
38
+// HarmonicMean gets the harmonic mean for a slice of numbers
39
+func HarmonicMean(input Float64Data) (float64, error) {
40
+
41
+	l := input.Len()
42
+	if l == 0 {
43
+		return math.NaN(), EmptyInputErr
44
+	}
45
+
46
+	// Get the sum of all the numbers reciprocals and return an
47
+	// error for values that cannot be included in harmonic mean
48
+	var p float64
49
+	for _, n := range input {
50
+		if n < 0 {
51
+			return math.NaN(), NegativeErr
52
+		} else if n == 0 {
53
+			return math.NaN(), ZeroErr
54
+		}
55
+		p += (1 / n)
56
+	}
57
+
58
+	return float64(l) / p, nil
59
+}
0 60
new file mode 100644
... ...
@@ -0,0 +1,25 @@
0
+package stats
1
+
2
+import "math"
3
+
4
+// Median gets the median number in a slice of numbers
5
+func Median(input Float64Data) (median float64, err error) {
6
+
7
+	// Start by sorting a copy of the slice
8
+	c := sortedCopy(input)
9
+
10
+	// No math is needed if there are no numbers
11
+	// For even numbers we add the two middle numbers
12
+	// and divide by two using the mean function above
13
+	// For odd numbers we just use the middle number
14
+	l := len(c)
15
+	if l == 0 {
16
+		return math.NaN(), EmptyInputErr
17
+	} else if l%2 == 0 {
18
+		median, _ = Mean(c[l/2-1 : l/2+1])
19
+	} else {
20
+		median = c[l/2]
21
+	}
22
+
23
+	return median, nil
24
+}
0 25
new file mode 100644
... ...
@@ -0,0 +1,26 @@
0
+package stats
1
+
2
+import "math"
3
+
4
+// Min finds the lowest number in a set of data
5
+func Min(input Float64Data) (min float64, err error) {
6
+
7
+	// Get the count of numbers in the slice
8
+	l := input.Len()
9
+
10
+	// Return an error if there are no numbers
11
+	if l == 0 {
12
+		return math.NaN(), EmptyInputErr
13
+	}
14
+
15
+	// Get the first value as the starting point
16
+	min = input.Get(0)
17
+
18
+	// Iterate until done checking for a lower value
19
+	for i := 1; i < l; i++ {
20
+		if input.Get(i) < min {
21
+			min = input.Get(i)
22
+		}
23
+	}
24
+	return min, nil
25
+}
0 26
new file mode 100644
... ...
@@ -0,0 +1,47 @@
0
+package stats
1
+
2
+// Mode gets the mode [most frequent value(s)] of a slice of float64s
3
+func Mode(input Float64Data) (mode []float64, err error) {
4
+	// Return the input if there's only one number
5
+	l := input.Len()
6
+	if l == 1 {
7
+		return input, nil
8
+	} else if l == 0 {
9
+		return nil, EmptyInputErr
10
+	}
11
+
12
+	c := sortedCopyDif(input)
13
+	// Traverse sorted array,
14
+	// tracking the longest repeating sequence
15
+	mode = make([]float64, 5)
16
+	cnt, maxCnt := 1, 1
17
+	for i := 1; i < l; i++ {
18
+		switch {
19
+		case c[i] == c[i-1]:
20
+			cnt++
21
+		case cnt == maxCnt && maxCnt != 1:
22
+			mode = append(mode, c[i-1])
23
+			cnt = 1
24
+		case cnt > maxCnt:
25
+			mode = append(mode[:0], c[i-1])
26
+			maxCnt, cnt = cnt, 1
27
+		default:
28
+			cnt = 1
29
+		}
30
+	}
31
+	switch {
32
+	case cnt == maxCnt:
33
+		mode = append(mode, c[l-1])
34
+	case cnt > maxCnt:
35
+		mode = append(mode[:0], c[l-1])
36
+		maxCnt = cnt
37
+	}
38
+
39
+	// Since length must be greater than 1,
40
+	// check for slices of distinct values
41
+	if maxCnt == 1 || len(mode)*maxCnt == l && maxCnt != l {
42
+		return Float64Data{}, nil
43
+	}
44
+
45
+	return mode, nil
46
+}
0 47
new file mode 100644
... ...
@@ -0,0 +1,254 @@
0
+package stats
1
+
2
+import (
3
+	"math"
4
+	"math/rand"
5
+	"strings"
6
+	"time"
7
+)
8
+
9
+// NormPpfRvs generates random variates using the Point Percentile Function.
10
+// For more information please visit: https://demonstrations.wolfram.com/TheMethodOfInverseTransforms/
11
+func NormPpfRvs(loc float64, scale float64, size int) []float64 {
12
+	rand.Seed(time.Now().UnixNano())
13
+	var toReturn []float64
14
+	for i := 0; i < size; i++ {
15
+		toReturn = append(toReturn, NormPpf(rand.Float64(), loc, scale))
16
+	}
17
+	return toReturn
18
+}
19
+
20
+// NormBoxMullerRvs generates random variates using the Box–Muller transform.
21
+// For more information please visit: http://mathworld.wolfram.com/Box-MullerTransformation.html
22
+func NormBoxMullerRvs(loc float64, scale float64, size int) []float64 {
23
+	rand.Seed(time.Now().UnixNano())
24
+	var toReturn []float64
25
+	for i := 0; i < int(float64(size/2)+float64(size%2)); i++ {
26
+		// u1 and u2 are uniformly distributed random numbers between 0 and 1.
27
+		u1 := rand.Float64()
28
+		u2 := rand.Float64()
29
+		// x1 and x2 are normally distributed random numbers.
30
+		x1 := loc + (scale * (math.Sqrt(-2*math.Log(u1)) * math.Cos(2*math.Pi*u2)))
31
+		toReturn = append(toReturn, x1)
32
+		if (i+1)*2 <= size {
33
+			x2 := loc + (scale * (math.Sqrt(-2*math.Log(u1)) * math.Sin(2*math.Pi*u2)))
34
+			toReturn = append(toReturn, x2)
35
+		}
36
+	}
37
+	return toReturn
38
+}
39
+
40
+// NormPdf is the probability density function.
41
+func NormPdf(x float64, loc float64, scale float64) float64 {
42
+	return (math.Pow(math.E, -(math.Pow(x-loc, 2))/(2*math.Pow(scale, 2)))) / (scale * math.Sqrt(2*math.Pi))
43
+}
44
+
45
+// NormLogPdf is the log of the probability density function.
46
+func NormLogPdf(x float64, loc float64, scale float64) float64 {
47
+	return math.Log((math.Pow(math.E, -(math.Pow(x-loc, 2))/(2*math.Pow(scale, 2)))) / (scale * math.Sqrt(2*math.Pi)))
48
+}
49
+
50
+// NormCdf is the cumulative distribution function.
51
+func NormCdf(x float64, loc float64, scale float64) float64 {
52
+	return 0.5 * (1 + math.Erf((x-loc)/(scale*math.Sqrt(2))))
53
+}
54
+
55
+// NormLogCdf is the log of the cumulative distribution function.
56
+func NormLogCdf(x float64, loc float64, scale float64) float64 {
57
+	return math.Log(0.5 * (1 + math.Erf((x-loc)/(scale*math.Sqrt(2)))))
58
+}
59
+
60
+// NormSf is the survival function (also defined as 1 - cdf, but sf is sometimes more accurate).
61
+func NormSf(x float64, loc float64, scale float64) float64 {
62
+	return 1 - 0.5*(1+math.Erf((x-loc)/(scale*math.Sqrt(2))))
63
+}
64
+
65
+// NormLogSf is the log of the survival function.
66
+func NormLogSf(x float64, loc float64, scale float64) float64 {
67
+	return math.Log(1 - 0.5*(1+math.Erf((x-loc)/(scale*math.Sqrt(2)))))
68
+}
69
+
70
+// NormPpf is the point percentile function.
71
+// This is based on Peter John Acklam's inverse normal CDF.
72
+// algorithm: http://home.online.no/~pjacklam/notes/invnorm/ (no longer visible).
73
+// For more information please visit: https://stackedboxes.org/2017/05/01/acklams-normal-quantile-function/
74
+func NormPpf(p float64, loc float64, scale float64) (x float64) {
75
+	const (
76
+		a1 = -3.969683028665376e+01
77
+		a2 = 2.209460984245205e+02
78
+		a3 = -2.759285104469687e+02
79
+		a4 = 1.383577518672690e+02
80
+		a5 = -3.066479806614716e+01
81
+		a6 = 2.506628277459239e+00
82
+
83
+		b1 = -5.447609879822406e+01
84
+		b2 = 1.615858368580409e+02
85
+		b3 = -1.556989798598866e+02
86
+		b4 = 6.680131188771972e+01
87
+		b5 = -1.328068155288572e+01
88
+
89
+		c1 = -7.784894002430293e-03
90
+		c2 = -3.223964580411365e-01
91
+		c3 = -2.400758277161838e+00
92
+		c4 = -2.549732539343734e+00
93
+		c5 = 4.374664141464968e+00
94
+		c6 = 2.938163982698783e+00
95
+
96
+		d1 = 7.784695709041462e-03
97
+		d2 = 3.224671290700398e-01
98
+		d3 = 2.445134137142996e+00
99
+		d4 = 3.754408661907416e+00
100
+
101
+		plow  = 0.02425
102
+		phigh = 1 - plow
103
+	)
104
+
105
+	if p < 0 || p > 1 {
106
+		return math.NaN()
107
+	} else if p == 0 {
108
+		return -math.Inf(0)
109
+	} else if p == 1 {
110
+		return math.Inf(0)
111
+	}
112
+
113
+	if p < plow {
114
+		q := math.Sqrt(-2 * math.Log(p))
115
+		x = (((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q + c6) /
116
+			((((d1*q+d2)*q+d3)*q+d4)*q + 1)
117
+	} else if phigh < p {
118
+		q := math.Sqrt(-2 * math.Log(1-p))
119
+		x = -(((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q + c6) /
120
+			((((d1*q+d2)*q+d3)*q+d4)*q + 1)
121
+	} else {
122
+		q := p - 0.5
123
+		r := q * q
124
+		x = (((((a1*r+a2)*r+a3)*r+a4)*r+a5)*r + a6) * q /
125
+			(((((b1*r+b2)*r+b3)*r+b4)*r+b5)*r + 1)
126
+	}
127
+
128
+	e := 0.5*math.Erfc(-x/math.Sqrt2) - p
129
+	u := e * math.Sqrt(2*math.Pi) * math.Exp(x*x/2)
130
+	x = x - u/(1+x*u/2)
131
+
132
+	return x*scale + loc
133
+}
134
+
135
+// NormIsf is the inverse survival function (inverse of sf).
136
+func NormIsf(p float64, loc float64, scale float64) (x float64) {
137
+	if -NormPpf(p, loc, scale) == 0 {
138
+		return 0
139
+	}
140
+	return -NormPpf(p, loc, scale)
141
+}
142
+
143
+// NormMoment approximates the non-central (raw) moment of order n.
144
+// For more information please visit: https://math.stackexchange.com/questions/1945448/methods-for-finding-raw-moments-of-the-normal-distribution
145
+func NormMoment(n int, loc float64, scale float64) float64 {
146
+	toReturn := 0.0
147
+	for i := 0; i < n+1; i++ {
148
+		if (n-i)%2 == 0 {
149
+			toReturn += float64(Ncr(n, i)) * (math.Pow(loc, float64(i))) * (math.Pow(scale, float64(n-i))) *
150
+				(float64(factorial(n-i)) / ((math.Pow(2.0, float64((n-i)/2))) *
151
+					float64(factorial((n-i)/2))))
152
+		}
153
+	}
154
+	return toReturn
155
+}
156
+
157
+// NormStats returns the mean, variance, skew, and/or kurtosis.
158
+// Mean(‘m’), variance(‘v’), skew(‘s’), and/or kurtosis(‘k’).
159
+// Takes string containing any of 'mvsk'.
160
+// Returns array of m v s k in that order.
161
+func NormStats(loc float64, scale float64, moments string) []float64 {
162
+	var toReturn []float64
163
+	if strings.ContainsAny(moments, "m") {
164
+		toReturn = append(toReturn, loc)
165
+	}
166
+	if strings.ContainsAny(moments, "v") {
167
+		toReturn = append(toReturn, math.Pow(scale, 2))
168
+	}
169
+	if strings.ContainsAny(moments, "s") {
170
+		toReturn = append(toReturn, 0.0)
171
+	}
172
+	if strings.ContainsAny(moments, "k") {
173
+		toReturn = append(toReturn, 0.0)
174
+	}
175
+	return toReturn
176
+}
177
+
178
+// NormEntropy is the differential entropy of the RV.
179
+func NormEntropy(loc float64, scale float64) float64 {
180
+	return math.Log(scale * math.Sqrt(2*math.Pi*math.E))
181
+}
182
+
183
+// NormFit returns the maximum likelihood estimators for the Normal Distribution.
184
+// Takes array of float64 values.
185
+// Returns array of Mean followed by Standard Deviation.
186
+func NormFit(data []float64) [2]float64 {
187
+	sum := 0.00
188
+	for i := 0; i < len(data); i++ {
189
+		sum += data[i]
190
+	}
191
+	mean := sum / float64(len(data))
192
+	stdNumerator := 0.00
193
+	for i := 0; i < len(data); i++ {
194
+		stdNumerator += math.Pow(data[i]-mean, 2)
195
+	}
196
+	return [2]float64{mean, math.Sqrt((stdNumerator) / (float64(len(data))))}
197
+}
198
+
199
+// NormMedian is the median of the distribution.
200
+func NormMedian(loc float64, scale float64) float64 {
201
+	return loc
202
+}
203
+
204
+// NormMean is the mean/expected value of the distribution.
205
+func NormMean(loc float64, scale float64) float64 {
206
+	return loc
207
+}
208
+
209
+// NormVar is the variance of the distribution.
210
+func NormVar(loc float64, scale float64) float64 {
211
+	return math.Pow(scale, 2)
212
+}
213
+
214
+// NormStd is the standard deviation of the distribution.
215
+func NormStd(loc float64, scale float64) float64 {
216
+	return scale
217
+}
218
+
219
+// NormInterval finds endpoints of the range that contains alpha percent of the distribution.
220
+func NormInterval(alpha float64, loc float64, scale float64) [2]float64 {
221
+	q1 := (1.0 - alpha) / 2
222
+	q2 := (1.0 + alpha) / 2
223
+	a := NormPpf(q1, loc, scale)
224
+	b := NormPpf(q2, loc, scale)
225
+	return [2]float64{a, b}
226
+}
227
+
228
+// factorial is the naive factorial algorithm.
229
+func factorial(x int) int {
230
+	if x == 0 {
231
+		return 1
232
+	}
233
+	return x * factorial(x-1)
234
+}
235
+
236
+// Ncr is an N choose R algorithm.
237
+// Aaron Cannon's algorithm.
238
+func Ncr(n, r int) int {
239
+	if n <= 1 || r == 0 || n == r {
240
+		return 1
241
+	}
242
+	if newR := n - r; newR < r {
243
+		r = newR
244
+	}
245
+	if r == 1 {
246
+		return n
247
+	}
248
+	ret := int(n - r + 1)
249
+	for i, j := ret+1, int(2); j <= r; i, j = i+1, j+1 {
250
+		ret = ret * i / j
251
+	}
252
+	return ret
253
+}
0 254
new file mode 100644
... ...
@@ -0,0 +1,44 @@
0
+package stats
1
+
2
+// Outliers holds mild and extreme outliers found in data
3
+type Outliers struct {
4
+	Mild    Float64Data
5
+	Extreme Float64Data
6
+}
7
+
8
+// QuartileOutliers finds the mild and extreme outliers
9
+func QuartileOutliers(input Float64Data) (Outliers, error) {
10
+	if input.Len() == 0 {
11
+		return Outliers{}, EmptyInputErr
12
+	}
13
+
14
+	// Start by sorting a copy of the slice
15
+	copy := sortedCopy(input)
16
+
17
+	// Calculate the quartiles and interquartile range
18
+	qs, _ := Quartile(copy)
19
+	iqr, _ := InterQuartileRange(copy)
20
+
21
+	// Calculate the lower and upper inner and outer fences
22
+	lif := qs.Q1 - (1.5 * iqr)
23
+	uif := qs.Q3 + (1.5 * iqr)
24
+	lof := qs.Q1 - (3 * iqr)
25
+	uof := qs.Q3 + (3 * iqr)
26
+
27
+	// Find the data points that are outside of the
28
+	// inner and upper fences and add them to mild
29
+	// and extreme outlier slices
30
+	var mild Float64Data
31
+	var extreme Float64Data
32
+	for _, v := range copy {
33
+
34
+		if v < lof || v > uof {
35
+			extreme = append(extreme, v)
36
+		} else if v < lif || v > uif {
37
+			mild = append(mild, v)
38
+		}
39
+	}
40
+
41
+	// Wrap them into our struct
42
+	return Outliers{mild, extreme}, nil
43
+}
0 44
new file mode 100644
... ...
@@ -0,0 +1,86 @@
0
+package stats
1
+
2
+import (
3
+	"math"
4
+)
5
+
6
+// Percentile finds the relative standing in a slice of floats
7
+func Percentile(input Float64Data, percent float64) (percentile float64, err error) {
8
+	length := input.Len()
9
+	if length == 0 {
10
+		return math.NaN(), EmptyInputErr
11
+	}
12
+
13
+	if length == 1 {
14
+		return input[0], nil
15
+	}
16
+
17
+	if percent <= 0 || percent > 100 {
18
+		return math.NaN(), BoundsErr
19
+	}
20
+
21
+	// Start by sorting a copy of the slice
22
+	c := sortedCopy(input)
23
+
24
+	// Multiply percent by length of input
25
+	index := (percent / 100) * float64(len(c))
26
+
27
+	// Check if the index is a whole number
28
+	if index == float64(int64(index)) {
29
+
30
+		// Convert float to int
31
+		i := int(index)
32
+
33
+		// Find the value at the index
34
+		percentile = c[i-1]
35
+
36
+	} else if index > 1 {
37
+
38
+		// Convert float to int via truncation
39
+		i := int(index)
40
+
41
+		// Find the average of the index and following values
42
+		percentile, _ = Mean(Float64Data{c[i-1], c[i]})
43
+
44
+	} else {
45
+		return math.NaN(), BoundsErr
46
+	}
47
+
48
+	return percentile, nil
49
+
50
+}
51
+
52
+// PercentileNearestRank finds the relative standing in a slice of floats using the Nearest Rank method
53
+func PercentileNearestRank(input Float64Data, percent float64) (percentile float64, err error) {
54
+
55
+	// Find the length of items in the slice
56
+	il := input.Len()
57
+
58
+	// Return an error for empty slices
59
+	if il == 0 {
60
+		return math.NaN(), EmptyInputErr
61
+	}
62
+
63
+	// Return error for less than 0 or greater than 100 percentages
64
+	if percent < 0 || percent > 100 {
65
+		return math.NaN(), BoundsErr
66
+	}
67
+
68
+	// Start by sorting a copy of the slice
69
+	c := sortedCopy(input)
70
+
71
+	// Return the last item
72
+	if percent == 100.0 {
73
+		return c[il-1], nil
74
+	}
75
+
76
+	// Find ordinal ranking
77
+	or := int(math.Ceil(float64(il) * percent / 100))
78
+
79
+	// Return the item that is in the place of the ordinal rank
80
+	if or == 0 {
81
+		return c[0], nil
82
+	}
83
+	return c[or-1], nil
84
+
85
+}
0 86
new file mode 100644
... ...
@@ -0,0 +1,74 @@
0
+package stats
1
+
2
+import "math"
3
+
4
+// Quartiles holds the three quartile points
5
+type Quartiles struct {
6
+	Q1 float64
7
+	Q2 float64
8
+	Q3 float64
9
+}
10
+
11
+// Quartile returns the three quartile points from a slice of data
12
+func Quartile(input Float64Data) (Quartiles, error) {
13
+
14
+	il := input.Len()
15
+	if il == 0 {
16
+		return Quartiles{}, EmptyInputErr
17
+	}
18
+
19
+	// Start by sorting a copy of the slice
20
+	copy := sortedCopy(input)
21
+
22
+	// Find the cutoff places depeding on if
23
+	// the input slice length is even or odd
24
+	var c1 int
25
+	var c2 int
26
+	if il%2 == 0 {
27
+		c1 = il / 2
28
+		c2 = il / 2
29
+	} else {
30
+		c1 = (il - 1) / 2
31
+		c2 = c1 + 1
32
+	}
33
+
34
+	// Find the Medians with the cutoff points
35
+	Q1, _ := Median(copy[:c1])
36
+	Q2, _ := Median(copy)
37
+	Q3, _ := Median(copy[c2:])
38
+
39
+	return Quartiles{Q1, Q2, Q3}, nil
40
+
41
+}
42
+
43
+// InterQuartileRange finds the range between Q1 and Q3
44
+func InterQuartileRange(input Float64Data) (float64, error) {
45
+	if input.Len() == 0 {
46
+		return math.NaN(), EmptyInputErr
47
+	}
48
+	qs, _ := Quartile(input)
49
+	iqr := qs.Q3 - qs.Q1
50
+	return iqr, nil
51
+}
52
+
53
+// Midhinge finds the average of the first and third quartiles
54
+func Midhinge(input Float64Data) (float64, error) {
55
+	if input.Len() == 0 {
56
+		return math.NaN(), EmptyInputErr
57
+	}
58
+	qs, _ := Quartile(input)
59
+	mh := (qs.Q1 + qs.Q3) / 2
60
+	return mh, nil
61
+}
62
+
63
+// Trimean finds the average of the median and the midhinge
64
+func Trimean(input Float64Data) (float64, error) {
65
+	if input.Len() == 0 {
66
+		return math.NaN(), EmptyInputErr
67
+	}
68
+
69
+	c := sortedCopy(input)
70
+	q, _ := Quartile(c)
71
+
72
+	return (q.Q1 + (q.Q2 * 2) + q.Q3) / 4, nil
73
+}
0 74
new file mode 100644
... ...
@@ -0,0 +1,183 @@
0
+package stats
1
+
2
+// import "math"
3
+//
4
+// // WilcoxonRankSum tests the null hypothesis that two sets
5
+// // of data are drawn from the same distribution. It does
6
+// // not handle ties between measurements in x and y.
7
+// //
8
+// // Parameters:
9
+// //    data1 Float64Data: First set of data points.
10
+// //    data2 Float64Data: Second set of data points.
11
+// //    Length of both data samples must be equal.
12
+// //
13
+// // Return:
14
+// //    statistic float64: The test statistic under the
15
+// //                       large-sample approximation that the
16
+// //                       rank sum statistic is normally distributed.
17
+// //    pvalue float64: The two-sided p-value of the test
18
+// //    err error: Any error from the input data parameters
19
+// //
20
+// // https://en.wikipedia.org/wiki/Wilcoxon_rank-sum_test
21
+// func WilcoxonRankSum(data1, data2 Float64Data) (float64, float64, error) {
22
+//
23
+// 	l1 := data1.Len()
24
+// 	l2 := data2.Len()
25
+//
26
+// 	if l1 == 0 || l2 == 0 {
27
+// 		return math.NaN(), math.NaN(), EmptyInputErr
28
+// 	}
29
+//
30
+// 	if l1 != l2 {
31
+// 		return math.NaN(), math.NaN(), SizeErr
32
+// 	}
33
+//
34
+// 	alldata := Float64Data{}
35
+// 	alldata = append(alldata, data1...)
36
+// 	alldata = append(alldata, data2...)
37
+//
38
+// 	// ranked :=
39
+//
40
+// 	return 0.0, 0.0, nil
41
+// }
42
+//
43
+// //     x, y = map(np.asarray, (x, y))
44
+// //     n1 = len(x)
45
+// //     n2 = len(y)
46
+// //     alldata = np.concatenate((x, y))
47
+// //     ranked = rankdata(alldata)
48
+// //     x = ranked[:n1]
49
+// //     s = np.sum(x, axis=0)
50
+// //     expected = n1 * (n1+n2+1) / 2.0
51
+// //     z = (s - expected) / np.sqrt(n1*n2*(n1+n2+1)/12.0)
52
+// //     prob = 2 * distributions.norm.sf(abs(z))
53
+// //
54
+// //     return RanksumsResult(z, prob)
55
+//
56
+// // def rankdata(a, method='average'):
57
+// //     """
58
+// //     Assign ranks to data, dealing with ties appropriately.
59
+// //     Ranks begin at 1.  The `method` argument controls how ranks are assigned
60
+// //     to equal values.  See [1]_ for further discussion of ranking methods.
61
+// //     Parameters
62
+// //     ----------
63
+// //     a : array_like
64
+// //         The array of values to be ranked.  The array is first flattened.
65
+// //     method : str, optional
66
+// //         The method used to assign ranks to tied elements.
67
+// //         The options are 'average', 'min', 'max', 'dense' and 'ordinal'.
68
+// //         'average':
69
+// //             The average of the ranks that would have been assigned to
70
+// //             all the tied values is assigned to each value.
71
+// //         'min':
72
+// //             The minimum of the ranks that would have been assigned to all
73
+// //             the tied values is assigned to each value.  (This is also
74
+// //             referred to as "competition" ranking.)
75
+// //         'max':
76
+// //             The maximum of the ranks that would have been assigned to all
77
+// //             the tied values is assigned to each value.
78
+// //         'dense':
79
+// //             Like 'min', but the rank of the next highest element is assigned
80
+// //             the rank immediately after those assigned to the tied elements.
81
+// //         'ordinal':
82
+// //             All values are given a distinct rank, corresponding to the order
83
+// //             that the values occur in `a`.
84
+// //         The default is 'average'.
85
+// //     Returns
86
+// //     -------
87
+// //     ranks : ndarray
88
+// //          An array of length equal to the size of `a`, containing rank
89
+// //          scores.
90
+// //     References
91
+// //     ----------
92
+// //     .. [1] "Ranking", https://en.wikipedia.org/wiki/Ranking
93
+// //     Examples
94
+// //     --------
95
+// //     >>> from scipy.stats import rankdata
96
+// //     >>> rankdata([0, 2, 3, 2])
97
+// //     array([ 1. ,  2.5,  4. ,  2.5])
98
+// //     """
99
+// //
100
+// //     arr = np.ravel(np.asarray(a))
101
+// //     algo = 'quicksort'
102
+// //     sorter = np.argsort(arr, kind=algo)
103
+// //
104
+// //     inv = np.empty(sorter.size, dtype=np.intp)
105
+// //     inv[sorter] = np.arange(sorter.size, dtype=np.intp)
106
+// //
107
+// //
108
+// //     arr = arr[sorter]
109
+// //     obs = np.r_[True, arr[1:] != arr[:-1]]
110
+// //     dense = obs.cumsum()[inv]
111
+// //
112
+// //
113
+// //     # cumulative counts of each unique value
114
+// //     count = np.r_[np.nonzero(obs)[0], len(obs)]
115
+// //
116
+// //     # average method
117
+// //     return .5 * (count[dense] + count[dense - 1] + 1)
118
+//
119
+// type rankable interface {
120
+// 	Len() int
121
+// 	RankEqual(int, int) bool
122
+// }
123
+//
124
+// func StandardRank(d rankable) []float64 {
125
+// 	r := make([]float64, d.Len())
126
+// 	var k int
127
+// 	for i := range r {
128
+// 		if i == 0 || !d.RankEqual(i, i-1) {
129
+// 			k = i + 1
130
+// 		}
131
+// 		r[i] = float64(k)
132
+// 	}
133
+// 	return r
134
+// }
135
+//
136
+// func ModifiedRank(d rankable) []float64 {
137
+// 	r := make([]float64, d.Len())
138
+// 	for i := range r {
139
+// 		k := i + 1
140
+// 		for j := i + 1; j < len(r) && d.RankEqual(i, j); j++ {
141
+// 			k = j + 1
142
+// 		}
143
+// 		r[i] = float64(k)
144
+// 	}
145
+// 	return r
146
+// }
147
+//
148
+// func DenseRank(d rankable) []float64 {
149
+// 	r := make([]float64, d.Len())
150
+// 	var k int
151
+// 	for i := range r {
152
+// 		if i == 0 || !d.RankEqual(i, i-1) {
153
+// 			k++
154
+// 		}
155
+// 		r[i] = float64(k)
156
+// 	}
157
+// 	return r
158
+// }
159
+//
160
+// func OrdinalRank(d rankable) []float64 {
161
+// 	r := make([]float64, d.Len())
162
+// 	for i := range r {
163
+// 		r[i] = float64(i + 1)
164
+// 	}
165
+// 	return r
166
+// }
167
+//
168
+// func FractionalRank(d rankable) []float64 {
169
+// 	r := make([]float64, d.Len())
170
+// 	for i := 0; i < len(r); {
171
+// 		var j int
172
+// 		f := float64(i + 1)
173
+// 		for j = i + 1; j < len(r) && d.RankEqual(i, j); j++ {
174
+// 			f += float64(j + 1)
175
+// 		}
176
+// 		f /= float64(j - i)
177
+// 		for ; i < j; i++ {
178
+// 			r[i] = f
179
+// 		}
180
+// 	}
181
+// 	return r
182
+// }
0 183
new file mode 100644
... ...
@@ -0,0 +1,113 @@
0
+package stats
1
+
2
+import "math"
3
+
4
+// Series is a container for a series of data
5
+type Series []Coordinate
6
+
7
+// Coordinate holds the data in a series
8
+type Coordinate struct {
9
+	X, Y float64
10
+}
11
+
12
+// LinearRegression finds the least squares linear regression on data series
13
+func LinearRegression(s Series) (regressions Series, err error) {
14
+
15
+	if len(s) == 0 {
16
+		return nil, EmptyInputErr
17
+	}
18
+
19
+	// Placeholder for the math to be done
20
+	var sum [5]float64
21
+
22
+	// Loop over data keeping index in place
23
+	i := 0
24
+	for ; i < len(s); i++ {
25
+		sum[0] += s[i].X
26
+		sum[1] += s[i].Y
27
+		sum[2] += s[i].X * s[i].X
28
+		sum[3] += s[i].X * s[i].Y
29
+		sum[4] += s[i].Y * s[i].Y
30
+	}
31
+
32
+	// Find gradient and intercept
33
+	f := float64(i)
34
+	gradient := (f*sum[3] - sum[0]*sum[1]) / (f*sum[2] - sum[0]*sum[0])
35
+	intercept := (sum[1] / f) - (gradient * sum[0] / f)
36
+
37
+	// Create the new regression series
38
+	for j := 0; j < len(s); j++ {
39
+		regressions = append(regressions, Coordinate{
40
+			X: s[j].X,
41
+			Y: s[j].X*gradient + intercept,
42
+		})
43
+	}
44
+
45
+	return regressions, nil
46
+}
47
+
48
+// ExponentialRegression returns an exponential regression on data series
49
+func ExponentialRegression(s Series) (regressions Series, err error) {
50
+
51
+	if len(s) == 0 {
52
+		return nil, EmptyInputErr
53
+	}
54
+
55
+	var sum [6]float64
56
+
57
+	for i := 0; i < len(s); i++ {
58
+		if s[i].Y < 0 {
59
+			return nil, YCoordErr
60
+		}
61
+		sum[0] += s[i].X
62
+		sum[1] += s[i].Y
63
+		sum[2] += s[i].X * s[i].X * s[i].Y
64
+		sum[3] += s[i].Y * math.Log(s[i].Y)
65
+		sum[4] += s[i].X * s[i].Y * math.Log(s[i].Y)
66
+		sum[5] += s[i].X * s[i].Y
67
+	}
68
+
69
+	denominator := (sum[1]*sum[2] - sum[5]*sum[5])
70
+	a := math.Pow(math.E, (sum[2]*sum[3]-sum[5]*sum[4])/denominator)
71
+	b := (sum[1]*sum[4] - sum[5]*sum[3]) / denominator
72
+
73
+	for j := 0; j < len(s); j++ {
74
+		regressions = append(regressions, Coordinate{
75
+			X: s[j].X,
76
+			Y: a * math.Exp(b*s[j].X),
77
+		})
78
+	}
79
+
80
+	return regressions, nil
81
+}
82
+
83
+// LogarithmicRegression returns an logarithmic regression on data series
84
+func LogarithmicRegression(s Series) (regressions Series, err error) {
85
+
86
+	if len(s) == 0 {
87
+		return nil, EmptyInputErr
88
+	}
89
+
90
+	var sum [4]float64
91
+
92
+	i := 0
93
+	for ; i < len(s); i++ {
94
+		sum[0] += math.Log(s[i].X)
95
+		sum[1] += s[i].Y * math.Log(s[i].X)
96
+		sum[2] += s[i].Y
97
+		sum[3] += math.Pow(math.Log(s[i].X), 2)
98
+	}
99
+
100
+	f := float64(i)
101
+	a := (f*sum[1] - sum[2]*sum[0]) / (f*sum[3] - sum[0]*sum[0])
102
+	b := (sum[2] - a*sum[0]) / f
103
+
104
+	for j := 0; j < len(s); j++ {
105
+		regressions = append(regressions, Coordinate{
106
+			X: s[j].X,
107
+			Y: b + a*math.Log(s[j].X),
108
+		})
109
+	}
110
+
111
+	return regressions, nil
112
+}
0 113
new file mode 100644
... ...
@@ -0,0 +1,38 @@
0
+package stats
1
+
2
+import "math"
3
+
4
+// Round a float to a specific decimal place or precision
5
+func Round(input float64, places int) (rounded float64, err error) {
6
+
7
+	// If the float is not a number
8
+	if math.IsNaN(input) {
9
+		return math.NaN(), NaNErr
10
+	}
11
+
12
+	// Find out the actual sign and correct the input for later
13
+	sign := 1.0
14
+	if input < 0 {
15
+		sign = -1
16
+		input *= -1
17
+	}
18
+
19
+	// Use the places arg to get the amount of precision wanted
20
+	precision := math.Pow(10, float64(places))
21
+
22
+	// Find the decimal place we are looking to round
23
+	digit := input * precision
24
+
25
+	// Get the actual decimal number as a fraction to be compared
26
+	_, decimal := math.Modf(digit)
27
+
28
+	// If the decimal is less than .5 we round down otherwise up
29
+	if decimal >= 0.5 {
30
+		rounded = math.Ceil(digit)
31
+	} else {
32
+		rounded = math.Floor(digit)
33
+	}
34
+
35
+	// Finally we do the math to actually create a rounded number
36
+	return rounded / precision * sign, nil
37
+}
0 38
new file mode 100644
... ...
@@ -0,0 +1,76 @@
0
+package stats
1
+
2
+import (
3
+	"math/rand"
4
+	"sort"
5
+)
6
+
7
+// Sample returns sample from input with replacement or without
8
+func Sample(input Float64Data, takenum int, replacement bool) ([]float64, error) {
9
+
10
+	if input.Len() == 0 {
11
+		return nil, EmptyInputErr
12
+	}
13
+
14
+	length := input.Len()
15
+	if replacement {
16
+
17
+		result := Float64Data{}
18
+		rand.Seed(unixnano())
19
+
20
+		// In every step, randomly take the num for
21
+		for i := 0; i < takenum; i++ {
22
+			idx := rand.Intn(length)
23
+			result = append(result, input[idx])
24
+		}
25
+
26
+		return result, nil
27
+
28
+	} else if !replacement && takenum <= length {
29
+
30
+		rand.Seed(unixnano())
31
+
32
+		// Get permutation of number of indexies
33
+		perm := rand.Perm(length)
34
+		result := Float64Data{}
35
+
36
+		// Get element of input by permutated index
37
+		for _, idx := range perm[0:takenum] {
38
+			result = append(result, input[idx])
39
+		}
40
+
41
+		return result, nil
42
+
43
+	}
44
+
45
+	return nil, BoundsErr
46
+}
47
+
48
+// StableSample like stable sort, it returns samples from input while keeps the order of original data.
49
+func StableSample(input Float64Data, takenum int) ([]float64, error) {
50
+	if input.Len() == 0 {
51
+		return nil, EmptyInputErr
52
+	}
53
+
54
+	length := input.Len()
55
+
56
+	if takenum <= length {
57
+
58
+		rand.Seed(unixnano())
59
+
60
+		perm := rand.Perm(length)
61
+		perm = perm[0:takenum]
62
+		// Sort perm before applying
63
+		sort.Ints(perm)
64
+		result := Float64Data{}
65
+
66
+		for _, idx := range perm {
67
+			result = append(result, input[idx])
68
+		}
69
+
70
+		return result, nil
71
+
72
+	}
73
+
74
+	return nil, BoundsErr
75
+}
0 76
new file mode 100644
... ...
@@ -0,0 +1,18 @@
0
+package stats
1
+
2
+import "math"
3
+
4
+// Sigmoid returns the input values in the range of -1 to 1
5
+// along the sigmoid or s-shaped curve, commonly used in
6
+// machine learning while training neural networks as an
7
+// activation function.
8
+func Sigmoid(input Float64Data) ([]float64, error) {
9
+	if input.Len() == 0 {
10
+		return Float64Data{}, EmptyInput
11
+	}
12
+	s := make([]float64, len(input))
13
+	for i, v := range input {
14
+		s[i] = 1 / (1 + math.Exp(-v))
15
+	}
16
+	return s, nil
17
+}
0 18
new file mode 100644
... ...
@@ -0,0 +1,25 @@
0
+package stats
1
+
2
+import "math"
3
+
4
+// SoftMax returns the input values in the range of 0 to 1
5
+// with sum of all the probabilities being equal to one. It
6
+// is commonly used in machine learning neural networks.
7
+func SoftMax(input Float64Data) ([]float64, error) {
8
+	if input.Len() == 0 {
9
+		return Float64Data{}, EmptyInput
10
+	}
11
+
12
+	s := 0.0
13
+	c, _ := Max(input)
14
+	for _, e := range input {
15
+		s += math.Exp(e - c)
16
+	}
17
+
18
+	sm := make([]float64, len(input))
19
+	for i, v := range input {
20
+		sm[i] = math.Exp(v-c) / s
21
+	}
22
+
23
+	return sm, nil
24
+}
0 25
new file mode 100644
... ...
@@ -0,0 +1,18 @@
0
+package stats
1
+
2
+import "math"
3
+
4
+// Sum adds all the numbers of a slice together
5
+func Sum(input Float64Data) (sum float64, err error) {
6
+
7
+	if input.Len() == 0 {
8
+		return math.NaN(), EmptyInputErr
9
+	}
10
+
11
+	// Add em up
12
+	for _, n := range input {
13
+		sum += n
14
+	}
15
+
16
+	return sum, nil
17
+}
0 18
new file mode 100644
... ...
@@ -0,0 +1,43 @@
0
+package stats
1
+
2
+import (
3
+	"sort"
4
+	"time"
5
+)
6
+
7
+// float64ToInt rounds a float64 to an int
8
+func float64ToInt(input float64) (output int) {
9
+	r, _ := Round(input, 0)
10
+	return int(r)
11
+}
12
+
13
+// unixnano returns nanoseconds from UTC epoch
14
+func unixnano() int64 {
15
+	return time.Now().UTC().UnixNano()
16
+}
17
+
18
+// copyslice copies a slice of float64s
19
+func copyslice(input Float64Data) Float64Data {
20
+	s := make(Float64Data, input.Len())
21
+	copy(s, input)
22
+	return s
23
+}
24
+
25
+// sortedCopy returns a sorted copy of float64s
26
+func sortedCopy(input Float64Data) (copy Float64Data) {
27
+	copy = copyslice(input)
28
+	sort.Float64s(copy)
29
+	return
30
+}
31
+
32
+// sortedCopyDif returns a sorted copy of float64s
33
+// only if the original data isn't sorted.
34
+// Only use this if returned slice won't be manipulated!
35
+func sortedCopyDif(input Float64Data) (copy Float64Data) {
36
+	if sort.Float64sAreSorted(input) {
37
+		return input
38
+	}
39
+	copy = copyslice(input)
40
+	sort.Float64s(copy)
41
+	return
42
+}
0 43
new file mode 100644
... ...
@@ -0,0 +1,105 @@
0
+package stats
1
+
2
+import "math"
3
+
4
+// _variance finds the variance for both population and sample data
5
+func _variance(input Float64Data, sample int) (variance float64, err error) {
6
+
7
+	if input.Len() == 0 {
8
+		return math.NaN(), EmptyInputErr
9
+	}
10
+
11
+	// Sum the square of the mean subtracted from each number
12
+	m, _ := Mean(input)
13
+
14
+	for _, n := range input {
15
+		variance += (n - m) * (n - m)
16
+	}
17
+
18
+	// When getting the mean of the squared differences
19
+	// "sample" will allow us to know if it's a sample
20
+	// or population and wether to subtract by one or not
21
+	return variance / float64((input.Len() - (1 * sample))), nil
22
+}
23
+
24
+// Variance the amount of variation in the dataset
25
+func Variance(input Float64Data) (sdev float64, err error) {
26
+	return PopulationVariance(input)
27
+}
28
+
29
+// PopulationVariance finds the amount of variance within a population
30
+func PopulationVariance(input Float64Data) (pvar float64, err error) {
31
+
32
+	v, err := _variance(input, 0)
33
+	if err != nil {
34
+		return math.NaN(), err
35
+	}
36
+
37
+	return v, nil
38
+}
39
+
40
+// SampleVariance finds the amount of variance within a sample
41
+func SampleVariance(input Float64Data) (svar float64, err error) {
42
+
43
+	v, err := _variance(input, 1)
44
+	if err != nil {
45
+		return math.NaN(), err
46
+	}
47
+
48
+	return v, nil
49
+}
50
+
51
+// Covariance is a measure of how much two sets of data change
52
+func Covariance(data1, data2 Float64Data) (float64, error) {
53
+
54
+	l1 := data1.Len()
55
+	l2 := data2.Len()
56
+
57
+	if l1 == 0 || l2 == 0 {
58
+		return math.NaN(), EmptyInputErr
59
+	}
60
+
61
+	if l1 != l2 {
62
+		return math.NaN(), SizeErr
63
+	}
64
+
65
+	m1, _ := Mean(data1)
66
+	m2, _ := Mean(data2)
67
+
68
+	// Calculate sum of squares
69
+	var ss float64
70
+	for i := 0; i < l1; i++ {
71
+		delta1 := (data1.Get(i) - m1)
72
+		delta2 := (data2.Get(i) - m2)
73
+		ss += (delta1*delta2 - ss) / float64(i+1)
74
+	}
75
+
76
+	return ss * float64(l1) / float64(l1-1), nil
77
+}
78
+
79
+// CovariancePopulation computes covariance for entire population between two variables.
80
+func CovariancePopulation(data1, data2 Float64Data) (float64, error) {
81
+
82
+	l1 := data1.Len()
83
+	l2 := data2.Len()
84
+
85
+	if l1 == 0 || l2 == 0 {
86
+		return math.NaN(), EmptyInputErr
87
+	}
88
+
89
+	if l1 != l2 {
90
+		return math.NaN(), SizeErr
91
+	}
92
+
93
+	m1, _ := Mean(data1)
94
+	m2, _ := Mean(data2)
95
+
96
+	var s float64
97
+	for i := 0; i < l1; i++ {
98
+		delta1 := (data1.Get(i) - m1)
99
+		delta2 := (data2.Get(i) - m2)
100
+		s += delta1 * delta2
101
+	}
102
+
103
+	return s / float64(l1), nil
104
+}
... ...
@@ -1045,6 +1045,9 @@ github.com/moby/sys/userns
1045 1045
 ## explicit; go 1.18
1046 1046
 github.com/moby/term
1047 1047
 github.com/moby/term/windows
1048
+# github.com/montanaflynn/stats v0.7.1
1049
+## explicit; go 1.13
1050
+github.com/montanaflynn/stats
1048 1051
 # github.com/morikuni/aec v1.0.0
1049 1052
 ## explicit
1050 1053
 github.com/morikuni/aec