gonum.org/v1/gonum@v0.14.0/stat/stat_test.go (about)

     1  // Copyright ©2014 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package stat
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"reflect"
    11  	"strconv"
    12  	"testing"
    13  
    14  	"golang.org/x/exp/rand"
    15  
    16  	"gonum.org/v1/gonum/floats"
    17  	"gonum.org/v1/gonum/floats/scalar"
    18  )
    19  
    20  func ExampleCircularMean() {
    21  	x := []float64{0, 0.25 * math.Pi, 0.75 * math.Pi}
    22  	weights := []float64{1, 2, 2.5}
    23  	cmean := CircularMean(x, weights)
    24  
    25  	fmt.Printf("The circular mean is %.5f.\n", cmean)
    26  	// Output:
    27  	// The circular mean is 1.37037.
    28  }
    29  
    30  func TestCircularMean(t *testing.T) {
    31  	for i, test := range []struct {
    32  		x   []float64
    33  		wts []float64
    34  		ans float64
    35  	}{
    36  		// Values compared against scipy.
    37  		{
    38  			x:   []float64{0, 2 * math.Pi},
    39  			ans: 0,
    40  		},
    41  		{
    42  			x:   []float64{0, 0.5 * math.Pi},
    43  			ans: 0.78539816339744,
    44  		},
    45  		{
    46  			x:   []float64{-1.5 * math.Pi, 0.5 * math.Pi, 2.5 * math.Pi},
    47  			wts: []float64{1, 2, 3},
    48  			ans: 0.5 * math.Pi,
    49  		},
    50  		{
    51  			x:   []float64{0, 0.5 * math.Pi},
    52  			wts: []float64{1, 2},
    53  			ans: 1.10714871779409,
    54  		},
    55  	} {
    56  		c := CircularMean(test.x, test.wts)
    57  		if math.Abs(c-test.ans) > 1e-14 {
    58  			t.Errorf("Circular mean mismatch case %d: Expected %v, Found %v", i, test.ans, c)
    59  		}
    60  	}
    61  	if !panics(func() { CircularMean(make([]float64, 3), make([]float64, 2)) }) {
    62  		t.Errorf("CircularMean did not panic with x, wts length mismatch")
    63  	}
    64  }
    65  
    66  func ExampleCorrelation() {
    67  	x := []float64{8, -3, 7, 8, -4}
    68  	y := []float64{10, 5, 6, 3, -1}
    69  	w := []float64{2, 1.5, 3, 3, 2}
    70  
    71  	fmt.Println("Correlation computes the degree to which two datasets move together")
    72  	fmt.Println("about their mean. For example, x and y above move similarly.")
    73  
    74  	c := Correlation(x, y, w)
    75  	fmt.Printf("Correlation is %.5f\n", c)
    76  
    77  	// Output:
    78  	// Correlation computes the degree to which two datasets move together
    79  	// about their mean. For example, x and y above move similarly.
    80  	// Correlation is 0.59915
    81  }
    82  
    83  func TestCorrelation(t *testing.T) {
    84  	for i, test := range []struct {
    85  		x   []float64
    86  		y   []float64
    87  		w   []float64
    88  		ans float64
    89  	}{
    90  		{
    91  			x:   []float64{8, -3, 7, 8, -4},
    92  			y:   []float64{8, -3, 7, 8, -4},
    93  			w:   nil,
    94  			ans: 1,
    95  		},
    96  		{
    97  			x:   []float64{8, -3, 7, 8, -4},
    98  			y:   []float64{8, -3, 7, 8, -4},
    99  			w:   []float64{1, 1, 1, 1, 1},
   100  			ans: 1,
   101  		},
   102  		{
   103  			x:   []float64{8, -3, 7, 8, -4},
   104  			y:   []float64{8, -3, 7, 8, -4},
   105  			w:   []float64{1, 6, 7, 0.8, 2.1},
   106  			ans: 1,
   107  		},
   108  		{
   109  			x:   []float64{8, -3, 7, 8, -4},
   110  			y:   []float64{10, 15, 4, 5, -1},
   111  			w:   nil,
   112  			ans: 0.0093334660769059,
   113  		},
   114  		{
   115  			x:   []float64{8, -3, 7, 8, -4},
   116  			y:   []float64{10, 15, 4, 5, -1},
   117  			w:   nil,
   118  			ans: 0.0093334660769059,
   119  		},
   120  		{
   121  			x:   []float64{8, -3, 7, 8, -4},
   122  			y:   []float64{10, 15, 4, 5, -1},
   123  			w:   []float64{1, 3, 1, 2, 2},
   124  			ans: -0.13966633352689,
   125  		},
   126  	} {
   127  		c := Correlation(test.x, test.y, test.w)
   128  		if math.Abs(test.ans-c) > 1e-14 {
   129  			t.Errorf("Correlation mismatch case %d. Expected %v, Found %v", i, test.ans, c)
   130  		}
   131  	}
   132  	if !panics(func() { Correlation(make([]float64, 2), make([]float64, 3), make([]float64, 3)) }) {
   133  		t.Errorf("Correlation did not panic with length mismatch")
   134  	}
   135  	if !panics(func() { Correlation(make([]float64, 2), make([]float64, 3), nil) }) {
   136  		t.Errorf("Correlation did not panic with length mismatch")
   137  	}
   138  	if !panics(func() { Correlation(make([]float64, 3), make([]float64, 3), make([]float64, 2)) }) {
   139  		t.Errorf("Correlation did not panic with weights length mismatch")
   140  	}
   141  }
   142  
   143  func ExampleKendall() {
   144  	x := []float64{8, -3, 7, 8, -4}
   145  	y := []float64{10, 5, 6, 3, -1}
   146  	w := []float64{2, 1.5, 3, 3, 2}
   147  
   148  	fmt.Println("Kendall correlation computes the number of ordered pairs")
   149  	fmt.Println("between two datasets.")
   150  
   151  	c := Kendall(x, y, w)
   152  	fmt.Printf("Kendall correlation is %.5f\n", c)
   153  
   154  	// Output:
   155  	// Kendall correlation computes the number of ordered pairs
   156  	// between two datasets.
   157  	// Kendall correlation is 0.25000
   158  }
   159  
   160  func TestKendall(t *testing.T) {
   161  	for i, test := range []struct {
   162  		x       []float64
   163  		y       []float64
   164  		weights []float64
   165  		ans     float64
   166  	}{
   167  		{
   168  			x:       []float64{0, 1, 2, 3},
   169  			y:       []float64{0, 1, 2, 3},
   170  			weights: nil,
   171  			ans:     1,
   172  		},
   173  		{
   174  			x:       []float64{0, 1},
   175  			y:       []float64{1, 0},
   176  			weights: nil,
   177  			ans:     -1,
   178  		},
   179  		{
   180  			x:       []float64{8, -3, 7, 8, -4},
   181  			y:       []float64{10, 15, 4, 5, -1},
   182  			weights: nil,
   183  			ans:     0.2,
   184  		},
   185  		{
   186  			x:       []float64{8, -3, 7, 8, -4},
   187  			y:       []float64{10, 5, 6, 3, -1},
   188  			weights: nil,
   189  			ans:     0.4,
   190  		},
   191  		{
   192  			x:       []float64{1, 2, 3, 4, 5},
   193  			y:       []float64{2, 3, 4, 5, 6},
   194  			weights: []float64{1, 1, 1, 1, 1},
   195  			ans:     1,
   196  		},
   197  		{
   198  			x:       []float64{1, 2, 3, 2, 1},
   199  			y:       []float64{2, 3, 2, 1, 0},
   200  			weights: []float64{1, 1, 0, 0, 0},
   201  			ans:     1,
   202  		},
   203  	} {
   204  		c := Kendall(test.x, test.y, test.weights)
   205  		if math.Abs(test.ans-c) > 1e-14 {
   206  			t.Errorf("Correlation mismatch case %d. Expected %v, Found %v", i, test.ans, c)
   207  		}
   208  	}
   209  	if !panics(func() { Kendall(make([]float64, 2), make([]float64, 3), make([]float64, 3)) }) {
   210  		t.Errorf("Kendall did not panic with length mismatch")
   211  	}
   212  	if !panics(func() { Kendall(make([]float64, 2), make([]float64, 3), nil) }) {
   213  		t.Errorf("Kendall did not panic with length mismatch")
   214  	}
   215  	if !panics(func() { Kendall(make([]float64, 3), make([]float64, 3), make([]float64, 2)) }) {
   216  		t.Errorf("Kendall did not panic with weights length mismatch")
   217  	}
   218  }
   219  
   220  func ExampleCovariance() {
   221  	fmt.Println("Covariance computes the degree to which datasets move together")
   222  	fmt.Println("about their mean.")
   223  	x := []float64{8, -3, 7, 8, -4}
   224  	y := []float64{10, 2, 2, 4, 1}
   225  	cov := Covariance(x, y, nil)
   226  	fmt.Printf("Cov = %.4f\n", cov)
   227  	fmt.Println("If datasets move perfectly together, the variance equals the covariance")
   228  	y2 := []float64{12, 1, 11, 12, 0}
   229  	cov2 := Covariance(x, y2, nil)
   230  	varX := Variance(x, nil)
   231  	fmt.Printf("Cov2 is %.4f, VarX is %.4f", cov2, varX)
   232  	// Output:
   233  	// Covariance computes the degree to which datasets move together
   234  	// about their mean.
   235  	// Cov = 13.8000
   236  	// If datasets move perfectly together, the variance equals the covariance
   237  	// Cov2 is 37.7000, VarX is 37.7000
   238  }
   239  
   240  func TestCovariance(t *testing.T) {
   241  	for i, test := range []struct {
   242  		p       []float64
   243  		q       []float64
   244  		weights []float64
   245  		ans     float64
   246  	}{
   247  		{
   248  			p:   []float64{0.75, 0.1, 0.05},
   249  			q:   []float64{0.5, 0.25, 0.25},
   250  			ans: 0.05625,
   251  		},
   252  		{
   253  			p:   []float64{1, 2, 3},
   254  			q:   []float64{2, 4, 6},
   255  			ans: 2,
   256  		},
   257  		{
   258  			p:   []float64{1, 2, 3},
   259  			q:   []float64{1, 4, 9},
   260  			ans: 4,
   261  		},
   262  		{
   263  			p:       []float64{1, 2, 3},
   264  			q:       []float64{1, 4, 9},
   265  			weights: []float64{1, 1.5, 1},
   266  			ans:     3.2,
   267  		},
   268  		{
   269  			p:       []float64{1, 4, 9},
   270  			q:       []float64{1, 4, 9},
   271  			weights: []float64{1, 1.5, 1},
   272  			ans:     13.142857142857146,
   273  		},
   274  	} {
   275  		c := Covariance(test.p, test.q, test.weights)
   276  		if math.Abs(c-test.ans) > 1e-14 {
   277  			t.Errorf("Covariance mismatch case %d: Expected %v, Found %v", i, test.ans, c)
   278  		}
   279  	}
   280  
   281  	// test the panic states
   282  	if !panics(func() { Covariance(make([]float64, 2), make([]float64, 3), nil) }) {
   283  		t.Errorf("Covariance did not panic with x, y length mismatch")
   284  	}
   285  	if !panics(func() { Covariance(make([]float64, 3), make([]float64, 3), make([]float64, 2)) }) {
   286  		t.Errorf("Covariance did not panic with x, weights length mismatch")
   287  	}
   288  
   289  }
   290  
   291  func TestCrossEntropy(t *testing.T) {
   292  	for i, test := range []struct {
   293  		p   []float64
   294  		q   []float64
   295  		ans float64
   296  	}{
   297  		{
   298  			p:   []float64{0.75, 0.1, 0.05},
   299  			q:   []float64{0.5, 0.25, 0.25},
   300  			ans: 0.7278045395879426,
   301  		},
   302  		{
   303  			p:   []float64{0.75, 0.1, 0.05, 0, 0, 0},
   304  			q:   []float64{0.5, 0.25, 0.25, 0, 0, 0},
   305  			ans: 0.7278045395879426,
   306  		},
   307  		{
   308  			p:   []float64{0.75, 0.1, 0.05, 0, 0, 0.1},
   309  			q:   []float64{0.5, 0.25, 0.25, 0, 0, 0},
   310  			ans: math.Inf(1),
   311  		},
   312  		{
   313  			p:   nil,
   314  			q:   nil,
   315  			ans: 0,
   316  		},
   317  	} {
   318  		c := CrossEntropy(test.p, test.q)
   319  		if math.Abs(c-test.ans) > 1e-14 {
   320  			t.Errorf("Cross entropy mismatch case %d: Expected %v, Found %v", i, test.ans, c)
   321  		}
   322  	}
   323  	if !panics(func() { CrossEntropy(make([]float64, 3), make([]float64, 2)) }) {
   324  		t.Errorf("CrossEntropy did not panic with p, q length mismatch")
   325  	}
   326  }
   327  
   328  func ExampleEntropy() {
   329  
   330  	p := []float64{0.05, 0.1, 0.9, 0.05}
   331  	entP := Entropy(p)
   332  
   333  	q := []float64{0.2, 0.4, 0.25, 0.15}
   334  	entQ := Entropy(q)
   335  
   336  	r := []float64{0.2, 0, 0, 0.5, 0, 0.2, 0.1, 0, 0, 0}
   337  	entR := Entropy(r)
   338  
   339  	s := []float64{0, 0, 1, 0}
   340  	entS := Entropy(s)
   341  
   342  	fmt.Println("Entropy is a measure of the amount of uncertainty in a distribution")
   343  	fmt.Printf("The second bin of p is very likely to occur. It's entropy is %.4f\n", entP)
   344  	fmt.Printf("The distribution of q is more spread out. It's entropy is %.4f\n", entQ)
   345  	fmt.Println("Adding buckets with zero probability does not change the entropy.")
   346  	fmt.Printf("The entropy of r is: %.4f\n", entR)
   347  	fmt.Printf("A distribution with no uncertainty has entropy %.4f\n", entS)
   348  	// Output:
   349  	// Entropy is a measure of the amount of uncertainty in a distribution
   350  	// The second bin of p is very likely to occur. It's entropy is 0.6247
   351  	// The distribution of q is more spread out. It's entropy is 1.3195
   352  	// Adding buckets with zero probability does not change the entropy.
   353  	// The entropy of r is: 1.2206
   354  	// A distribution with no uncertainty has entropy 0.0000
   355  }
   356  
   357  func ExampleExKurtosis() {
   358  	fmt.Println(`Kurtosis is a measure of the 'peakedness' of a distribution, and the
   359  excess kurtosis is the kurtosis above or below that of the standard normal
   360  distribution`)
   361  	x := []float64{5, 4, -3, -2}
   362  	kurt := ExKurtosis(x, nil)
   363  	fmt.Printf("ExKurtosis = %.5f\n", kurt)
   364  	weights := []float64{1, 2, 3, 5}
   365  	wKurt := ExKurtosis(x, weights)
   366  	fmt.Printf("Weighted ExKurtosis is %.4f", wKurt)
   367  	// Output:
   368  	// Kurtosis is a measure of the 'peakedness' of a distribution, and the
   369  	// excess kurtosis is the kurtosis above or below that of the standard normal
   370  	// distribution
   371  	// ExKurtosis = -5.41200
   372  	// Weighted ExKurtosis is -0.6779
   373  }
   374  
   375  func TestExKurtosis(t *testing.T) {
   376  	// the example does a good job, this just has to cover the panic
   377  	if !panics(func() { ExKurtosis(make([]float64, 3), make([]float64, 2)) }) {
   378  		t.Errorf("ExKurtosis did not panic with x, weights length mismatch")
   379  	}
   380  }
   381  
   382  func ExampleGeometricMean() {
   383  	x := []float64{8, 2, 9, 15, 4}
   384  	weights := []float64{2, 2, 6, 7, 1}
   385  	mean := Mean(x, weights)
   386  	gmean := GeometricMean(x, weights)
   387  
   388  	logx := make([]float64, len(x))
   389  	for i, v := range x {
   390  		logx[i] = math.Log(v)
   391  	}
   392  	expMeanLog := math.Exp(Mean(logx, weights))
   393  	fmt.Printf("The arithmetic mean is %.4f, but the geometric mean is %.4f.\n", mean, gmean)
   394  	fmt.Printf("The exponential of the mean of the logs is %.4f\n", expMeanLog)
   395  	// Output:
   396  	// The arithmetic mean is 10.1667, but the geometric mean is 8.7637.
   397  	// The exponential of the mean of the logs is 8.7637
   398  }
   399  
   400  func TestGeometricMean(t *testing.T) {
   401  	for i, test := range []struct {
   402  		x   []float64
   403  		wts []float64
   404  		ans float64
   405  	}{
   406  		{
   407  			x:   []float64{2, 8},
   408  			ans: 4,
   409  		},
   410  		{
   411  			x:   []float64{3, 81},
   412  			wts: []float64{2, 1},
   413  			ans: 9,
   414  		},
   415  	} {
   416  		c := GeometricMean(test.x, test.wts)
   417  		if math.Abs(c-test.ans) > 1e-14 {
   418  			t.Errorf("Geometric mean mismatch case %d: Expected %v, Found %v", i, test.ans, c)
   419  		}
   420  	}
   421  	if !panics(func() { GeometricMean(make([]float64, 3), make([]float64, 2)) }) {
   422  		t.Errorf("GeometricMean did not panic with x, wts length mismatch")
   423  	}
   424  }
   425  
   426  func ExampleHarmonicMean() {
   427  	x := []float64{8, 2, 9, 15, 4}
   428  	weights := []float64{2, 2, 6, 7, 1}
   429  	mean := Mean(x, weights)
   430  	hmean := HarmonicMean(x, weights)
   431  
   432  	fmt.Printf("The arithmetic mean is %.5f, but the harmonic mean is %.4f.\n", mean, hmean)
   433  	// Output:
   434  	// The arithmetic mean is 10.16667, but the harmonic mean is 6.8354.
   435  }
   436  
   437  func TestHarmonicMean(t *testing.T) {
   438  	for i, test := range []struct {
   439  		x   []float64
   440  		wts []float64
   441  		ans float64
   442  	}{
   443  		{
   444  			x:   []float64{.5, .125},
   445  			ans: .2,
   446  		},
   447  		{
   448  			x:   []float64{.5, .125},
   449  			wts: []float64{2, 1},
   450  			ans: .25,
   451  		},
   452  	} {
   453  		c := HarmonicMean(test.x, test.wts)
   454  		if math.Abs(c-test.ans) > 1e-14 {
   455  			t.Errorf("Harmonic mean mismatch case %d: Expected %v, Found %v", i, test.ans, c)
   456  		}
   457  	}
   458  	if !panics(func() { HarmonicMean(make([]float64, 3), make([]float64, 2)) }) {
   459  		t.Errorf("HarmonicMean did not panic with x, wts length mismatch")
   460  	}
   461  }
   462  
   463  func TestHistogram(t *testing.T) {
   464  	for i, test := range []struct {
   465  		x        []float64
   466  		weights  []float64
   467  		dividers []float64
   468  		ans      []float64
   469  	}{
   470  		{
   471  			x:        []float64{1, 3, 5, 6, 7, 8},
   472  			dividers: []float64{0, 2, 4, 6, 7, 9},
   473  			ans:      []float64{1, 1, 1, 1, 2},
   474  		},
   475  		{
   476  			x:        []float64{1, 3, 5, 6, 7, 8},
   477  			dividers: []float64{1, 2, 4, 6, 7, 9},
   478  			weights:  []float64{1, 2, 1, 1, 1, 2},
   479  			ans:      []float64{1, 2, 1, 1, 3},
   480  		},
   481  		{
   482  			x:        []float64{1, 8},
   483  			dividers: []float64{0, 2, 4, 6, 7, 9},
   484  			weights:  []float64{1, 2},
   485  			ans:      []float64{1, 0, 0, 0, 2},
   486  		},
   487  		{
   488  			x:        []float64{1, 8},
   489  			dividers: []float64{0, 2, 4, 6, 7, 9},
   490  			ans:      []float64{1, 0, 0, 0, 1},
   491  		},
   492  		{
   493  			x:        []float64{},
   494  			dividers: []float64{1, 3},
   495  			ans:      []float64{0},
   496  		},
   497  	} {
   498  		hist := Histogram(nil, test.dividers, test.x, test.weights)
   499  		if !floats.Equal(hist, test.ans) {
   500  			t.Errorf("Hist mismatch case %d. Expected %v, Found %v", i, test.ans, hist)
   501  		}
   502  		// Test with non-zero values
   503  		Histogram(hist, test.dividers, test.x, test.weights)
   504  		if !floats.Equal(hist, test.ans) {
   505  			t.Errorf("Hist mismatch case %d. Expected %v, Found %v", i, test.ans, hist)
   506  		}
   507  	}
   508  	// panic cases
   509  	for _, test := range []struct {
   510  		name     string
   511  		x        []float64
   512  		weights  []float64
   513  		dividers []float64
   514  		count    []float64
   515  	}{
   516  		{
   517  			name:    "len(x) != len(weights)",
   518  			x:       []float64{1, 3, 5, 6, 7, 8},
   519  			weights: []float64{1, 1, 1, 1},
   520  		},
   521  		{
   522  			name:     "len(count) != len(dividers) - 1",
   523  			x:        []float64{1, 3, 5, 6, 7, 8},
   524  			dividers: []float64{1, 4, 9},
   525  			count:    make([]float64, 6),
   526  		},
   527  		{
   528  			name:     "dividers not sorted",
   529  			x:        []float64{1, 3, 5, 6, 7, 8},
   530  			dividers: []float64{0, -1, 0},
   531  		},
   532  		{
   533  			name:     "x not sorted",
   534  			x:        []float64{1, 5, 2, 9, 7, 8},
   535  			dividers: []float64{1, 4, 9},
   536  		},
   537  		{
   538  			name:     "fewer than 2 dividers",
   539  			x:        []float64{1, 2, 3},
   540  			dividers: []float64{5},
   541  		},
   542  		{
   543  			name:     "x too large",
   544  			x:        []float64{1, 2, 3},
   545  			dividers: []float64{1, 3},
   546  		},
   547  		{
   548  			name:     "x too small",
   549  			x:        []float64{1, 2, 3},
   550  			dividers: []float64{2, 3},
   551  		},
   552  	} {
   553  		if !panics(func() { Histogram(test.count, test.dividers, test.x, test.weights) }) {
   554  			t.Errorf("Histogram did not panic when %s", test.name)
   555  		}
   556  	}
   557  }
   558  
   559  func ExampleHistogram() {
   560  	x := make([]float64, 101)
   561  	for i := range x {
   562  		x[i] = 1.1 * float64(i) // x data ranges from 0 to 110
   563  	}
   564  	dividers := []float64{0, 7, 20, 100, 1000}
   565  	fmt.Println(`Histogram counts the amount of data in the bins specified by
   566  the dividers. In this data set, there are 7 data points less than 7 (between dividers[0]
   567  and dividers[1]), 12 data points between 7 and 20 (dividers[1] and dividers[2]),
   568  and 0 data points above 1000. Since dividers has length 5, there will be 4 bins.`)
   569  	hist := Histogram(nil, dividers, x, nil)
   570  	fmt.Printf("Hist = %v\n", hist)
   571  
   572  	fmt.Println()
   573  	fmt.Println("For ease, the floats Span function can be used to set the dividers")
   574  	nBins := 10
   575  	dividers = make([]float64, nBins+1)
   576  	min := floats.Min(x)
   577  	max := floats.Max(x)
   578  	// Increase the maximum divider so that the maximum value of x is contained
   579  	// within the last bucket.
   580  	max++
   581  	floats.Span(dividers, min, max)
   582  	// Span includes the min and the max. Trim the dividers to create 10 buckets
   583  	hist = Histogram(nil, dividers, x, nil)
   584  	fmt.Printf("Hist = %v\n", hist)
   585  	fmt.Println()
   586  	fmt.Println(`Histogram also works with weighted data, and allows reusing of
   587  the count field in order to avoid extra garbage`)
   588  	weights := make([]float64, len(x))
   589  	for i := range weights {
   590  		weights[i] = float64(i + 1)
   591  	}
   592  	Histogram(hist, dividers, x, weights)
   593  	fmt.Printf("Weighted Hist = %v\n", hist)
   594  
   595  	// Output:
   596  	// Histogram counts the amount of data in the bins specified by
   597  	// the dividers. In this data set, there are 7 data points less than 7 (between dividers[0]
   598  	// and dividers[1]), 12 data points between 7 and 20 (dividers[1] and dividers[2]),
   599  	// and 0 data points above 1000. Since dividers has length 5, there will be 4 bins.
   600  	// Hist = [7 12 72 10]
   601  	//
   602  	// For ease, the floats Span function can be used to set the dividers
   603  	// Hist = [11 10 10 10 10 10 10 10 10 10]
   604  	//
   605  	// Histogram also works with weighted data, and allows reusing of
   606  	// the count field in order to avoid extra garbage
   607  	// Weighted Hist = [66 165 265 365 465 565 665 765 865 965]
   608  }
   609  
   610  func TestJensenShannon(t *testing.T) {
   611  	for i, test := range []struct {
   612  		p []float64
   613  		q []float64
   614  	}{
   615  		{
   616  			p: []float64{0.5, 0.1, 0.3, 0.1},
   617  			q: []float64{0.1, 0.4, 0.25, 0.25},
   618  		},
   619  		{
   620  			p: []float64{0.4, 0.6, 0.0},
   621  			q: []float64{0.2, 0.2, 0.6},
   622  		},
   623  		{
   624  			p: []float64{0.1, 0.1, 0.0, 0.8},
   625  			q: []float64{0.6, 0.3, 0.0, 0.1},
   626  		},
   627  		{
   628  			p: []float64{0.5, 0.1, 0.3, 0.1},
   629  			q: []float64{0.5, 0, 0.25, 0.25},
   630  		},
   631  		{
   632  			p: []float64{0.5, 0.1, 0, 0.4},
   633  			q: []float64{0.1, 0.4, 0.25, 0.25},
   634  		},
   635  	} {
   636  
   637  		m := make([]float64, len(test.p))
   638  		p := test.p
   639  		q := test.q
   640  		floats.Add(m, p)
   641  		floats.Add(m, q)
   642  		floats.Scale(0.5, m)
   643  
   644  		js1 := 0.5*KullbackLeibler(p, m) + 0.5*KullbackLeibler(q, m)
   645  		js2 := JensenShannon(p, q)
   646  
   647  		if math.IsNaN(js2) {
   648  			t.Errorf("In case %v, JS distance is NaN", i)
   649  		}
   650  
   651  		if math.Abs(js1-js2) > 1e-14 {
   652  			t.Errorf("JS mismatch case %v. Expected %v, found %v.", i, js1, js2)
   653  		}
   654  	}
   655  	if !panics(func() { JensenShannon(make([]float64, 3), make([]float64, 2)) }) {
   656  		t.Errorf("JensenShannon did not panic with p, q length mismatch")
   657  	}
   658  }
   659  
   660  func TestKolmogorovSmirnov(t *testing.T) {
   661  	for i, test := range []struct {
   662  		x        []float64
   663  		xWeights []float64
   664  		y        []float64
   665  		yWeights []float64
   666  		dist     float64
   667  	}{
   668  
   669  		{
   670  			dist: 0,
   671  		},
   672  		{
   673  			x:    []float64{1},
   674  			dist: 1,
   675  		},
   676  		{
   677  			y:    []float64{1},
   678  			dist: 1,
   679  		},
   680  		{
   681  			x:        []float64{1},
   682  			xWeights: []float64{8},
   683  			dist:     1,
   684  		},
   685  		{
   686  			y:        []float64{1},
   687  			yWeights: []float64{8},
   688  			dist:     1,
   689  		},
   690  		{
   691  			x:        []float64{1},
   692  			xWeights: []float64{8},
   693  			y:        []float64{1},
   694  			yWeights: []float64{8},
   695  			dist:     0,
   696  		},
   697  		{
   698  			x:        []float64{1, 1, 1},
   699  			xWeights: []float64{2, 3, 7},
   700  			y:        []float64{1},
   701  			yWeights: []float64{8},
   702  			dist:     0,
   703  		},
   704  		{
   705  			x:        []float64{1, 1, 1, 1, 1},
   706  			y:        []float64{1, 1, 1},
   707  			yWeights: []float64{2, 5, 2},
   708  			dist:     0,
   709  		},
   710  
   711  		{
   712  			x:    []float64{1, 2, 3},
   713  			y:    []float64{1, 2, 3},
   714  			dist: 0,
   715  		},
   716  		{
   717  			x:        []float64{1, 2, 3},
   718  			y:        []float64{1, 2, 3},
   719  			yWeights: []float64{1, 1, 1},
   720  			dist:     0,
   721  		},
   722  
   723  		{
   724  			x:        []float64{1, 2, 3},
   725  			xWeights: []float64{1, 1, 1},
   726  			y:        []float64{1, 2, 3},
   727  			yWeights: []float64{1, 1, 1},
   728  			dist:     0,
   729  		},
   730  		{
   731  			x:        []float64{1, 2},
   732  			xWeights: []float64{2, 5},
   733  			y:        []float64{1, 1, 2, 2, 2, 2, 2},
   734  			dist:     0,
   735  		},
   736  		{
   737  			x:        []float64{1, 1, 2, 2, 2, 2, 2},
   738  			y:        []float64{1, 2},
   739  			yWeights: []float64{2, 5},
   740  			dist:     0,
   741  		},
   742  		{
   743  			x:        []float64{1, 1, 2, 2, 2},
   744  			xWeights: []float64{0.5, 1.5, 1, 2, 2},
   745  			y:        []float64{1, 2},
   746  			yWeights: []float64{2, 5},
   747  			dist:     0,
   748  		},
   749  		{
   750  			x:    []float64{1, 2, 3, 4},
   751  			y:    []float64{5, 6},
   752  			dist: 1,
   753  		},
   754  		{
   755  			x:    []float64{5, 6},
   756  			y:    []float64{1, 2, 3, 4},
   757  			dist: 1,
   758  		},
   759  		{
   760  			x:        []float64{5, 6},
   761  			xWeights: []float64{8, 7},
   762  			y:        []float64{1, 2, 3, 4},
   763  			dist:     1,
   764  		},
   765  		{
   766  			x:        []float64{5, 6},
   767  			xWeights: []float64{8, 7},
   768  			y:        []float64{1, 2, 3, 4},
   769  			yWeights: []float64{9, 2, 1, 6},
   770  			dist:     1,
   771  		},
   772  		{
   773  			x:        []float64{-4, 5, 6},
   774  			xWeights: []float64{0, 8, 7},
   775  			y:        []float64{1, 2, 3, 4},
   776  			yWeights: []float64{9, 2, 1, 6},
   777  			dist:     1,
   778  		},
   779  		{
   780  			x:        []float64{-4, -2, -2, 5, 6},
   781  			xWeights: []float64{0, 0, 0, 8, 7},
   782  			y:        []float64{1, 2, 3, 4},
   783  			yWeights: []float64{9, 2, 1, 6},
   784  			dist:     1,
   785  		},
   786  		{
   787  			x:    []float64{1, 2, 3},
   788  			y:    []float64{1, 1, 3},
   789  			dist: 1.0 / 3.0,
   790  		},
   791  		{
   792  			x:        []float64{1, 2, 3},
   793  			y:        []float64{1, 3},
   794  			yWeights: []float64{2, 1},
   795  			dist:     1.0 / 3.0,
   796  		},
   797  		{
   798  			x:        []float64{1, 2, 3},
   799  			xWeights: []float64{2, 2, 2},
   800  			y:        []float64{1, 3},
   801  			yWeights: []float64{2, 1},
   802  			dist:     1.0 / 3.0,
   803  		},
   804  		{
   805  			x:    []float64{2, 3, 4},
   806  			y:    []float64{1, 5},
   807  			dist: 1.0 / 2.0,
   808  		},
   809  		{
   810  			x:    []float64{1, 2, math.NaN()},
   811  			y:    []float64{1, 1, 3},
   812  			dist: math.NaN(),
   813  		},
   814  		{
   815  			x:    []float64{1, 2, 3},
   816  			y:    []float64{1, 1, math.NaN()},
   817  			dist: math.NaN(),
   818  		},
   819  	} {
   820  		dist := KolmogorovSmirnov(test.x, test.xWeights, test.y, test.yWeights)
   821  		if math.Abs(dist-test.dist) > 1e-14 && !(math.IsNaN(test.dist) && math.IsNaN(dist)) {
   822  			t.Errorf("Distance mismatch case %v: Expected: %v, Found: %v", i, test.dist, dist)
   823  		}
   824  	}
   825  	// panic cases
   826  	for _, test := range []struct {
   827  		name     string
   828  		x        []float64
   829  		xWeights []float64
   830  		y        []float64
   831  		yWeights []float64
   832  	}{
   833  		{
   834  			name:     "len(x) != len(xWeights)",
   835  			x:        []float64{1, 3, 5, 6, 7, 8},
   836  			xWeights: []float64{1, 1, 1, 1},
   837  		},
   838  		{
   839  			name:     "len(y) != len(yWeights)",
   840  			x:        []float64{1, 3, 5, 6, 7, 8},
   841  			y:        []float64{1, 3, 5, 6, 7, 8},
   842  			yWeights: []float64{1, 1, 1, 1},
   843  		},
   844  		{
   845  			name: "x not sorted",
   846  			x:    []float64{10, 3, 5, 6, 7, 8},
   847  			y:    []float64{1, 3, 5, 6, 7, 8},
   848  		},
   849  		{
   850  			name: "y not sorted",
   851  			x:    []float64{1, 3, 5, 6, 7, 8},
   852  			y:    []float64{10, 3, 5, 6, 7, 8},
   853  		},
   854  	} {
   855  		if !panics(func() { KolmogorovSmirnov(test.x, test.xWeights, test.y, test.yWeights) }) {
   856  			t.Errorf("KolmogorovSmirnov did not panic when %s", test.name)
   857  		}
   858  	}
   859  }
   860  
   861  func ExampleKullbackLeibler() {
   862  
   863  	p := []float64{0.05, 0.1, 0.9, 0.05}
   864  	q := []float64{0.2, 0.4, 0.25, 0.15}
   865  	s := []float64{0, 0, 1, 0}
   866  
   867  	klPQ := KullbackLeibler(p, q)
   868  	klPS := KullbackLeibler(p, s)
   869  	klPP := KullbackLeibler(p, p)
   870  
   871  	fmt.Println("Kullback-Leibler is one measure of the difference between two distributions")
   872  	fmt.Printf("The K-L distance between p and q is %.4f\n", klPQ)
   873  	fmt.Println("It is impossible for s and p to be the same distribution, because")
   874  	fmt.Println("the first bucket has zero probability in s and non-zero in p. Thus,")
   875  	fmt.Printf("the K-L distance between them is %.4f\n", klPS)
   876  	fmt.Printf("The K-L distance between identical distributions is %.4f\n", klPP)
   877  
   878  	// Kullback-Leibler is one measure of the difference between two distributions
   879  	// The K-L distance between p and q is 0.8900
   880  	// It is impossible for s and p to be the same distribution, because
   881  	// the first bucket has zero probability in s and non-zero in p. Thus,
   882  	// the K-L distance between them is +Inf
   883  	// The K-L distance between identical distributions is 0.0000
   884  }
   885  
   886  func TestKullbackLeibler(t *testing.T) {
   887  	if !panics(func() { KullbackLeibler(make([]float64, 3), make([]float64, 2)) }) {
   888  		t.Errorf("KullbackLeibler did not panic with p, q length mismatch")
   889  	}
   890  }
   891  
   892  var linearRegressionTests = []struct {
   893  	name string
   894  
   895  	x, y    []float64
   896  	weights []float64
   897  	origin  bool
   898  
   899  	alpha float64
   900  	beta  float64
   901  	r     float64
   902  
   903  	tol float64
   904  }{
   905  	{
   906  		name: "faithful",
   907  
   908  		x: faithful.waiting,
   909  		y: faithful.eruptions,
   910  
   911  		// Values calculated by R using lm(eruptions ~ waiting, data=faithful).
   912  		alpha: -1.87402,
   913  		beta:  0.07563,
   914  		r:     0.8114608,
   915  
   916  		tol: 1e-5,
   917  	},
   918  	{
   919  		name: "faithful through origin",
   920  
   921  		x:      faithful.waiting,
   922  		y:      faithful.eruptions,
   923  		origin: true,
   924  
   925  		// Values calculated by R using lm(eruptions ~ waiting - 1, data=faithful).
   926  		alpha: 0,
   927  		beta:  0.05013,
   928  		r:     0.9726036,
   929  
   930  		tol: 1e-5,
   931  	},
   932  	{
   933  		name: "faithful explicit weights",
   934  
   935  		x: faithful.waiting,
   936  		y: faithful.eruptions,
   937  		weights: func() []float64 {
   938  			w := make([]float64, len(faithful.eruptions))
   939  			for i := range w {
   940  				w[i] = 1
   941  			}
   942  			return w
   943  		}(),
   944  
   945  		// Values calculated by R using lm(eruptions ~ waiting, data=faithful).
   946  		alpha: -1.87402,
   947  		beta:  0.07563,
   948  		r:     0.8114608,
   949  
   950  		tol: 1e-5,
   951  	},
   952  	{
   953  		name: "faithful non-uniform weights",
   954  
   955  		x:       faithful.waiting,
   956  		y:       faithful.eruptions,
   957  		weights: faithful.waiting, // Just an arbitrary set of non-uniform weights.
   958  
   959  		// Values calculated by R using lm(eruptions ~ waiting, data=faithful, weights=faithful$waiting).
   960  		alpha: -1.79268,
   961  		beta:  0.07452,
   962  		r:     0.7840372,
   963  
   964  		tol: 1e-5,
   965  	},
   966  }
   967  
   968  func TestLinearRegression(t *testing.T) {
   969  	for _, test := range linearRegressionTests {
   970  		alpha, beta := LinearRegression(test.x, test.y, test.weights, test.origin)
   971  		var r float64
   972  		if test.origin {
   973  			r = RNoughtSquared(test.x, test.y, test.weights, beta)
   974  		} else {
   975  			r = RSquared(test.x, test.y, test.weights, alpha, beta)
   976  			ests := make([]float64, len(test.y))
   977  			for i, x := range test.x {
   978  				ests[i] = alpha + beta*x
   979  			}
   980  			rvals := RSquaredFrom(ests, test.y, test.weights)
   981  			if r != rvals {
   982  				t.Errorf("%s: RSquared and RSquaredFrom mismatch: %v != %v", test.name, r, rvals)
   983  			}
   984  		}
   985  		if !scalar.EqualWithinAbsOrRel(alpha, test.alpha, test.tol, test.tol) {
   986  			t.Errorf("%s: unexpected alpha estimate: want:%v got:%v", test.name, test.alpha, alpha)
   987  		}
   988  		if !scalar.EqualWithinAbsOrRel(beta, test.beta, test.tol, test.tol) {
   989  			t.Errorf("%s: unexpected beta estimate: want:%v got:%v", test.name, test.beta, beta)
   990  		}
   991  		if !scalar.EqualWithinAbsOrRel(r, test.r, test.tol, test.tol) {
   992  			t.Errorf("%s: unexpected r estimate: want:%v got:%v", test.name, test.r, r)
   993  		}
   994  	}
   995  }
   996  
   997  func BenchmarkLinearRegression(b *testing.B) {
   998  	rnd := rand.New(rand.NewSource(1))
   999  	slope, offset := 2.0, 3.0
  1000  
  1001  	maxn := 10000
  1002  	xs := make([]float64, maxn)
  1003  	ys := make([]float64, maxn)
  1004  	weights := make([]float64, maxn)
  1005  	for i := range xs {
  1006  		x := rnd.Float64()
  1007  		xs[i] = x
  1008  		ys[i] = slope*x + offset
  1009  		weights[i] = rnd.Float64()
  1010  	}
  1011  
  1012  	for _, n := range []int{10, 100, 1000, maxn} {
  1013  		for _, weighted := range []bool{true, false} {
  1014  			for _, origin := range []bool{true, false} {
  1015  				name := "n" + strconv.Itoa(n)
  1016  				if weighted {
  1017  					name += "wt"
  1018  				} else {
  1019  					name += "wf"
  1020  				}
  1021  				if origin {
  1022  					name += "ot"
  1023  				} else {
  1024  					name += "of"
  1025  				}
  1026  				b.Run(name, func(b *testing.B) {
  1027  					for i := 0; i < b.N; i++ {
  1028  						var ws []float64
  1029  						if weighted {
  1030  							ws = weights[:n]
  1031  						}
  1032  						LinearRegression(xs[:n], ys[:n], ws, origin)
  1033  					}
  1034  				})
  1035  			}
  1036  		}
  1037  	}
  1038  }
  1039  
  1040  func TestChiSquare(t *testing.T) {
  1041  	for i, test := range []struct {
  1042  		p   []float64
  1043  		q   []float64
  1044  		res float64
  1045  	}{
  1046  		{
  1047  			p:   []float64{16, 18, 16, 14, 12, 12},
  1048  			q:   []float64{16, 16, 16, 16, 16, 8},
  1049  			res: 3.5,
  1050  		},
  1051  		{
  1052  			p:   []float64{16, 18, 16, 14, 12, 12},
  1053  			q:   []float64{8, 20, 20, 16, 12, 12},
  1054  			res: 9.25,
  1055  		},
  1056  		{
  1057  			p:   []float64{40, 60, 30, 45},
  1058  			q:   []float64{50, 50, 50, 50},
  1059  			res: 12.5,
  1060  		},
  1061  		{
  1062  			p:   []float64{40, 60, 30, 45, 0, 0},
  1063  			q:   []float64{50, 50, 50, 50, 0, 0},
  1064  			res: 12.5,
  1065  		},
  1066  	} {
  1067  		resultpq := ChiSquare(test.p, test.q)
  1068  
  1069  		if math.Abs(resultpq-test.res) > 1e-10 {
  1070  			t.Errorf("ChiSquare distance mismatch in case %d. Expected %v, Found %v", i, test.res, resultpq)
  1071  		}
  1072  	}
  1073  	if !panics(func() { ChiSquare(make([]float64, 2), make([]float64, 3)) }) {
  1074  		t.Errorf("ChiSquare did not panic with length mismatch")
  1075  	}
  1076  }
  1077  
  1078  // panics returns true if the called function panics during evaluation.
  1079  func panics(fun func()) (b bool) {
  1080  	defer func() {
  1081  		err := recover()
  1082  		if err != nil {
  1083  			b = true
  1084  		}
  1085  	}()
  1086  	fun()
  1087  	return
  1088  }
  1089  
  1090  func TestBhattacharyya(t *testing.T) {
  1091  	for i, test := range []struct {
  1092  		p   []float64
  1093  		q   []float64
  1094  		res float64
  1095  	}{
  1096  		{
  1097  			p:   []float64{0.5, 0.1, 0.3, 0.1},
  1098  			q:   []float64{0.1, 0.4, 0.25, 0.25},
  1099  			res: 0.15597338718671386,
  1100  		},
  1101  		{
  1102  			p:   []float64{0.4, 0.6, 0.0},
  1103  			q:   []float64{0.2, 0.2, 0.6},
  1104  			res: 0.46322207765351153,
  1105  		},
  1106  		{
  1107  			p:   []float64{0.1, 0.1, 0.0, 0.8},
  1108  			q:   []float64{0.6, 0.3, 0.0, 0.1},
  1109  			res: 0.3552520032137785,
  1110  		},
  1111  	} {
  1112  		resultpq := Bhattacharyya(test.p, test.q)
  1113  		resultqp := Bhattacharyya(test.q, test.p)
  1114  
  1115  		if math.Abs(resultpq-test.res) > 1e-10 {
  1116  			t.Errorf("Bhattacharyya distance mismatch in case %d. Expected %v, Found %v", i, test.res, resultpq)
  1117  		}
  1118  		if math.Abs(resultpq-resultqp) > 1e-10 {
  1119  			t.Errorf("Bhattacharyya distance is asymmetric in case %d.", i)
  1120  		}
  1121  	}
  1122  	// Bhattacharyya should panic if the inputs have different length
  1123  	if !panics(func() { Bhattacharyya(make([]float64, 2), make([]float64, 3)) }) {
  1124  		t.Errorf("Bhattacharyya did not panic with length mismatch")
  1125  	}
  1126  }
  1127  
  1128  func TestHellinger(t *testing.T) {
  1129  	for i, test := range []struct {
  1130  		p   []float64
  1131  		q   []float64
  1132  		res float64
  1133  	}{
  1134  		{
  1135  			p:   []float64{0.5, 0.1, 0.3, 0.1},
  1136  			q:   []float64{0.1, 0.4, 0.25, 0.25},
  1137  			res: 0.3800237367441919,
  1138  		},
  1139  		{
  1140  			p:   []float64{0.4, 0.6, 0.0},
  1141  			q:   []float64{0.2, 0.2, 0.6},
  1142  			res: 0.6088900771170487,
  1143  		},
  1144  		{
  1145  			p:   []float64{0.1, 0.1, 0.0, 0.8},
  1146  			q:   []float64{0.6, 0.3, 0.0, 0.1},
  1147  			res: 0.5468118803484205,
  1148  		},
  1149  	} {
  1150  		resultpq := Hellinger(test.p, test.q)
  1151  		resultqp := Hellinger(test.q, test.p)
  1152  
  1153  		if math.Abs(resultpq-test.res) > 1e-10 {
  1154  			t.Errorf("Hellinger distance mismatch in case %d. Expected %v, Found %v", i, test.res, resultpq)
  1155  		}
  1156  		if math.Abs(resultpq-resultqp) > 1e-10 {
  1157  			t.Errorf("Hellinger distance is asymmetric in case %d.", i)
  1158  		}
  1159  	}
  1160  	if !panics(func() { Hellinger(make([]float64, 2), make([]float64, 3)) }) {
  1161  		t.Errorf("Hellinger did not panic with length mismatch")
  1162  	}
  1163  }
  1164  
  1165  func ExampleMean() {
  1166  	x := []float64{8.2, -6, 5, 7}
  1167  	mean := Mean(x, nil)
  1168  	fmt.Printf("The mean of the samples is %.4f\n", mean)
  1169  	w := []float64{2, 6, 3, 5}
  1170  	weightedMean := Mean(x, w)
  1171  	fmt.Printf("The weighted mean of the samples is %.4f\n", weightedMean)
  1172  	x2 := []float64{8.2, 8.2, -6, -6, -6, -6, -6, -6, 5, 5, 5, 7, 7, 7, 7, 7}
  1173  	mean2 := Mean(x2, nil)
  1174  	fmt.Printf("The mean of x2 is %.4f\n", mean2)
  1175  	fmt.Println("The weights act as if there were more samples of that number")
  1176  	// Output:
  1177  	// The mean of the samples is 3.5500
  1178  	// The weighted mean of the samples is 1.9000
  1179  	// The mean of x2 is 1.9000
  1180  	// The weights act as if there were more samples of that number
  1181  }
  1182  func TestMean(t *testing.T) {
  1183  	if !panics(func() { Mean(make([]float64, 3), make([]float64, 2)) }) {
  1184  		t.Errorf("Mean did not panic with x, weights length mismatch")
  1185  	}
  1186  }
  1187  
  1188  func TestMode(t *testing.T) {
  1189  	for i, test := range []struct {
  1190  		x       []float64
  1191  		weights []float64
  1192  		ans     float64
  1193  		count   float64
  1194  	}{
  1195  		{},
  1196  		{
  1197  			x:     []float64{1, 6, 1, 9, -2},
  1198  			ans:   1,
  1199  			count: 2,
  1200  		},
  1201  		{
  1202  			x:       []float64{1, 6, 1, 9, -2},
  1203  			weights: []float64{1, 7, 3, 5, 0},
  1204  			ans:     6,
  1205  			count:   7,
  1206  		},
  1207  	} {
  1208  		m, count := Mode(test.x, test.weights)
  1209  		if test.ans != m {
  1210  			t.Errorf("Mode mismatch case %d. Expected %v, found %v", i, test.ans, m)
  1211  		}
  1212  		if test.count != count {
  1213  			t.Errorf("Mode count mismatch case %d. Expected %v, found %v", i, test.count, count)
  1214  		}
  1215  	}
  1216  	if !panics(func() { Mode(make([]float64, 3), make([]float64, 2)) }) {
  1217  		t.Errorf("Mode did not panic with x, weights length mismatch")
  1218  	}
  1219  }
  1220  
  1221  func TestMixedMoment(t *testing.T) {
  1222  	for i, test := range []struct {
  1223  		x, y, weights []float64
  1224  		r, s          float64
  1225  		ans           float64
  1226  	}{
  1227  		{
  1228  			x:   []float64{10, 2, 1, 8, 5},
  1229  			y:   []float64{8, 15, 1, 6, 3},
  1230  			r:   1,
  1231  			s:   1,
  1232  			ans: 0.48,
  1233  		},
  1234  		{
  1235  			x:       []float64{10, 2, 1, 8, 5},
  1236  			y:       []float64{8, 15, 1, 6, 3},
  1237  			weights: []float64{1, 1, 1, 1, 1},
  1238  			r:       1,
  1239  			s:       1,
  1240  			ans:     0.48,
  1241  		},
  1242  		{
  1243  			x:       []float64{10, 2, 1, 8, 5},
  1244  			y:       []float64{8, 15, 1, 6, 3},
  1245  			weights: []float64{2, 3, 0.2, 8, 4},
  1246  			r:       1,
  1247  			s:       1,
  1248  			ans:     -4.786371011357490,
  1249  		},
  1250  		{
  1251  			x:       []float64{10, 2, 1, 8, 5},
  1252  			y:       []float64{8, 15, 1, 6, 3},
  1253  			weights: []float64{2, 3, 0.2, 8, 4},
  1254  			r:       2,
  1255  			s:       3,
  1256  			ans:     1.598600579313326e+03,
  1257  		},
  1258  	} {
  1259  		m := BivariateMoment(test.r, test.s, test.x, test.y, test.weights)
  1260  		if math.Abs(test.ans-m) > 1e-14 {
  1261  			t.Errorf("Moment mismatch case %d. Expected %v, found %v", i, test.ans, m)
  1262  		}
  1263  	}
  1264  	if !panics(func() { BivariateMoment(1, 1, make([]float64, 3), make([]float64, 2), nil) }) {
  1265  		t.Errorf("Moment did not panic with x, y length mismatch")
  1266  	}
  1267  	if !panics(func() { BivariateMoment(1, 1, make([]float64, 2), make([]float64, 3), nil) }) {
  1268  		t.Errorf("Moment did not panic with x, y length mismatch")
  1269  	}
  1270  	if !panics(func() { BivariateMoment(1, 1, make([]float64, 2), make([]float64, 2), make([]float64, 3)) }) {
  1271  		t.Errorf("Moment did not panic with x, weights length mismatch")
  1272  	}
  1273  	if !panics(func() { BivariateMoment(1, 1, make([]float64, 2), make([]float64, 2), make([]float64, 1)) }) {
  1274  		t.Errorf("Moment did not panic with x, weights length mismatch")
  1275  	}
  1276  }
  1277  
  1278  func TestMoment(t *testing.T) {
  1279  	for i, test := range []struct {
  1280  		x       []float64
  1281  		weights []float64
  1282  		moment  float64
  1283  		ans     float64
  1284  	}{
  1285  		{
  1286  			x:      []float64{6, 2, 4, 8, 10},
  1287  			moment: 5,
  1288  			ans:    0,
  1289  		},
  1290  		{
  1291  			x:       []float64{6, 2, 4, 8, 10},
  1292  			weights: []float64{1, 2, 2, 2, 1},
  1293  			moment:  5,
  1294  			ans:     121.875,
  1295  		},
  1296  	} {
  1297  		m := Moment(test.moment, test.x, test.weights)
  1298  		if math.Abs(test.ans-m) > 1e-14 {
  1299  			t.Errorf("Moment mismatch case %d. Expected %v, found %v", i, test.ans, m)
  1300  		}
  1301  	}
  1302  	if !panics(func() { Moment(1, make([]float64, 3), make([]float64, 2)) }) {
  1303  		t.Errorf("Moment did not panic with x, weights length mismatch")
  1304  	}
  1305  	if !panics(func() { Moment(1, make([]float64, 2), make([]float64, 3)) }) {
  1306  		t.Errorf("Moment did not panic with x, weights length mismatch")
  1307  	}
  1308  }
  1309  
  1310  func TestMomentAbout(t *testing.T) {
  1311  	for i, test := range []struct {
  1312  		x       []float64
  1313  		weights []float64
  1314  		moment  float64
  1315  		mean    float64
  1316  		ans     float64
  1317  	}{
  1318  		{
  1319  			x:      []float64{6, 2, 4, 8, 9},
  1320  			mean:   3,
  1321  			moment: 5,
  1322  			ans:    2.2288e3,
  1323  		},
  1324  		{
  1325  			x:       []float64{6, 2, 4, 8, 9},
  1326  			weights: []float64{1, 2, 2, 2, 1},
  1327  			mean:    3,
  1328  			moment:  5,
  1329  			ans:     1.783625e3,
  1330  		},
  1331  	} {
  1332  		m := MomentAbout(test.moment, test.x, test.mean, test.weights)
  1333  		if math.Abs(test.ans-m) > 1e-14 {
  1334  			t.Errorf("MomentAbout mismatch case %d. Expected %v, found %v", i, test.ans, m)
  1335  		}
  1336  	}
  1337  	if !panics(func() { MomentAbout(1, make([]float64, 3), 0, make([]float64, 2)) }) {
  1338  		t.Errorf("MomentAbout did not panic with x, weights length mismatch")
  1339  	}
  1340  }
  1341  
  1342  func TestCDF(t *testing.T) {
  1343  	cumulantKinds := []CumulantKind{Empirical}
  1344  	for i, test := range []struct {
  1345  		q       []float64
  1346  		x       []float64
  1347  		weights []float64
  1348  		ans     [][]float64
  1349  	}{
  1350  		{},
  1351  		{
  1352  			q:   []float64{0, 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1},
  1353  			x:   []float64{1, 2, 3, 4, 5},
  1354  			ans: [][]float64{{0, 0, 0.2, 0.2, 0.4, 0.6, 0.6, 0.8, 1, 1}},
  1355  		},
  1356  		{
  1357  			q:       []float64{0, 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1},
  1358  			x:       []float64{1, 2, 3, 4, 5},
  1359  			weights: []float64{1, 1, 1, 1, 1},
  1360  			ans:     [][]float64{{0, 0, 0.2, 0.2, 0.4, 0.6, 0.6, 0.8, 1, 1}},
  1361  		},
  1362  		{
  1363  			q:   []float64{0, 0.9, 1},
  1364  			x:   []float64{math.NaN()},
  1365  			ans: [][]float64{{math.NaN(), math.NaN(), math.NaN()}},
  1366  		},
  1367  	} {
  1368  		copyX := make([]float64, len(test.x))
  1369  		copy(copyX, test.x)
  1370  		var copyW []float64
  1371  		if test.weights != nil {
  1372  			copyW = make([]float64, len(test.weights))
  1373  			copy(copyW, test.weights)
  1374  		}
  1375  		for j, q := range test.q {
  1376  			for k, kind := range cumulantKinds {
  1377  				v := CDF(q, kind, test.x, test.weights)
  1378  				if !floats.Equal(copyX, test.x) && !math.IsNaN(v) {
  1379  					t.Errorf("x changed for case %d kind %d percentile %v", i, k, q)
  1380  				}
  1381  				if !floats.Equal(copyW, test.weights) {
  1382  					t.Errorf("x changed for case %d kind %d percentile %v", i, k, q)
  1383  				}
  1384  				if v != test.ans[k][j] && !(math.IsNaN(v) && math.IsNaN(test.ans[k][j])) {
  1385  					t.Errorf("mismatch case %d kind %d percentile %v. Expected: %v, found: %v", i, k, q, test.ans[k][j], v)
  1386  				}
  1387  			}
  1388  		}
  1389  	}
  1390  
  1391  	// these test cases should all result in a panic
  1392  	for i, test := range []struct {
  1393  		name    string
  1394  		q       float64
  1395  		kind    CumulantKind
  1396  		x       []float64
  1397  		weights []float64
  1398  	}{
  1399  		{
  1400  			name: "x == nil",
  1401  			kind: Empirical,
  1402  			x:    nil,
  1403  		},
  1404  		{
  1405  			name: "len(x) == 0",
  1406  			kind: Empirical,
  1407  			x:    []float64{},
  1408  		},
  1409  		{
  1410  			name:    "len(x) != len(weights)",
  1411  			q:       1.5,
  1412  			kind:    Empirical,
  1413  			x:       []float64{1, 2, 3, 4, 5},
  1414  			weights: []float64{1, 2, 3},
  1415  		},
  1416  		{
  1417  			name: "unsorted x",
  1418  			q:    1.5,
  1419  			kind: Empirical,
  1420  			x:    []float64{3, 2, 1},
  1421  		},
  1422  		{
  1423  			name: "unknown CumulantKind",
  1424  			q:    1.5,
  1425  			kind: CumulantKind(1000), // bogus
  1426  			x:    []float64{1, 2, 3},
  1427  		},
  1428  	} {
  1429  		if !panics(func() { CDF(test.q, test.kind, test.x, test.weights) }) {
  1430  			t.Errorf("did not panic as expected with %s for case %d kind %d percentile %v x %v weights %v", test.name, i, test.kind, test.q, test.x, test.weights)
  1431  		}
  1432  	}
  1433  
  1434  }
  1435  
  1436  func TestQuantile(t *testing.T) {
  1437  	cumulantKinds := []CumulantKind{
  1438  		Empirical,
  1439  		LinInterp,
  1440  	}
  1441  	for i, test := range []struct {
  1442  		p      []float64
  1443  		x      []float64
  1444  		w      []float64
  1445  		ans    [][]float64
  1446  		panics bool
  1447  	}{
  1448  		{
  1449  			p:      []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
  1450  			x:      nil,
  1451  			w:      nil,
  1452  			panics: true,
  1453  		},
  1454  		{
  1455  			p:      []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
  1456  			x:      []float64{},
  1457  			w:      nil,
  1458  			panics: true,
  1459  		},
  1460  		{
  1461  			p: []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
  1462  			x: []float64{1},
  1463  			w: nil,
  1464  			ans: [][]float64{
  1465  				{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
  1466  				{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
  1467  			},
  1468  		},
  1469  		{
  1470  			p: []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
  1471  			x: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
  1472  			w: nil,
  1473  			ans: [][]float64{
  1474  				{1, 1, 1, 2, 5, 5, 6, 9, 9, 10, 10},
  1475  				{1, 1, 1, 1.5, 4.5, 5, 5.5, 8.5, 9, 9.5, 10},
  1476  			},
  1477  		},
  1478  		{
  1479  			p: []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
  1480  			x: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
  1481  			w: []float64{3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
  1482  			ans: [][]float64{
  1483  				{1, 1, 1, 2, 5, 5, 6, 9, 9, 10, 10},
  1484  				{1, 1, 1, 1.5, 4.5, 5, 5.5, 8.5, 9, 9.5, 10},
  1485  			},
  1486  		},
  1487  		{
  1488  			p: []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
  1489  			x: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
  1490  			w: []float64{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0},
  1491  			ans: [][]float64{
  1492  				{1, 2, 3, 4, 7, 7, 8, 10, 10, 10, 10},
  1493  				{1, 1.875, 2.833333333333333, 3.5625, 6.535714285714286, 6.928571428571429, 7.281250000000001, 9.175, 9.45, 9.725, 10},
  1494  			},
  1495  		},
  1496  		{
  1497  			p: []float64{0.5},
  1498  			x: []float64{1, 2, 3, 4, 5, 6, 7, 8, math.NaN(), 10},
  1499  			ans: [][]float64{
  1500  				{math.NaN()},
  1501  				{math.NaN()},
  1502  			},
  1503  		},
  1504  	} {
  1505  		copyX := make([]float64, len(test.x))
  1506  		copy(copyX, test.x)
  1507  		var copyW []float64
  1508  		if test.w != nil {
  1509  			copyW = make([]float64, len(test.w))
  1510  			copy(copyW, test.w)
  1511  		}
  1512  		for j, p := range test.p {
  1513  			for k, kind := range cumulantKinds {
  1514  				var v float64
  1515  				if test.panics != panics(func() { v = Quantile(p, kind, test.x, test.w) }) {
  1516  					t.Errorf("Quantile did not panic when expected: test %d", j)
  1517  				}
  1518  				if !floats.Same(copyX, test.x) {
  1519  					t.Errorf("x changed for case %d kind %d percentile %v", i, k, p)
  1520  				}
  1521  				if !floats.Same(copyW, test.w) {
  1522  					t.Errorf("x changed for case %d kind %d percentile %v", i, k, p)
  1523  				}
  1524  				if test.panics {
  1525  					continue
  1526  				}
  1527  				if v != test.ans[k][j] && !(math.IsNaN(v) && math.IsNaN(test.ans[k][j])) {
  1528  					t.Errorf("mismatch case %d kind %d percentile %v. Expected: %v, found: %v", i, k, p, test.ans[k][j], v)
  1529  				}
  1530  			}
  1531  		}
  1532  	}
  1533  }
  1534  
  1535  func TestQuantileInvalidInput(t *testing.T) {
  1536  	cumulantKinds := []CumulantKind{
  1537  		Empirical,
  1538  		LinInterp,
  1539  	}
  1540  	for _, test := range []struct {
  1541  		name string
  1542  		p    float64
  1543  		x    []float64
  1544  		w    []float64
  1545  	}{
  1546  		{
  1547  			name: "p < 0",
  1548  			p:    -1,
  1549  		},
  1550  		{
  1551  			name: "p > 1",
  1552  			p:    2,
  1553  		},
  1554  		{
  1555  			name: "p is NaN",
  1556  			p:    math.NaN(),
  1557  		},
  1558  		{
  1559  			name: "len(x) != len(weights)",
  1560  			p:    .5,
  1561  			x:    make([]float64, 4),
  1562  			w:    make([]float64, 2),
  1563  		},
  1564  		{
  1565  			name: "x not sorted",
  1566  			p:    .5,
  1567  			x:    []float64{3, 2, 1},
  1568  		},
  1569  	} {
  1570  		for _, kind := range cumulantKinds {
  1571  			if !panics(func() { Quantile(test.p, kind, test.x, test.w) }) {
  1572  				t.Errorf("Quantile did not panic when %s", test.name)
  1573  			}
  1574  		}
  1575  	}
  1576  }
  1577  
  1578  func TestQuantileInvalidCumulantKind(t *testing.T) {
  1579  	if !panics(func() { Quantile(0.5, CumulantKind(1000), []float64{1, 2, 3}, nil) }) {
  1580  		t.Errorf("Quantile did not panic when CumulantKind is unknown")
  1581  	}
  1582  }
  1583  
  1584  func ExampleStdDev() {
  1585  	x := []float64{8, 2, -9, 15, 4}
  1586  	stdev := StdDev(x, nil)
  1587  	fmt.Printf("The standard deviation of the samples is %.4f\n", stdev)
  1588  
  1589  	weights := []float64{2, 2, 6, 7, 1}
  1590  	weightedStdev := StdDev(x, weights)
  1591  	fmt.Printf("The weighted standard deviation of the samples is %.4f\n", weightedStdev)
  1592  	// Output:
  1593  	// The standard deviation of the samples is 8.8034
  1594  	// The weighted standard deviation of the samples is 10.5733
  1595  }
  1596  
  1597  func ExamplePopStdDev() {
  1598  	x := []float64{8, 2, -9, 15, 4}
  1599  	stdev := PopStdDev(x, nil)
  1600  	fmt.Printf("The standard deviation of the population is %.4f\n", stdev)
  1601  
  1602  	weights := []float64{2, 2, 6, 7, 1}
  1603  	weightedStdev := PopStdDev(x, weights)
  1604  	fmt.Printf("The weighted standard deviation of the population is %.4f\n", weightedStdev)
  1605  	// Output:
  1606  	// The standard deviation of the population is 7.8740
  1607  	// The weighted standard deviation of the population is 10.2754
  1608  }
  1609  
  1610  func ExampleStdErr() {
  1611  	x := []float64{8, 2, -9, 15, 4}
  1612  	weights := []float64{2, 2, 6, 7, 1}
  1613  	mean := Mean(x, weights)
  1614  	stdev := StdDev(x, weights)
  1615  	nSamples := floats.Sum(weights)
  1616  	stdErr := StdErr(stdev, nSamples)
  1617  	fmt.Printf("The standard deviation is %.4f and there are %g samples, so the mean\nis likely %.4f ± %.4f.", stdev, nSamples, mean, stdErr)
  1618  	// Output:
  1619  	// The standard deviation is 10.5733 and there are 18 samples, so the mean
  1620  	// is likely 4.1667 ± 2.4921.
  1621  }
  1622  
  1623  func TestSkew(t *testing.T) {
  1624  	for i, test := range []struct {
  1625  		x       []float64
  1626  		weights []float64
  1627  		ans     float64
  1628  	}{
  1629  		{
  1630  			x:       []float64{8, 3, 7, 8, 4},
  1631  			weights: nil,
  1632  			ans:     -0.581456499151665,
  1633  		},
  1634  		{
  1635  			x:       []float64{8, 3, 7, 8, 4},
  1636  			weights: []float64{1, 1, 1, 1, 1},
  1637  			ans:     -0.581456499151665,
  1638  		},
  1639  		{
  1640  			x:       []float64{8, 3, 7, 8, 4},
  1641  			weights: []float64{2, 1, 2, 1, 1},
  1642  			ans:     -1.12066646837198,
  1643  		},
  1644  	} {
  1645  		skew := Skew(test.x, test.weights)
  1646  		if math.Abs(skew-test.ans) > 1e-14 {
  1647  			t.Errorf("Skew mismatch case %d. Expected %v, Found %v", i, test.ans, skew)
  1648  		}
  1649  	}
  1650  	if !panics(func() { Skew(make([]float64, 3), make([]float64, 2)) }) {
  1651  		t.Errorf("Skew did not panic with x, weights length mismatch")
  1652  	}
  1653  }
  1654  
  1655  func TestSortWeighted(t *testing.T) {
  1656  	for i, test := range []struct {
  1657  		x    []float64
  1658  		w    []float64
  1659  		ansx []float64
  1660  		answ []float64
  1661  	}{
  1662  		{
  1663  			x:    []float64{8, 3, 7, 8, 4},
  1664  			ansx: []float64{3, 4, 7, 8, 8},
  1665  		},
  1666  		{
  1667  			x:    []float64{8, 3, 7, 8, 4},
  1668  			w:    []float64{.5, 1, 1, .5, 1},
  1669  			ansx: []float64{3, 4, 7, 8, 8},
  1670  			answ: []float64{1, 1, 1, .5, .5},
  1671  		},
  1672  	} {
  1673  		SortWeighted(test.x, test.w)
  1674  		if !floats.Same(test.x, test.ansx) {
  1675  			t.Errorf("SortWeighted mismatch case %d. Expected x %v, Found x %v", i, test.ansx, test.x)
  1676  		}
  1677  		if !(test.w == nil) && !floats.Same(test.w, test.answ) {
  1678  			t.Errorf("SortWeighted mismatch case %d. Expected w %v, Found w %v", i, test.answ, test.w)
  1679  		}
  1680  	}
  1681  	if !panics(func() { SortWeighted(make([]float64, 3), make([]float64, 2)) }) {
  1682  		t.Errorf("SortWeighted did not panic with x, weights length mismatch")
  1683  	}
  1684  }
  1685  
  1686  func TestSortWeightedLabeled(t *testing.T) {
  1687  	for i, test := range []struct {
  1688  		x    []float64
  1689  		l    []bool
  1690  		w    []float64
  1691  		ansx []float64
  1692  		ansl []bool
  1693  		answ []float64
  1694  	}{
  1695  		{
  1696  			x:    []float64{8, 3, 7, 8, 4},
  1697  			ansx: []float64{3, 4, 7, 8, 8},
  1698  		},
  1699  		{
  1700  			x:    []float64{8, 3, 7, 8, 4},
  1701  			w:    []float64{.5, 1, 1, .5, 1},
  1702  			ansx: []float64{3, 4, 7, 8, 8},
  1703  			answ: []float64{1, 1, 1, .5, .5},
  1704  		},
  1705  		{
  1706  			x:    []float64{8, 3, 7, 8, 4},
  1707  			l:    []bool{false, false, true, false, true},
  1708  			ansx: []float64{3, 4, 7, 8, 8},
  1709  			ansl: []bool{false, true, true, false, false},
  1710  		},
  1711  		{
  1712  			x:    []float64{8, 3, 7, 8, 4},
  1713  			l:    []bool{false, false, true, false, true},
  1714  			w:    []float64{.5, 1, 1, .5, 1},
  1715  			ansx: []float64{3, 4, 7, 8, 8},
  1716  			ansl: []bool{false, true, true, false, false},
  1717  			answ: []float64{1, 1, 1, .5, .5},
  1718  		},
  1719  	} {
  1720  		SortWeightedLabeled(test.x, test.l, test.w)
  1721  		if !floats.Same(test.x, test.ansx) {
  1722  			t.Errorf("SortWeightedLabelled mismatch case %d. Expected x %v, Found x %v", i, test.ansx, test.x)
  1723  		}
  1724  		if (test.l != nil) && !reflect.DeepEqual(test.l, test.ansl) {
  1725  			t.Errorf("SortWeightedLabelled mismatch case %d. Expected l %v, Found l %v", i, test.ansl, test.l)
  1726  		}
  1727  		if (test.w != nil) && !floats.Same(test.w, test.answ) {
  1728  			t.Errorf("SortWeightedLabelled mismatch case %d. Expected w %v, Found w %v", i, test.answ, test.w)
  1729  		}
  1730  	}
  1731  	if !panics(func() { SortWeightedLabeled(make([]float64, 3), make([]bool, 2), make([]float64, 3)) }) {
  1732  		t.Errorf("SortWeighted did not panic with x, labels length mismatch")
  1733  	}
  1734  	if !panics(func() { SortWeightedLabeled(make([]float64, 3), make([]bool, 2), nil) }) {
  1735  		t.Errorf("SortWeighted did not panic with x, labels length mismatch")
  1736  	}
  1737  	if !panics(func() { SortWeightedLabeled(make([]float64, 3), make([]bool, 3), make([]float64, 2)) }) {
  1738  		t.Errorf("SortWeighted did not panic with x, weights length mismatch")
  1739  	}
  1740  	if !panics(func() { SortWeightedLabeled(make([]float64, 3), nil, make([]float64, 2)) }) {
  1741  		t.Errorf("SortWeighted did not panic with x, weights length mismatch")
  1742  	}
  1743  }
  1744  
  1745  func TestVariance(t *testing.T) {
  1746  	for i, test := range []struct {
  1747  		x       []float64
  1748  		weights []float64
  1749  		ans     float64
  1750  	}{
  1751  		{
  1752  			x:       []float64{8, -3, 7, 8, -4},
  1753  			weights: nil,
  1754  			ans:     37.7,
  1755  		},
  1756  		{
  1757  			x:       []float64{8, -3, 7, 8, -4},
  1758  			weights: []float64{1, 1, 1, 1, 1},
  1759  			ans:     37.7,
  1760  		},
  1761  		{
  1762  			x:       []float64{8, 3, 7, 8, 4},
  1763  			weights: []float64{2, 1, 2, 1, 1},
  1764  			ans:     4.2857142857142865,
  1765  		},
  1766  		{
  1767  			x:       []float64{1, 4, 9},
  1768  			weights: []float64{1, 1.5, 1},
  1769  			ans:     13.142857142857146,
  1770  		},
  1771  		{
  1772  			x:       []float64{1, 2, 3},
  1773  			weights: []float64{1, 1.5, 1},
  1774  			ans:     .8,
  1775  		},
  1776  	} {
  1777  		variance := Variance(test.x, test.weights)
  1778  		if math.Abs(variance-test.ans) > 1e-14 {
  1779  			t.Errorf("Variance mismatch case %d. Expected %v, Found %v", i, test.ans, variance)
  1780  		}
  1781  	}
  1782  	if !panics(func() { Variance(make([]float64, 3), make([]float64, 2)) }) {
  1783  		t.Errorf("Variance did not panic with x, weights length mismatch")
  1784  	}
  1785  }
  1786  
  1787  func TestPopVariance(t *testing.T) {
  1788  	for i, test := range []struct {
  1789  		x       []float64
  1790  		weights []float64
  1791  		ans     float64
  1792  	}{
  1793  		{
  1794  			x:       []float64{8, -3, 7, 8, -4},
  1795  			weights: nil,
  1796  			ans:     30.16,
  1797  		},
  1798  		{
  1799  			x:       []float64{8, -3, 7, 8, -4},
  1800  			weights: []float64{1, 1, 1, 1, 1},
  1801  			ans:     30.16,
  1802  		},
  1803  		{
  1804  			x:       []float64{8, 3, 7, 8, 4},
  1805  			weights: []float64{2, 1, 2, 1, 1},
  1806  			ans:     3.6734693877551026,
  1807  		},
  1808  		{
  1809  			x:       []float64{1, 4, 9},
  1810  			weights: []float64{1, 1.5, 1},
  1811  			ans:     9.387755102040817,
  1812  		},
  1813  		{
  1814  			x:       []float64{1, 2, 3},
  1815  			weights: []float64{1, 1.5, 1},
  1816  			ans:     0.5714285714285714,
  1817  		},
  1818  		{
  1819  			x:       []float64{2},
  1820  			weights: nil,
  1821  			ans:     0,
  1822  		},
  1823  		{
  1824  			x:       []float64{2},
  1825  			weights: []float64{2},
  1826  			ans:     0,
  1827  		},
  1828  	} {
  1829  		variance := PopVariance(test.x, test.weights)
  1830  		if math.Abs(variance-test.ans) > 1e-14 {
  1831  			t.Errorf("PopVariance mismatch case %d. Expected %v, Found %v", i, test.ans, variance)
  1832  		}
  1833  	}
  1834  	if !panics(func() { PopVariance(make([]float64, 3), make([]float64, 2)) }) {
  1835  		t.Errorf("PopVariance did not panic with x, weights length mismatch")
  1836  	}
  1837  }
  1838  
  1839  func ExampleVariance() {
  1840  	x := []float64{8, 2, -9, 15, 4}
  1841  	variance := Variance(x, nil)
  1842  	fmt.Printf("The variance of the samples is %.4f\n", variance)
  1843  
  1844  	weights := []float64{2, 2, 6, 7, 1}
  1845  	weightedVariance := Variance(x, weights)
  1846  	fmt.Printf("The weighted variance of the samples is %.4f\n", weightedVariance)
  1847  	// Output:
  1848  	// The variance of the samples is 77.5000
  1849  	// The weighted variance of the samples is 111.7941
  1850  }
  1851  
  1852  func ExamplePopVariance() {
  1853  	x := []float64{8, 2, -9, 15, 4}
  1854  	variance := PopVariance(x, nil)
  1855  	fmt.Printf("The biased variance of the samples is %.4f\n", variance)
  1856  
  1857  	weights := []float64{2, 2, 6, 7, 1}
  1858  	weightedVariance := PopVariance(x, weights)
  1859  	fmt.Printf("The weighted biased variance of the samples is %.4f\n", weightedVariance)
  1860  	// Output:
  1861  	// The biased variance of the samples is 62.0000
  1862  	// The weighted biased variance of the samples is 105.5833
  1863  }
  1864  
  1865  func TestStdScore(t *testing.T) {
  1866  	for i, test := range []struct {
  1867  		x float64
  1868  		u float64
  1869  		s float64
  1870  		z float64
  1871  	}{
  1872  		{
  1873  			x: 4,
  1874  			u: -6,
  1875  			s: 5,
  1876  			z: 2,
  1877  		},
  1878  		{
  1879  			x: 1,
  1880  			u: 0,
  1881  			s: 1,
  1882  			z: 1,
  1883  		},
  1884  	} {
  1885  		z := StdScore(test.x, test.u, test.s)
  1886  		if math.Abs(z-test.z) > 1e-14 {
  1887  			t.Errorf("StdScore mismatch case %d. Expected %v, Found %v", i, test.z, z)
  1888  		}
  1889  	}
  1890  }