From c0fc2baa5051de798b504a2e0d0e9fce15ca9b3f Mon Sep 17 00:00:00 2001 From: Will Charczuk Date: Mon, 1 May 2017 22:18:52 -0700 Subject: [PATCH] updates --- sequence/sequence.go | 132 +++++++++++++++++++++++++++++++++++++- sequence/sequence_test.go | 11 ++++ sequence/util.go | 32 +++++++++ util/math.go | 4 +- 4 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 sequence/util.go diff --git a/sequence/sequence.go b/sequence/sequence.go index b2b2f00..4565af4 100644 --- a/sequence/sequence.go +++ b/sequence/sequence.go @@ -1,6 +1,14 @@ package sequence -import "math" +import ( + "math" + "sort" +) + +// New returns a new sequence. +func New(values ...float64) Seq { + return Seq{Provider: Array(values)} +} // Provider is a provider for values for a sequence. type Provider interface { @@ -73,6 +81,88 @@ func (s Seq) FoldRight(mapfn func(i int, v0, v float64) float64) (v0 float64) { return } +// Min returns the minimum value in the sequence. +func (s Seq) Min() float64 { + if s.Len() == 0 { + return 0 + } + min := s.GetValue(0) + var value float64 + for i := 1; i < s.Len(); i++ { + value = s.GetValue(i) + if value < min { + min = value + } + } + return min +} + +// Max returns the maximum value in the sequence. +func (s Seq) Max() float64 { + if s.Len() == 0 { + return 0 + } + max := s.GetValue(0) + var value float64 + for i := 1; i < s.Len(); i++ { + value = s.GetValue(i) + if value > max { + max = value + } + } + return max +} + +// MinMax returns the minimum and the maximum in one pass. +func (s Seq) MinMax() (min, max float64) { + if s.Len() == 0 { + return + } + min = s.GetValue(0) + max = min + var value float64 + for i := 1; i < s.Len(); i++ { + value = s.GetValue(i) + if value < min { + min = value + } + if value > max { + max = value + } + } + return +} + +// Sort returns the sequence sorted in ascending order. +// This fully enumerates the sequence. +func (s Seq) Sort() Seq { + if s.Len() == 0 { + return s + } + values := s.Array() + sort.Float64s(values) + return Seq{Provider: Array(values)} +} + +// Median returns the median or middle value in the sorted sequence. +func (s Seq) Median() (median float64) { + l := s.Len() + if l == 0 { + return + } + + sorted := s.Sort() + if l%2 == 0 { + v0 := sorted.GetValue(l/2 - 1) + v1 := sorted.GetValue(l/2 + 1) + median = (v0 + v1) / 2 + } else { + median = float64(sorted.GetValue(l << 1)) + } + + return +} + // Sum adds all the elements of a series together. func (s Seq) Sum() (accum float64) { if s.Len() == 0 { @@ -118,3 +208,43 @@ func (s Seq) StdDev() float64 { return math.Pow(s.Variance(), 0.5) } + +//Percentile finds the relative standing in a slice of floats. +// `percent` should be given on the interval [0,1.0). +func (s Seq) Percentile(percent float64) (percentile float64) { + l := s.Len() + if l == 0 { + return 0 + } + + if percent < 0 || percent > 1.0 { + panic("percent out of range [0.0, 1.0)") + } + + sorted := s.Sort() + index := percent * float64(l) + if index == float64(int64(index)) { + i := f64i(index) + ci := sorted.GetValue(i - 1) + c := sorted.GetValue(i) + percentile = (ci + c) / 2.0 + } else { + i := f64i(index) + percentile = sorted.GetValue(i) + } + + return percentile +} + +// Normalize maps every value to the interval [0, 1.0). +func (s Seq) Normalize() Seq { + min, max := s.MinMax() + + delta := max - min + output := make([]float64, s.Len()) + for i := 0; i < s.Len(); i++ { + output[i] = (s.GetValue(i) - min) / delta + } + + return Seq{Provider: Array(output)} +} diff --git a/sequence/sequence_test.go b/sequence/sequence_test.go index d9ffcef..2b4c81b 100644 --- a/sequence/sequence_test.go +++ b/sequence/sequence_test.go @@ -81,3 +81,14 @@ func TestSequenceVariance(t *testing.T) { values := Seq{NewArray(1, 2, 3, 4, 5)} assert.Equal(2, values.Variance()) } + +func TestSequenceNormalize(t *testing.T) { + assert := assert.New(t) + + normalized := New(1, 2, 3, 4, 5).Normalize().Array() + + assert.NotEmpty(normalized) + assert.Len(normalized, 5) + assert.Equal(0, normalized[0]) + assert.Equal(1, normalized[4]) +} diff --git a/sequence/util.go b/sequence/util.go new file mode 100644 index 0000000..7d9e57e --- /dev/null +++ b/sequence/util.go @@ -0,0 +1,32 @@ +package sequence + +import "math" + +func round(input float64, places int) (rounded float64) { + if math.IsNaN(input) { + return 0.0 + } + + sign := 1.0 + if input < 0 { + sign = -1 + input *= -1 + } + + precision := math.Pow(10, float64(places)) + digit := input * precision + _, decimal := math.Modf(digit) + + if decimal >= 0.5 { + rounded = math.Ceil(digit) + } else { + rounded = math.Floor(digit) + } + + return rounded / precision * sign +} + +func f64i(value float64) int { + r := round(value, 0) + return int(r) +} diff --git a/util/math.go b/util/math.go index 0e588f0..73f4976 100644 --- a/util/math.go +++ b/util/math.go @@ -47,7 +47,7 @@ func (m mathUtil) MinAndMax(values ...float64) (min float64, max float64) { } min = values[0] max = values[0] - for _, v := range values { + for _, v := range values[1:] { if max < v { max = v } @@ -68,7 +68,7 @@ func (m mathUtil) MinAndMaxOfTime(values ...time.Time) (min time.Time, max time. min = values[0] max = values[0] - for _, v := range values { + for _, v := range values[1:] { if max.Before(v) { max = v }