Repository: elliotchance/pie
Pull request #164 (2020-10-02 16:14:40)

Add stddev
Refs: issue#41

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/elliotchance/pie/164)
<!-- Reviewable:end -->



Patch:
diff --git a/.travis.yml b/.travis.yml
index 441a920..71430f0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,9 @@
 language: go
 
 go:
-  - "1.8"
-  - "1.9"
-  - "1.10"
-  - "1.11"
-  - "1.12"
+  - "1.13"
+  - "1.14"
+  - "1.15"
 
 script:
   # Make sure everything is formatted correctly.
diff --git a/README.md b/README.md
index f3db4a8..5928890 100644
--- a/README.md
+++ b/README.md
@@ -225,6 +225,7 @@ The letters in brackets indicate:
 | [`Sort`](#sort)                         | ✓      | ✓      |        |      | n⋅log(n) | Sort works similar to sort.SliceType(). However, unlike sort.SliceType the slice returned will be reallocated as to not modify the input slice.  |
 | [`SortStableUsing`](#sortstableusing)   | ✓      |        | ✓      |      | n⋅log(n) | SortStableUsing works similar to sort.SliceStable. However, unlike sort.SliceStable the slice returned will be reallocated as to not modify the input slice.  |
 | [`SortUsing`](#sortusing)               | ✓      |        | ✓      |      | n⋅log(n) | SortUsing works similar to sort.Slice. However, unlike sort.Slice the slice returned will be reallocated as to not modify the input slice.  |
+| [`Stddev`](#stddev)                     |        | ✓      |        |      | n        | Stddev is the standard deviation  |
 | [`Strings`](#strings) (S)               | ✓      | ✓      | ✓      |      | n        | Strings transforms each element to a string.  |
 | [`SubSlice`](#subslice)                 | ✓      | ✓      | ✓      |      | n        | SubSlice will return the subSlice from start to end(excluded)  |
 | [`Sum`](#sum)                           |        | ✓      |        |      | n        | Sum is the sum of all of the elements.  |
@@ -680,6 +681,11 @@ SortUsing works similar to sort.Slice. However, unlike sort.Slice the
 slice returned will be reallocated as to not modify the input slice.
 
 
+## Stddev
+
+Stddev is the standard deviation
+
+
 ## Strings
 
 Strings transforms each element to a string.
diff --git a/functions/main.go b/functions/main.go
index 5ca0cec..5c8496c 100644
--- a/functions/main.go
+++ b/functions/main.go
@@ -72,6 +72,7 @@ var Functions = []struct {
 	{"Sort", "sort.go", ForNumbersAndStrings, "n⋅log(n)"},
 	{"SortStableUsing", "sort_stable_using.go", ForStrings | ForStructs, "n⋅log(n)"},
 	{"SortUsing", "sort_using.go", ForStrings | ForStructs, "n⋅log(n)"},
+	{"Stddev", "stddev.go", ForNumbers, "n"},
 	{"Strings", "strings.go", ForAll, "n"},
 	{"SubSlice", "sub_slice.go", ForAll, "n"},
 	{"Sum", "sum.go", ForNumbers, "n"},
diff --git a/functions/mode.go b/functions/mode.go
index d9f8e59..0083c1b 100644
--- a/functions/mode.go
+++ b/functions/mode.go
@@ -8,7 +8,7 @@ func (ss SliceType) Mode() SliceType {
 	if len(ss) == 0 {
 		return nil
 	}
-	values := make(map[ElementType]int, 0)
+	values := make(map[ElementType]int)
 	for _, s := range ss {
 		values[s]++
 	}
diff --git a/functions/stddev.go b/functions/stddev.go
new file mode 100644
index 0000000..447e848
--- /dev/null
+++ b/functions/stddev.go
@@ -0,0 +1,20 @@
+package functions
+
+import "math"
+
+// Stddev is the standard deviation
+func (ss SliceType) Stddev() float64 {
+	if len(ss) == 0 {
+		return 0.0
+	}
+
+	avg := ss.Average()
+
+	var sd float64
+	for i := range ss {
+		sd += math.Pow(float64(ss[i])-avg, 2)
+	}
+	sd = math.Sqrt(sd / float64(len(ss)))
+
+	return sd
+}
diff --git a/pie/carpointers_pie.go b/pie/carpointers_pie.go
index 97202a0..f5c97ce 100755
--- a/pie/carpointers_pie.go
+++ b/pie/carpointers_pie.go
@@ -420,7 +420,7 @@ func (ss carPointers) Mode() carPointers {
 	if len(ss) == 0 {
 		return nil
 	}
-	values := make(map[*car]int, 0)
+	values := make(map[*car]int)
 	for _, s := range ss {
 		values[s]++
 	}
diff --git a/pie/cars_pie.go b/pie/cars_pie.go
index 5b18f75..22f942d 100755
--- a/pie/cars_pie.go
+++ b/pie/cars_pie.go
@@ -420,7 +420,7 @@ func (ss cars) Mode() cars {
 	if len(ss) == 0 {
 		return nil
 	}
-	values := make(map[car]int, 0)
+	values := make(map[car]int)
 	for _, s := range ss {
 		values[s]++
 	}
diff --git a/pie/float64s_pie.go b/pie/float64s_pie.go
index ef1ce85..b27eac2 100755
--- a/pie/float64s_pie.go
+++ b/pie/float64s_pie.go
@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"github.com/elliotchance/pie/pie/util"
+	"math"
 	"math/rand"
 	"sort"
 	"strconv"
@@ -600,7 +601,7 @@ func (ss Float64s) Mode() Float64s {
 	if len(ss) == 0 {
 		return nil
 	}
-	values := make(map[float64]int, 0)
+	values := make(map[float64]int)
 	for _, s := range ss {
 		values[s]++
 	}
@@ -838,6 +839,23 @@ func (ss Float64s) Sort() Float64s {
 	return sorted
 }
 
+// Stddev is the standard deviation
+func (ss Float64s) Stddev() float64 {
+	if len(ss) == 0 {
+		return 0.0
+	}
+
+	avg := ss.Average()
+
+	var sd float64
+	for i := range ss {
+		sd += math.Pow(float64(ss[i])-avg, 2)
+	}
+	sd = math.Sqrt(sd / float64(len(ss)))
+
+	return sd
+}
+
 // Strings transforms each element to a string.
 //
 // If the element type implements fmt.Stringer it will be used. Otherwise it
diff --git a/pie/ints_pie.go b/pie/ints_pie.go
index 90425fd..13c8238 100755
--- a/pie/ints_pie.go
+++ b/pie/ints_pie.go
@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"github.com/elliotchance/pie/pie/util"
+	"math"
 	"math/rand"
 	"sort"
 	"strconv"
@@ -600,7 +601,7 @@ func (ss Ints) Mode() Ints {
 	if len(ss) == 0 {
 		return nil
 	}
-	values := make(map[int]int, 0)
+	values := make(map[int]int)
 	for _, s := range ss {
 		values[s]++
 	}
@@ -838,6 +839,23 @@ func (ss Ints) Sort() Ints {
 	return sorted
 }
 
+// Stddev is the standard deviation
+func (ss Ints) Stddev() float64 {
+	if len(ss) == 0 {
+		return 0.0
+	}
+
+	avg := ss.Average()
+
+	var sd float64
+	for i := range ss {
+		sd += math.Pow(float64(ss[i])-avg, 2)
+	}
+	sd = math.Sqrt(sd / float64(len(ss)))
+
+	return sd
+}
+
 // Strings transforms each element to a string.
 //
 // If the element type implements fmt.Stringer it will be used. Otherwise it
diff --git a/pie/strings_pie.go b/pie/strings_pie.go
index 5ff32b7..f55df35 100755
--- a/pie/strings_pie.go
+++ b/pie/strings_pie.go
@@ -511,7 +511,7 @@ func (ss Strings) Mode() Strings {
 	if len(ss) == 0 {
 		return nil
 	}
-	values := make(map[string]int, 0)
+	values := make(map[string]int)
 	for _, s := range ss {
 		values[s]++
 	}
diff --git a/template.go b/template.go
index 3d1feef..d7f88f5 100644
--- a/template.go
+++ b/template.go
@@ -719,7 +719,7 @@ func (ss SliceType) Mode() SliceType {
 	if len(ss) == 0 {
 		return nil
 	}
-	values := make(map[ElementType]int, 0)
+	values := make(map[ElementType]int)
 	for _, s := range ss {
 		values[s]++
 	}
@@ -1045,6 +1045,27 @@ func (ss SliceType) SortUsing(less func(a, b ElementType) bool) SliceType {
 
 	return sorted
 }
+`,
+	"Stddev": `package functions
+
+import "math"
+
+// Stddev is the standard deviation
+func (ss SliceType) Stddev() float64 {
+	if len(ss) == 0 {
+		return 0.0
+	}
+
+	avg := ss.Average()
+
+	var sd float64
+	for i := range ss {
+		sd += math.Pow(float64(ss[i])-avg, 2)
+	}
+	sd = math.Sqrt(sd / float64(len(ss)))
+
+	return sd
+}
 `,
 	"Strings": `package functions
 


Test patch:
diff --git a/pie/float64s_test.go b/pie/float64s_test.go
index 51091b4..8e32276 100644
--- a/pie/float64s_test.go
+++ b/pie/float64s_test.go
@@ -1521,3 +1521,11 @@ func TestFloat64s_Insert(t *testing.T) {
 	assert.Equal(t, Float64s{1.0, 2.0}, Float64s{1.0}.Insert(1, 2.0))
 	assert.Equal(t, Float64s{1.0, 2.0, 3.3}, Float64s{1.0, 3.3}.Insert(1, 2.0))
 }
+
+func TestFloat64s_Stddev(t *testing.T) {
+
+	assert.Equal(t, 0.0, Float64s{}.Stddev())
+	assert.Equal(t, 0.0, Float64s{1}.Stddev())
+	assert.Equal(t, 4.8587389053127765, Float64s{10.0, 12.5, 23.3, 23.1, 16.5, 23.1, 21.2, 16.4}.Stddev())
+
+}
diff --git a/pie/ints_test.go b/pie/ints_test.go
index 471ffd7..ac68b32 100644
--- a/pie/ints_test.go
+++ b/pie/ints_test.go
@@ -1510,3 +1510,11 @@ func TestInts_Insert(t *testing.T) {
 	assert.Equal(t, Ints{1, 2}, Ints{1}.Insert(1, 2))
 	assert.Equal(t, Ints{1, 2, 3}, Ints{1, 3}.Insert(1, 2))
 }
+
+func TestInts_Stddev(t *testing.T) {
+
+	assert.Equal(t, 0.0, Ints{}.Stddev())
+	assert.Equal(t, 0.0, Ints{1}.Stddev())
+	assert.Equal(t, 4.898979485566356, Ints{10, 12, 23, 23, 16, 23, 21, 16}.Stddev())
+
+}