func TestSortWeighted(t *testing.T) { for i, test := range []struct { x []float64 w []float64 ansx []float64 answ []float64 }{ { x: []float64{8, 3, 7, 8, 4}, ansx: []float64{3, 4, 7, 8, 8}, }, { x: []float64{8, 3, 7, 8, 4}, w: []float64{.5, 1, 1, .5, 1}, ansx: []float64{3, 4, 7, 8, 8}, answ: []float64{1, 1, 1, .5, .5}, }, } { SortWeighted(test.x, test.w) if !floats.Same(test.x, test.ansx) { t.Errorf("SortWeighted mismatch case %d. Expected x %v, Found x %v", i, test.ansx, test.x) } if !(test.w == nil) && !floats.Same(test.w, test.answ) { t.Errorf("SortWeighted mismatch case %d. Expected w %v, Found w %v", i, test.answ, test.w) } } if !Panics(func() { SortWeighted(make([]float64, 3), make([]float64, 2)) }) { t.Errorf("SortWeighted did not panic with x, weights length mismatch") } }
func TestSortWeightedLabeled(t *testing.T) { for i, test := range []struct { x []float64 l []bool w []float64 ansx []float64 ansl []bool answ []float64 }{ { x: []float64{8, 3, 7, 8, 4}, ansx: []float64{3, 4, 7, 8, 8}, }, { x: []float64{8, 3, 7, 8, 4}, w: []float64{.5, 1, 1, .5, 1}, ansx: []float64{3, 4, 7, 8, 8}, answ: []float64{1, 1, 1, .5, .5}, }, { x: []float64{8, 3, 7, 8, 4}, l: []bool{false, false, true, false, true}, ansx: []float64{3, 4, 7, 8, 8}, ansl: []bool{false, true, true, false, false}, }, { x: []float64{8, 3, 7, 8, 4}, l: []bool{false, false, true, false, true}, w: []float64{.5, 1, 1, .5, 1}, ansx: []float64{3, 4, 7, 8, 8}, ansl: []bool{false, true, true, false, false}, answ: []float64{1, 1, 1, .5, .5}, }, } { SortWeightedLabeled(test.x, test.l, test.w) if !floats.Same(test.x, test.ansx) { t.Errorf("SortWeightedLabelled mismatch case %d. Expected x %v, Found x %v", i, test.ansx, test.x) } if (test.l != nil) && !reflect.DeepEqual(test.l, test.ansl) { t.Errorf("SortWeightedLabelled mismatch case %d. Expected l %v, Found l %v", i, test.ansl, test.l) } if (test.w != nil) && !floats.Same(test.w, test.answ) { t.Errorf("SortWeightedLabelled mismatch case %d. Expected w %v, Found w %v", i, test.answ, test.w) } } if !Panics(func() { SortWeightedLabeled(make([]float64, 3), make([]bool, 2), make([]float64, 3)) }) { t.Errorf("SortWeighted did not panic with x, labels length mismatch") } if !Panics(func() { SortWeightedLabeled(make([]float64, 3), make([]bool, 2), nil) }) { t.Errorf("SortWeighted did not panic with x, labels length mismatch") } if !Panics(func() { SortWeightedLabeled(make([]float64, 3), make([]bool, 3), make([]float64, 2)) }) { t.Errorf("SortWeighted did not panic with x, weights length mismatch") } if !Panics(func() { SortWeightedLabeled(make([]float64, 3), nil, make([]float64, 2)) }) { t.Errorf("SortWeighted did not panic with x, weights length mismatch") } }
func sameAnswerF64SliceOfSlice(a, b interface{}) bool { for i, v := range a.([][]float64) { if same := floats.Same(v, b.([][]float64)[i]); !same { return false } } return true }
// Test cases where calculated manually. func TestROC(t *testing.T) { cases := []struct { y []float64 c []bool w []float64 n int wantTPR []float64 wantFPR []float64 }{ { y: []float64{0, 3, 5, 6, 7.5, 8}, c: []bool{true, false, true, false, false, false}, wantTPR: []float64{0, 0.5, 0.5, 1, 1, 1, 1}, wantFPR: []float64{0, 0, 0.25, 0.25, 0.5, 0.75, 1}, }, { y: []float64{0, 3, 5, 6, 7.5, 8}, c: []bool{true, false, true, false, false, false}, w: []float64{4, 1, 6, 3, 2, 2}, wantTPR: []float64{0, 0.4, 0.4, 1, 1, 1, 1}, wantFPR: []float64{0, 0, 0.125, 0.125, 0.5, 0.75, 1}, }, { y: []float64{0, 3, 5, 6, 7.5, 8}, c: []bool{true, false, true, false, false, false}, n: int(5), wantTPR: []float64{0, 0.5, 0.5, 1, 1}, wantFPR: []float64{0, 0, 0.25, 0.5, 1}, }, { y: []float64{0, 3, 5, 6, 7.5, 8}, c: []bool{true, false, true, false, false, false}, n: int(9), wantTPR: []float64{0, 0.5, 0.5, 0.5, 0.5, 1, 1, 1, 1}, wantFPR: []float64{0, 0, 0, 0.25, 0.25, 0.25, 0.5, 0.5, 1}, }, { y: []float64{0, 3, 5, 6, 7.5, 8}, c: []bool{true, false, true, false, false, false}, w: []float64{4, 1, 6, 3, 2, 2}, n: int(5), wantTPR: []float64{0, 0.4, 0.4, 1, 1}, wantFPR: []float64{0, 0, 0.125, 0.5, 1}, }, { y: []float64{0, 3, 5, 6, 7.5, 8}, c: []bool{true, false, true, false, false, false}, w: []float64{4, 1, 6, 3, 2, 2}, n: int(9), wantTPR: []float64{0, 0.4, 0.4, 0.4, 0.4, 1, 1, 1, 1}, wantFPR: []float64{0, 0, 0, 0.125, 0.125, 0.125, 0.5, 0.5, 1}, }, { y: []float64{0, 3, 6, 6, 6, 8}, c: []bool{true, false, true, false, false, false}, wantTPR: []float64{0, 0.5, 0.5, 1, 1}, wantFPR: []float64{0, 0, 0.25, 0.75, 1}, }, { y: []float64{0, 3, 6, 6, 6, 8}, c: []bool{true, false, true, false, false, false}, w: []float64{4, 1, 6, 3, 2, 2}, wantTPR: []float64{0, 0.4, 0.4, 1, 1}, wantFPR: []float64{0, 0, 0.125, 0.75, 1}, }, { y: []float64{0, 3, 6, 6, 6, 8}, c: []bool{true, false, true, false, false, false}, n: int(5), wantTPR: []float64{0, 0.5, 0.5, 1, 1}, wantFPR: []float64{0, 0, 0.25, 0.75, 1}, }, { y: []float64{0, 3, 6, 6, 6, 8}, c: []bool{true, false, true, false, false, false}, n: int(9), wantTPR: []float64{0, 0.5, 0.5, 0.5, 0.5, 0.5, 1, 1, 1}, wantFPR: []float64{0, 0, 0, 0.25, 0.25, 0.25, 0.75, 0.75, 1}, }, { y: []float64{0, 3, 6, 6, 6, 8}, c: []bool{true, false, true, false, false, false}, w: []float64{4, 1, 6, 3, 2, 2}, n: int(5), wantTPR: []float64{0, 0.4, 0.4, 1, 1}, wantFPR: []float64{0, 0, 0.125, 0.75, 1}, }, { y: []float64{0, 3, 6, 6, 6, 8}, c: []bool{true, false, true, false, false, false}, w: []float64{4, 1, 6, 3, 2, 2}, n: int(9), wantTPR: []float64{0, 0.4, 0.4, 0.4, 0.4, 0.4, 1, 1, 1}, wantFPR: []float64{0, 0, 0, 0.125, 0.125, 0.125, 0.75, 0.75, 1}, }, { y: []float64{1, 2}, c: []bool{true, true}, wantTPR: []float64{0, 0.5, 1}, wantFPR: []float64{0, 0, 1}, }, { y: []float64{1, 2}, c: []bool{true, true}, n: int(2), wantTPR: []float64{0, 1}, wantFPR: []float64{0, 1}, }, { y: []float64{1, 2}, c: []bool{true, true}, n: int(7), wantTPR: []float64{0, 0.5, 0.5, 0.5, 0.5, 0.5, 1}, wantFPR: []float64{0, 0, 0, 0, 0, 0, 1}, }, { y: []float64{1}, c: []bool{true}, wantTPR: []float64{0, 1}, wantFPR: []float64{0, 1}, }, { y: []float64{1}, c: []bool{true}, n: int(2), wantTPR: []float64{0, 1}, wantFPR: []float64{0, 1}, }, { y: []float64{1}, c: []bool{false}, wantTPR: []float64{0, 1}, wantFPR: []float64{0, 1}, }, { y: []float64{0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 10}, c: []bool{true, false, true, true, false, false, true}, n: int(5), wantTPR: []float64{0, 0.75, 0.75, 0.75, 1}, wantFPR: []float64{0, 1, 1, 1, 1}, }, { y: []float64{}, c: []bool{}, wantTPR: nil, wantFPR: nil, }, { y: []float64{}, c: []bool{}, n: int(5), wantTPR: nil, wantFPR: nil, }, } for i, test := range cases { gotTPR, gotFPR := ROC(test.n, test.y, test.c, test.w) if !floats.Same(gotTPR, test.wantTPR) { t.Errorf("%d: unexpected TPR got:%v want:%v", i, gotTPR, test.wantTPR) } if !floats.Same(gotFPR, test.wantFPR) { t.Errorf("%d: unexpected FPR got:%v want:%v", i, gotFPR, test.wantFPR) } } }
func TestFlattenBanded(t *testing.T) { for i, test := range []struct { dense [][]float64 ku int kl int condensed [][]float64 }{ { dense: [][]float64{{3}}, ku: 0, kl: 0, condensed: [][]float64{{3}}, }, { dense: [][]float64{ {3, 4, 0}, }, ku: 1, kl: 0, condensed: [][]float64{ {3, 4}, }, }, { dense: [][]float64{ {3, 4, 0, 0, 0}, }, ku: 1, kl: 0, condensed: [][]float64{ {3, 4}, }, }, { dense: [][]float64{ {3, 4, 0}, {0, 5, 8}, {0, 0, 2}, {0, 0, 0}, {0, 0, 0}, }, ku: 1, kl: 0, condensed: [][]float64{ {3, 4}, {5, 8}, {2, math.NaN()}, {math.NaN(), math.NaN()}, {math.NaN(), math.NaN()}, }, }, { dense: [][]float64{ {3, 4, 6}, {0, 5, 8}, {0, 0, 2}, {0, 0, 0}, {0, 0, 0}, }, ku: 2, kl: 0, condensed: [][]float64{ {3, 4, 6}, {5, 8, math.NaN()}, {2, math.NaN(), math.NaN()}, {math.NaN(), math.NaN(), math.NaN()}, {math.NaN(), math.NaN(), math.NaN()}, }, }, { dense: [][]float64{ {3, 4, 6}, {1, 5, 8}, {0, 6, 2}, {0, 0, 7}, {0, 0, 0}, }, ku: 2, kl: 1, condensed: [][]float64{ {math.NaN(), 3, 4, 6}, {1, 5, 8, math.NaN()}, {6, 2, math.NaN(), math.NaN()}, {7, math.NaN(), math.NaN(), math.NaN()}, {math.NaN(), math.NaN(), math.NaN(), math.NaN()}, }, }, { dense: [][]float64{ {1, 2, 0}, {3, 4, 5}, {6, 7, 8}, {0, 9, 10}, {0, 0, 11}, }, ku: 1, kl: 2, condensed: [][]float64{ {math.NaN(), math.NaN(), 1, 2}, {math.NaN(), 3, 4, 5}, {6, 7, 8, math.NaN()}, {9, 10, math.NaN(), math.NaN()}, {11, math.NaN(), math.NaN(), math.NaN()}, }, }, { dense: [][]float64{ {1, 0, 0}, {3, 4, 0}, {6, 7, 8}, {0, 9, 10}, {0, 0, 11}, }, ku: 0, kl: 2, condensed: [][]float64{ {math.NaN(), math.NaN(), 1}, {math.NaN(), 3, 4}, {6, 7, 8}, {9, 10, math.NaN()}, {11, math.NaN(), math.NaN()}, }, }, { dense: [][]float64{ {1, 0, 0, 0, 0}, {3, 4, 0, 0, 0}, {1, 3, 5, 0, 0}, }, ku: 0, kl: 2, condensed: [][]float64{ {math.NaN(), math.NaN(), 1}, {math.NaN(), 3, 4}, {1, 3, 5}, }, }, } { condensed := flattenBanded(test.dense, test.ku, test.kl) correct := flatten(test.condensed) if !floats.Same(condensed, correct) { t.Errorf("Case %v mismatch. Want %v, got %v.", i, correct, condensed) } } }
func SameF64S(str string, c, native []float64) { if !floats.Same(c, native) { panic(fmt.Sprintf("Case %s: []float64 mismatch. c = %v, native = %v.", str, c, native)) } }
func TestQuantile(t *testing.T) { cumulantKinds := []CumulantKind{Empirical} for i, test := range []struct { p []float64 x []float64 w []float64 ans [][]float64 }{ { p: []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1}, x: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, w: nil, ans: [][]float64{{1, 1, 1, 2, 5, 5, 6, 9, 9, 10, 10}}, }, { p: []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1}, x: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, w: []float64{3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, ans: [][]float64{{1, 1, 1, 2, 5, 5, 6, 9, 9, 10, 10}}, }, { p: []float64{0.5}, x: []float64{1, 2, 3, 4, 5, 6, 7, 8, math.NaN(), 10}, ans: [][]float64{{math.NaN()}}, }, } { copyX := make([]float64, len(test.x)) copy(copyX, test.x) var copyW []float64 if test.w != nil { copyW = make([]float64, len(test.w)) copy(copyW, test.w) } for j, p := range test.p { for k, kind := range cumulantKinds { v := Quantile(p, kind, test.x, test.w) if !floats.Same(copyX, test.x) { t.Errorf("x changed for case %d kind %d percentile %v", i, k, p) } if !floats.Same(copyW, test.w) { t.Errorf("x changed for case %d kind %d percentile %v", i, k, p) } if v != test.ans[k][j] && !(math.IsNaN(v) && math.IsNaN(test.ans[k][j])) { t.Errorf("mismatch case %d kind %d percentile %v. Expected: %v, found: %v", i, k, p, test.ans[k][j], v) } } } } // panic cases for _, test := range []struct { name string p float64 c CumulantKind x []float64 w []float64 }{ { name: "p < 0", c: Empirical, p: -1, }, { name: "p > 1", c: Empirical, p: 2, }, { name: "p is NaN", c: Empirical, p: math.NaN(), }, { name: "len(x) != len(weights)", c: Empirical, p: .5, x: make([]float64, 4), w: make([]float64, 2), }, { name: "x not sorted", c: Empirical, p: .5, x: []float64{3, 2, 1}, }, { name: "CumulantKind is unknown", c: CumulantKind(1000), p: .5, x: []float64{1, 2, 3}, }, } { if !Panics(func() { Quantile(test.p, test.c, test.x, test.w) }) { t.Errorf("Quantile did not panic when %s", test.name) } } }