blob: 92b14868dfa2178280258a93387268955f4062a3 [file] [log] [blame]
package stats
import (
"math"
"strings"
"testing"
)
func TestRank(t *testing.T) {
for _, test := range []struct {
testName string
nums []float64
result []float64
hasTie bool
}{
{
testName: "Input nums has ties",
nums: []float64{10, 10, 30, 40, 50},
result: []float64{1.5, 1.5, 3, 4, 5},
hasTie: true,
}, {
testName: "Input nums does not have ties",
nums: []float64{10, 20, 30, 40, 50},
result: []float64{1, 2, 3, 4, 5},
hasTie: false,
},
} {
t.Run(test.testName, func(t *testing.T) {
gotRes, gotTie := rank(test.nums)
if gotTie != test.hasTie {
t.Errorf("Expected (%v) and got (%v) tie doesn't match", test.hasTie, gotTie)
}
for i, ele := range test.result {
if gotRes[i] != ele {
t.Errorf("Expected (%v) and got (%v) res doesn't match", ele, gotRes[i])
}
}
})
}
}
func TestWilcoxonSignedRankedTest(t *testing.T) {
for _, test := range []struct {
testName string
x []float64
y []float64
alt Hypothesis
result *WilcoxonSignedRankedTestResult
expectError bool
expectErrMsg string
}{
{
testName: "Input x is missing",
x: nil,
y: nil,
alt: TwoSided,
result: nil,
expectError: true,
expectErrMsg: "x is missing",
}, {
testName: "Length of input x and y do not match",
x: []float64{1.0},
y: []float64{1.0, 2.0},
alt: TwoSided,
result: nil,
expectError: true,
expectErrMsg: "same length",
}, {
testName: "Paired sample (TwoSided) test with exact estimation",
x: []float64{447, 832, 640, 286, 501, 123},
y: []float64{241, 608, 130, 951, 604, 690},
alt: TwoSided,
result: &WilcoxonSignedRankedTestResult{Estimate: -77.5, LowerCi: -665, UpperCi: 510, PValue: 0.84375},
expectError: false,
}, {
testName: "Paired sample (Less) test with exact estimation",
x: []float64{447, 832, 640, 286, 501, 123},
y: []float64{241, 608, 130, 951, 604, 690},
alt: Less,
result: &WilcoxonSignedRankedTestResult{Estimate: -77.5, LowerCi: math.Inf(-1), UpperCi: 358, PValue: 0.421875},
expectError: false,
}, {
testName: "Paired sample (Greater) test with exact estimation",
x: []float64{447, 832, 640, 286, 501, 123},
y: []float64{241, 608, 130, 951, 604, 690},
alt: Greater,
result: &WilcoxonSignedRankedTestResult{Estimate: -77.5, LowerCi: -567, UpperCi: math.Inf(1), PValue: 0.65625},
expectError: false,
}, {
testName: "One sample (TwoSided) test with exact estimation",
x: []float64{206, 224, 510, -665, -103, -567},
y: []float64{},
alt: TwoSided,
result: &WilcoxonSignedRankedTestResult{Estimate: -77.5, LowerCi: -665, UpperCi: 510, PValue: 0.84375},
expectError: false,
}, {
testName: "One sample (Less) test with exact estimation",
x: []float64{206, 224, 510, -665, -103, -567},
y: []float64{},
alt: Less,
result: &WilcoxonSignedRankedTestResult{Estimate: -77.5, LowerCi: math.Inf(-1), UpperCi: 358, PValue: 0.421875},
expectError: false,
}, {
testName: "One sample (Greater) test with exact estimation",
x: []float64{206, 224, 510, -665, -103, -567},
y: []float64{},
alt: Greater,
result: &WilcoxonSignedRankedTestResult{Estimate: -77.5, LowerCi: -567, UpperCi: math.Inf(1), PValue: 0.65625},
expectError: false,
}, {
testName: "One sample (TwoSided) test with exact estimation with odd number of input length",
x: []float64{206, 224, 510, -665, -103},
y: []float64{},
alt: TwoSided,
result: &WilcoxonSignedRankedTestResult{Estimate: 60.5, LowerCi: -665, UpperCi: 510, PValue: 0.8125},
expectError: false,
}, {
testName: "One sample (TwoSided) test with exact estimation with ties in the input",
x: []float64{206, 206, 510, -665, -103},
y: []float64{},
alt: TwoSided,
result: &WilcoxonSignedRankedTestResult{Estimate: 51.50002392602765, LowerCi: -665, UpperCi: 510, PValue: 0.7864570351373765},
expectError: false,
}, {
testName: "One sample (TwoSided) test with exact estimation with zeros in the input",
x: []float64{206, 0, 510, -665, -103},
y: []float64{},
alt: TwoSided,
result: &WilcoxonSignedRankedTestResult{Estimate: -16.99287685975377, LowerCi: -664.9999137353511, UpperCi: 509.9999137353511, PValue: 1},
expectError: false,
}, {
testName: "Paired sample (TwoSided) test with normal approximation",
x: []float64{4737, 1582, 5352, 4606, 7701, 2267, 2247, 6200, 9248, 2297, 4152, 199, 1743, 8457, 2462, 7268, 7014, 4716, 4992, 3264, 3885, 160, 4495, 6600, 3249, 4187, 1167, 8918, 6826, 9391, 3164, 3459, 9559, 836, 6252, 9997, 7246, 8492, 9713, 7141, 2880, 1499, 5605, 5838, 1469, 6679, 9534, 125, 5544, 3365},
y: []float64{8362, 3569, 5106, 98, 4711, 3640, 8634, 1815, 7558, 2354, 4629, 6486, 630, 3679, 7190, 163, 2545, 6947, 802, 6571, 4834, 688, 7618, 8954, 3200, 1801, 9162, 6049, 6298, 5785, 9655, 9630, 9504, 1850, 4927, 1653, 7669, 4331, 4616, 9526, 8724, 4015, 8545, 246, 9912, 5474, 5455, 4335, 7096, 4175},
alt: TwoSided,
result: &WilcoxonSignedRankedTestResult{Estimate: -292.4999968196823, LowerCi: -1456.499966104948, UpperCi: 983.4999551510192, PValue: 0.6604972529810283},
expectError: false,
}, {
testName: "Paired sample (Less) test with normal approximation",
x: []float64{4737, 1582, 5352, 4606, 7701, 2267, 2247, 6200, 9248, 2297, 4152, 199, 1743, 8457, 2462, 7268, 7014, 4716, 4992, 3264, 3885, 160, 4495, 6600, 3249, 4187, 1167, 8918, 6826, 9391, 3164, 3459, 9559, 836, 6252, 9997, 7246, 8492, 9713, 7141, 2880, 1499, 5605, 5838, 1469, 6679, 9534, 125, 5544, 3365},
y: []float64{8362, 3569, 5106, 98, 4711, 3640, 8634, 1815, 7558, 2354, 4629, 6486, 630, 3679, 7190, 163, 2545, 6947, 802, 6571, 4834, 688, 7618, 8954, 3200, 1801, 9162, 6049, 6298, 5785, 9655, 9630, 9504, 1850, 4927, 1653, 7669, 4331, 4616, 9526, 8724, 4015, 8545, 246, 9912, 5474, 5455, 4335, 7096, 4175},
alt: Less,
result: &WilcoxonSignedRankedTestResult{Estimate: -292.4999968196823, LowerCi: math.Inf(-1), UpperCi: 787.999996305465, PValue: 0.3302486264905142},
expectError: false,
}, {
testName: "Paired sample (Greater) test with normal approximation",
x: []float64{4737, 1582, 5352, 4606, 7701, 2267, 2247, 6200, 9248, 2297, 4152, 199, 1743, 8457, 2462, 7268, 7014, 4716, 4992, 3264, 3885, 160, 4495, 6600, 3249, 4187, 1167, 8918, 6826, 9391, 3164, 3459, 9559, 836, 6252, 9997, 7246, 8492, 9713, 7141, 2880, 1499, 5605, 5838, 1469, 6679, 9534, 125, 5544, 3365},
y: []float64{8362, 3569, 5106, 98, 4711, 3640, 8634, 1815, 7558, 2354, 4629, 6486, 630, 3679, 7190, 163, 2545, 6947, 802, 6571, 4834, 688, 7618, 8954, 3200, 1801, 9162, 6049, 6298, 5785, 9655, 9630, 9504, 1850, 4927, 1653, 7669, 4331, 4616, 9526, 8724, 4015, 8545, 246, 9912, 5474, 5455, 4335, 7096, 4175},
alt: Greater,
result: &WilcoxonSignedRankedTestResult{Estimate: -292.4999968196823, LowerCi: -1230.4999712058636, UpperCi: math.Inf(1), PValue: 0.6732409140493585},
expectError: false,
}, {
testName: "Paired sample (TwoSided) test with all ties",
x: []float64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
y: []float64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
alt: TwoSided,
result: &WilcoxonSignedRankedTestResult{Estimate: math.NaN(), LowerCi: math.NaN(), UpperCi: math.NaN(), PValue: math.NaN()},
expectError: false,
}, {
testName: "Paired sample (Less) test with all ties",
x: []float64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
y: []float64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
alt: Less,
result: &WilcoxonSignedRankedTestResult{Estimate: math.NaN(), LowerCi: math.Inf(-1), UpperCi: math.NaN(), PValue: 1},
expectError: false,
}, {
testName: "Paired sample (Greater) test with all ties",
x: []float64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
y: []float64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
alt: Greater,
result: &WilcoxonSignedRankedTestResult{Estimate: math.NaN(), LowerCi: math.NaN(), UpperCi: math.Inf(1), PValue: 1},
expectError: false,
},
} {
t.Run(test.testName, func(t *testing.T) {
res, err := WilcoxonSignedRankedTest(test.x, test.y, test.alt)
if err != nil && !strings.Contains(err.Error(), test.expectErrMsg) {
t.Errorf("Expected (%s) and got (%v) error message doesn't match", test.expectErrMsg, err)
}
if err == nil && test.expectError {
t.Error("Expected error but not nil")
}
if err == nil && !math.IsNaN(res.Estimate) && (res.PValue != test.result.PValue || res.Estimate != test.result.Estimate ||
res.UpperCi != test.result.UpperCi || res.LowerCi != test.result.LowerCi) {
t.Errorf("Expected res (%v) and got (%v) does not match", test.result, res)
}
if err == nil && math.IsNaN(res.Estimate) {
if !math.IsNaN(test.result.Estimate) {
t.Errorf("Expected res (%v) and got (%v) does not match", test.result, res)
}
if math.IsNaN(res.LowerCi) {
if !math.IsNaN(test.result.LowerCi) {
t.Errorf("Expected res (%v) and got (%v) does not match", test.result, res)
}
}
if math.IsNaN(res.UpperCi) {
if !math.IsNaN(test.result.UpperCi) {
t.Errorf("Expected res (%v) and got (%v) does not match", test.result, res)
}
}
if math.IsInf(res.UpperCi, 1) {
if !math.IsInf(test.result.UpperCi, 1) {
t.Errorf("Expected res (%v) and got (%v) does not match", test.result, res)
}
}
if math.IsInf(res.LowerCi, -1) {
if !math.IsInf(test.result.LowerCi, -1) {
t.Errorf("Expected res (%v) and got (%v) does not match", test.result, res)
}
}
}
})
}
}
func TestBerfWilcoxonSignedRankedTest(t *testing.T) {
for _, test := range []struct {
testName string
x []float64
y []float64
alt Hypothesis
transform DataTransform
result *BerfWilcoxonSignedRankedTestResult
expectError bool
expectErrMsg string
}{
{
testName: "Length of input x and y does not match",
x: []float64{1.0},
y: []float64{1.0, 2.0},
alt: TwoSided,
transform: LogTransform,
result: nil,
expectError: true,
expectErrMsg: "must have the same length",
}, {
testName: "Paired sample (TwoSided) test using LogTransform",
x: []float64{0.1287916, 0.1551960, 0.3128008, 0.4482681, 0.2929843, 0.4259846},
y: []float64{0.20628058, 0.33817586, 0.22281578, 0.28119775, 0.07023901, 0.23528268},
alt: TwoSided,
transform: LogTransform,
result: &BerfWilcoxonSignedRankedTestResult{Estimate: 40.385389221535384, LowerCi: -54.10790113759154, UpperCi: 317.1247573107878, PValue: 0.6875, XMedian: 0.30289255, YMedian: 0.22904923},
expectError: false,
}, {
testName: "Paired sample (TwoSided) test using NormalizeResult",
x: []float64{0.4476217, 0.6152619},
y: []float64{0.2416391, 0.3708087},
alt: TwoSided,
transform: NormalizeResult,
result: &BerfWilcoxonSignedRankedTestResult{Estimate: 73.54680676459284, LowerCi: 67.2653571455396, UpperCi: 79.8282563836461, PValue: 0.5, XMedian: 0.5314418000000001, YMedian: 0.3062239},
expectError: false,
}, {
testName: "Paired sample (TwoSided) test using OriginalResult",
x: []float64{0.3583569, 0.3603624, 0.5738001, 0.3583569},
y: []float64{0.4493686, 0.8384975, 0.7492481, 0.4493686},
alt: TwoSided,
transform: OriginalResult,
result: &BerfWilcoxonSignedRankedTestResult{Estimate: -0.15951242203815116, LowerCi: -0.2845975069273507, UpperCi: -0.09101170000000003, PValue: 0.09751253817810965, XMedian: 0.35935965000000003, YMedian: 0.59930835},
expectError: false,
},
} {
t.Run(test.testName, func(t *testing.T) {
res, err := BerfWilcoxonSignedRankedTest(test.x, test.y, test.alt, test.transform)
if err != nil && !strings.Contains(err.Error(), test.expectErrMsg) {
t.Errorf("Expected (%s) and got (%v) error message doesn't match", test.expectErrMsg, err)
}
if err == nil && test.expectError {
t.Error("Expected error but not nil")
}
if err == nil && !math.IsNaN(res.Estimate) && (res.PValue != test.result.PValue || res.Estimate != test.result.Estimate ||
res.UpperCi != test.result.UpperCi || res.LowerCi != test.result.LowerCi || res.XMedian != test.result.XMedian || res.YMedian != test.result.YMedian) {
t.Errorf("Expected res (%v) and got (%v) does not match", test.result, res)
}
})
}
}