From 4d217b1c800d687bcdfbbe612aff814e7a98e643 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Thu, 7 Mar 2024 13:38:28 +0100 Subject: [PATCH] Rework MannWhitneyTest --- .../SignificanceTesting/BrunnerMunzelTests.cs | 2 +- .../SignificanceTesting/MannWhitneyTests.cs | 386 +++++++----------- .../SignificanceTesting/WelchTests.cs | 4 +- src/Perfolizer/Perfolizer.sln.DotSettings | 5 +- .../Mathematics/Common/MathExtensions.cs | 2 + .../Base/ISignificanceTwoSampleTest.cs | 2 +- .../SignificanceTesting/BrunnerMunzelTest.cs | 10 +- .../MannWhitney/IMannWhitneyCdf.cs | 6 + .../MannWhitney/MannWhitneyClassicExactCdf.cs | 49 +++ .../MannWhitneyEdgeworthApproxCdf.cs | 67 +++ .../MannWhitneyLoefflerExactCdf.cs | 44 ++ .../MannWhitney/MannWhitneyNormalApproxCdf.cs | 16 + .../{ => MannWhitney}/MannWhitneyResult.cs | 2 +- .../MannWhitney/MannWhitneyStrategy.cs | 10 + .../MannWhitney/MannWhitneyTest.cs | 114 ++++++ .../SignificanceTesting/MannWhitneyTest.cs | 154 ------- .../SignificanceTesting/WelchTest.cs | 6 +- .../Mathematics/Thresholds/Threshold.cs | 2 +- 18 files changed, 482 insertions(+), 399 deletions(-) create mode 100644 src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/IMannWhitneyCdf.cs create mode 100644 src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyClassicExactCdf.cs create mode 100644 src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyEdgeworthApproxCdf.cs create mode 100644 src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyLoefflerExactCdf.cs create mode 100644 src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyNormalApproxCdf.cs rename src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/{ => MannWhitney}/MannWhitneyResult.cs (90%) create mode 100644 src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyStrategy.cs create mode 100644 src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyTest.cs delete mode 100644 src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitneyTest.cs diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/BrunnerMunzelTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/BrunnerMunzelTests.cs index aa53b79e..c9543436 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/BrunnerMunzelTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/BrunnerMunzelTests.cs @@ -88,7 +88,7 @@ public void BrunnerMunzelTest05() private void CheckGreater(double[] x, double[] y, double w, double df, double pValue) { var threshold = AbsoluteThreshold.Zero; - var result = BrunnerMunzelTest.Instance.Run(x.ToSample(), y.ToSample(), AlternativeHypothesis.Greater, threshold); + var result = BrunnerMunzelTest.Instance.Perform(x.ToSample(), y.ToSample(), AlternativeHypothesis.Greater, threshold); if (result == null) throw new NullReferenceException($"{nameof(BrunnerMunzelTest)} returned null"); output.WriteLine("W = " + result.W + " (Expected: " + w + ")"); diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/MannWhitneyTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/MannWhitneyTests.cs index c7bc11bd..8b7de4df 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/MannWhitneyTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/MannWhitneyTests.cs @@ -1,21 +1,14 @@ using JetBrains.Annotations; using Perfolizer.Collections; -using Perfolizer.Mathematics.SignificanceTesting; using Perfolizer.Mathematics.SignificanceTesting.Base; +using Perfolizer.Mathematics.SignificanceTesting.MannWhitney; using Perfolizer.Mathematics.Thresholds; using Perfolizer.Tests.Common; namespace Perfolizer.Tests.Mathematics.SignificanceTesting; -public class MannWhitneyTests +public class MannWhitneyTests(ITestOutputHelper output) { - private readonly ITestOutputHelper output; - - public MannWhitneyTests(ITestOutputHelper output) - { - this.output = output; - } - [Theory] [InlineData(-30, 91, 0.0005250168)] [InlineData(-15, 76, 0.02621295)] @@ -26,17 +19,14 @@ public void N10(double t, int u, double pValue) { double[] x = { - 101.560680277179, 84.5009342862592, 87.2294917081476, 129.63805718437, 105.404085588131, 107.725754537927, 80.1006409348006, - 113.864283866225, - 102.430864178515, 104.403374809552 + 101.560680277179, 84.5009342862592, 87.2294917081476, 129.63805718437, 105.404085588131, 107.725754537927, + 80.1006409348006, 113.864283866225, 102.430864178515, 104.403374809552 }; double[] y = { - 82.352823982586, 126.135642815364, 109.069571619921, 91.5330798762612, 94.2532257217699, 70.568270818608, 92.5436886596998, - 117.210691979832, - 119.721076854462, 113.813867732564 + 82.352823982586, 126.135642815364, 109.069571619921, 91.5330798762612, 94.2532257217699, 70.568270818608, + 92.5436886596998, 117.210691979832, 119.721076854462, 113.813867732564 }; - CheckIsGreater(x, y, new AbsoluteThreshold(t), u, pValue); } @@ -50,21 +40,18 @@ public void N20(double t, int u, double pValue) { double[] x = { - 97.8506464838511, 90.5704863589574, 106.919850380017, 114.968781001117, 133.163509926947, 98.5371366517863, 89.9809819778675, - 94.8330330098023, - 113.848103602446, 100.747077722794, 118.58905185697, 100.050778406168, 103.429498836193, 91.9991877755192, 117.868745215013, - 107.662781698883, - 110.434016813579, 118.772597143401, 91.0851351093108, 108.595287565388 + 97.8506464838511, 90.5704863589574, 106.919850380017, 114.968781001117, 133.163509926947, 98.5371366517863, + 89.9809819778675, 94.8330330098023, 113.848103602446, 100.747077722794, 118.58905185697, 100.050778406168, + 103.429498836193, 91.9991877755192, 117.868745215013, 107.662781698883, 110.434016813579, 118.772597143401, + 91.0851351093108, 108.595287565388 }; double[] y = { - 141.325356080042, 101.705375558118, 121.135232885412, 104.233257876038, 113.697498891312, 89.3279837525589, 112.305397095148, - 107.035411938303, - 126.862312493224, 137.838076178722, 100.055966433672, 106.011372282587, 99.220787317467, 105.141703581153, 95.2136664971929, - 127.87168139858, - 97.5524729768301, 117.961786925524, 142.32225043168, 85.9508305239065 + 141.325356080042, 101.705375558118, 121.135232885412, 104.233257876038, 113.697498891312, 89.3279837525589, + 112.305397095148, 107.035411938303, 126.862312493224, 137.838076178722, 100.055966433672, 106.011372282587, + 99.220787317467, 105.141703581153, 95.2136664971929, 127.87168139858, 97.5524729768301, 117.961786925524, + 142.32225043168, 85.9508305239065 }; - CheckIsGreater(x, y, new AbsoluteThreshold(t), u, pValue); } @@ -72,33 +59,27 @@ public void N20(double t, int u, double pValue) [InlineData(-75, 947, 4.162974e-11)] [InlineData(-50, 549, 0.3131681)] [InlineData(-25, 119, 1)] - [InlineData(0, 5, 1)] - [InlineData(25, 0, 1)] + [InlineData(0, 5, 1)] [InlineData(25, 0, 1)] public void N32(double t, int u, double pValue) { double[] x = { - 116.998310570223, 103.536732200858, 81.4977149674052, 109.240370046523, 112.685608892842, 92.4961887142488, 81.0875839754617, - 107.885504895313, - 129.489136448277, 114.743862868848, 92.8640399750995, 99.5044810768261, 100.611715633651, 92.9347711165703, 125.105417118737, - 107.956368991103, - 104.812061571297, 93.8558811186138, 101.649455913474, 108.168151882154, 86.9840546937499, 102.813126398294, 62.9278860673337, - 81.9721302396966, - 99.5326103757994, 99.1127497773892, 89.6472790590878, 94.5383623495609, 104.265979458838, 89.0962721023424, 113.828847763463, - 74.9119601526303 + 116.998310570223, 103.536732200858, 81.4977149674052, 109.240370046523, 112.685608892842, 92.4961887142488, + 81.0875839754617, 107.885504895313, 129.489136448277, 114.743862868848, 92.8640399750995, 99.5044810768261, + 100.611715633651, 92.9347711165703, 125.105417118737, 107.956368991103, 104.812061571297, 93.8558811186138, + 101.649455913474, 108.168151882154, 86.9840546937499, 102.813126398294, 62.9278860673337, 81.9721302396966, + 99.5326103757994, 99.1127497773892, 89.6472790590878, 94.5383623495609, 104.265979458838, 89.0962721023424, + 113.828847763463, 74.9119601526303 }; double[] y = { - 134.807641760747, 153.862779834471, 155.685166413893, 153.130746875771, 153.260935827381, 156.349904137714, 156.189659805232, - 121.163530829502, - 147.486553850055, 132.381319853769, 135.272938963356, 159.631552618188, 165.266283582675, 150.072225393336, 150.645061760864, - 139.783511337841, - 144.258022179319, 173.127172505296, 168.767084858625, 138.364489170039, 156.172173370812, 125.970596582247, 152.637584228714, - 147.006073519481, - 134.162738650882, 138.128111355384, 163.306156590866, 133.37466051387, 141.733740469802, 153.328995694082, 154.841803368071, - 120.244223151518 + 134.807641760747, 153.862779834471, 155.685166413893, 153.130746875771, 153.260935827381, 156.349904137714, + 156.189659805232, 121.163530829502, 147.486553850055, 132.381319853769, 135.272938963356, 159.631552618188, + 165.266283582675, 150.072225393336, 150.645061760864, 139.783511337841, 144.258022179319, 173.127172505296, + 168.767084858625, 138.364489170039, 156.172173370812, 125.970596582247, 152.637584228714, 147.006073519481, + 134.162738650882, 138.128111355384, 163.306156590866, 133.37466051387, 141.733740469802, 153.328995694082, + 154.841803368071, 120.244223151518 }; - CheckIsGreater(x, y, new AbsoluteThreshold(t), u, pValue); } @@ -112,29 +93,22 @@ public void N33(double t, int u, double pValue) { double[] x = { - 157.183612367683, 152.028902955382, 153.698861602206, 153.609486299015, 154.291063026817, 141.317831985261, 178.900284004259, - 167.478597683646, - 161.832190643462, 147.626654232355, 148.374015299603, 174.068375692261, 141.51911277841, 139.69371979521, 126.871207404749, - 153.808773375195, - 161.106156148851, 155.51480817283, 157.155363595268, 158.055454232685, 162.798646844568, 166.532006705421, 140.111049964116, - 128.996157091284, - 169.680723418997, 137.479319232307, 157.03932404715, 163.508869366932, 165.356386977935, 145.571530643651, 155.600613010786, - 158.004851800436, - 134.918152600764 + 157.183612367683, 152.028902955382, 153.698861602206, 153.609486299015, 154.291063026817, 141.317831985261, + 178.900284004259, 167.478597683646, 161.832190643462, 147.626654232355, 148.374015299603, 174.068375692261, + 141.51911277841, 139.69371979521, 126.871207404749, 153.808773375195, 161.106156148851, 155.51480817283, + 157.155363595268, 158.055454232685, 162.798646844568, 166.532006705421, 140.111049964116, 128.996157091284, + 169.680723418997, 137.479319232307, 157.03932404715, 163.508869366932, 165.356386977935, 145.571530643651, + 155.600613010786, 158.004851800436, 134.918152600764 }; double[] y = { - 99.9917764157985, 110.338659968681, 91.9010057855087, 120.039901613317, 117.797945862094, 123.134528084071, 72.8122128513182, - 97.7368150018351, - 113.098574902131, 97.3639348027811, 110.481300557315, 140.288931294698, 102.184946433365, 97.0400366425305, 98.0057356321133, - 103.417878439606, - 99.4765968818225, 79.2430526140985, 75.375686521915, 104.855304104658, 103.619087606989, 96.227750057318, 112.563251473402, - 82.063032157974, - 126.197302954924, 81.2530975187152, 124.059305772757, 117.114945385568, 109.651754612905, 103.601482510961, 128.404461213919, - 123.245412480788, - 121.186685876309 + 99.9917764157985, 110.338659968681, 91.9010057855087, 120.039901613317, 117.797945862094, 123.134528084071, + 72.8122128513182, 97.7368150018351, 113.098574902131, 97.3639348027811, 110.481300557315, 140.288931294698, + 102.184946433365, 97.0400366425305, 98.0057356321133, 103.417878439606, 99.4765968818225, 79.2430526140985, + 75.375686521915, 104.855304104658, 103.619087606989, 96.227750057318, 112.563251473402, 82.063032157974, + 126.197302954924, 81.2530975187152, 124.059305772757, 117.114945385568, 109.651754612905, 103.601482510961, + 128.404461213919, 123.245412480788, 121.186685876309 }; - CheckIsGreater(x, y, new AbsoluteThreshold(t), u, pValue); } @@ -144,24 +118,17 @@ public void N3Vs49(double t, int u, double pValue) { // set.seed(42); x <- rnorm(3, mean = 10) // set.seed(42); y <- rnorm(49, mean = 11) - double[] x = - { - 11.3709584471467, 9.43530182860391, 10.3631284113373 - }; + double[] x = { 11.3709584471467, 9.43530182860391, 10.3631284113373 }; double[] y = { - 12.3709584471467, 10.4353018286039, 11.3631284113373, 11.632862604961, - 11.404268323141, 10.8938754839085, 12.5115219974389, 10.9053409615869, - 13.018423713877, 10.9372859009476, 12.3048696542235, 13.2866453927011, - 9.61113929888766, 10.7212112331826, 10.8666786636063, 11.6359503980701, - 10.7157470785839, 8.34354457909522, 8.55953307142448, 12.3201133457302, - 10.6933614059215, 9.21869156602, 10.8280826442404, 12.2146746991726, - 12.895193461265, 10.5695308683938, 10.7427306172311, 9.23683691480522, - 11.4600973548313, 10.3600051240399, 11.4554501232412, 11.7048373372288, - 12.0351035219699, 10.3910736245928, 11.504955123298, 9.28299132092666, - 10.2155409916205, 10.1490924058235, 8.58579235005337, 11.0361226068923, - 11.2059986002003, 10.6389427014513, 11.7581632356995, 10.2732951729234, - 9.63171895558071, 11.4328180258887, 10.1886068238133, 12.4441012617213, + 12.3709584471467, 10.4353018286039, 11.3631284113373, 11.632862604961, 11.404268323141, 10.8938754839085, + 12.5115219974389, 10.9053409615869, 13.018423713877, 10.9372859009476, 12.3048696542235, 13.2866453927011, + 9.61113929888766, 10.7212112331826, 10.8666786636063, 11.6359503980701, 10.7157470785839, 8.34354457909522, + 8.55953307142448, 12.3201133457302, 10.6933614059215, 9.21869156602, 10.8280826442404, 12.2146746991726, + 12.895193461265, 10.5695308683938, 10.7427306172311, 9.23683691480522, 11.4600973548313, 10.3600051240399, + 11.4554501232412, 11.7048373372288, 12.0351035219699, 10.3910736245928, 11.504955123298, 9.28299132092666, + 10.2155409916205, 10.1490924058235, 8.58579235005337, 11.0361226068923, 11.2059986002003, 10.6389427014513, + 11.7581632356995, 10.2732951729234, 9.63171895558071, 11.4328180258887, 10.1886068238133, 12.4441012617213, 10.5685537973867 }; CheckIsGreater(x, y, new AbsoluteThreshold(t), u, pValue); @@ -179,21 +146,16 @@ public void N3Vs62(double t, int u, double pValue) }; double[] y = { - 12.3709584471467, 10.4353018286039, 11.3631284113373, 11.632862604961, - 11.404268323141, 10.8938754839085, 12.5115219974389, 10.9053409615869, - 13.018423713877, 10.9372859009476, 12.3048696542235, 13.2866453927011, - 9.61113929888766, 10.7212112331826, 10.8666786636063, 11.6359503980701, - 10.7157470785839, 8.34354457909522, 8.55953307142448, 12.3201133457302, - 10.6933614059215, 9.21869156602, 10.8280826442404, 12.2146746991726, - 12.895193461265, 10.5695308683938, 10.7427306172311, 9.23683691480522, - 11.4600973548313, 10.3600051240399, 11.4554501232412, 11.7048373372288, - 12.0351035219699, 10.3910736245928, 11.504955123298, 9.28299132092666, - 10.2155409916205, 10.1490924058235, 8.58579235005337, 11.0361226068923, - 11.2059986002003, 10.6389427014513, 11.7581632356995, 10.2732951729234, - 9.63171895558071, 11.4328180258887, 10.1886068238133, 12.4441012617213, - 10.5685537973867, 11.6556478834022, 11.3219252652039, 10.2161610591196, - 12.575727519792, 11.6428993057173, 11.0897606465996, 11.2765507472915, - 11.6792888160553, 11.0898328865791, 8.00690991684706, 11.2848829535307, + 12.3709584471467, 10.4353018286039, 11.3631284113373, 11.632862604961, 11.404268323141, 10.8938754839085, + 12.5115219974389, 10.9053409615869, 13.018423713877, 10.9372859009476, 12.3048696542235, 13.2866453927011, + 9.61113929888766, 10.7212112331826, 10.8666786636063, 11.6359503980701, 10.7157470785839, 8.34354457909522, + 8.55953307142448, 12.3201133457302, 10.6933614059215, 9.21869156602, 10.8280826442404, 12.2146746991726, + 12.895193461265, 10.5695308683938, 10.7427306172311, 9.23683691480522, 11.4600973548313, 10.3600051240399, + 11.4554501232412, 11.7048373372288, 12.0351035219699, 10.3910736245928, 11.504955123298, 9.28299132092666, + 10.2155409916205, 10.1490924058235, 8.58579235005337, 11.0361226068923, 11.2059986002003, 10.6389427014513, + 11.7581632356995, 10.2732951729234, 9.63171895558071, 11.4328180258887, 10.1886068238133, 12.4441012617213, + 10.5685537973867, 11.6556478834022, 11.3219252652039, 10.2161610591196, 12.575727519792, 11.6428993057173, + 11.0897606465996, 11.2765507472915, 11.6792888160553, 11.0898328865791, 8.00690991684706, 11.2848829535307, 10.632765357259, 11.1852305648656 }; CheckIsGreater(x, y, new AbsoluteThreshold(t), u, pValue); @@ -212,26 +174,20 @@ public void N30Vs40(double t, int u, double pValue) // set.seed(42); y <- rnorm(40, mean = 11) double[] x = { - 11.3709584471467, 9.43530182860391, 10.3631284113373, 10.632862604961, - 10.404268323141, 9.89387548390852, 11.5115219974389, 9.9053409615869, - 12.018423713877, 9.93728590094758, 11.3048696542235, 12.2866453927011, - 8.61113929888766, 9.72121123318263, 9.86667866360634, 10.6359503980701, - 9.71574707858393, 7.34354457909522, 7.55953307142448, 11.3201133457302, - 9.69336140592153, 8.21869156602, 9.82808264424038, 11.2146746991726, - 11.895193461265, 9.5695308683938, 9.74273061723107, 8.23683691480522, - 10.4600973548313, 9.36000512403988 + 11.3709584471467, 9.43530182860391, 10.3631284113373, 10.632862604961, 10.404268323141, 9.89387548390852, + 11.5115219974389, 9.9053409615869, 12.018423713877, 9.93728590094758, 11.3048696542235, 12.2866453927011, + 8.61113929888766, 9.72121123318263, 9.86667866360634, 10.6359503980701, 9.71574707858393, 7.34354457909522, + 7.55953307142448, 11.3201133457302, 9.69336140592153, 8.21869156602, 9.82808264424038, 11.2146746991726, + 11.895193461265, 9.5695308683938, 9.74273061723107, 8.23683691480522, 10.4600973548313, 9.36000512403988 }; double[] y = { - 12.3709584471467, 10.4353018286039, 11.3631284113373, 11.632862604961, - 11.404268323141, 10.8938754839085, 12.5115219974389, 10.9053409615869, - 13.018423713877, 10.9372859009476, 12.3048696542235, 13.2866453927011, - 9.61113929888766, 10.7212112331826, 10.8666786636063, 11.6359503980701, - 10.7157470785839, 8.34354457909522, 8.55953307142448, 12.3201133457302, - 10.6933614059215, 9.21869156602, 10.8280826442404, 12.2146746991726, - 12.895193461265, 10.5695308683938, 10.7427306172311, 9.23683691480522, - 11.4600973548313, 10.3600051240399, 11.4554501232412, 11.7048373372288, - 12.0351035219699, 10.3910736245928, 11.504955123298, 9.28299132092666, + 12.3709584471467, 10.4353018286039, 11.3631284113373, 11.632862604961, 11.404268323141, 10.8938754839085, + 12.5115219974389, 10.9053409615869, 13.018423713877, 10.9372859009476, 12.3048696542235, 13.2866453927011, + 9.61113929888766, 10.7212112331826, 10.8666786636063, 11.6359503980701, 10.7157470785839, 8.34354457909522, + 8.55953307142448, 12.3201133457302, 10.6933614059215, 9.21869156602, 10.8280826442404, 12.2146746991726, + 12.895193461265, 10.5695308683938, 10.7427306172311, 9.23683691480522, 11.4600973548313, 10.3600051240399, + 11.4554501232412, 11.7048373372288, 12.0351035219699, 10.3910736245928, 11.504955123298, 9.28299132092666, 10.2155409916205, 10.1490924058235, 8.58579235005337, 11.0361226068923 }; CheckIsGreater(x, y, new AbsoluteThreshold(t), u, pValue); @@ -247,97 +203,70 @@ public void N300(double t, double u, double pValue) { double[] x = { - 353.4302, 350.9193, 396.4676, 354.2004, 349.9472, 350.1746, 351.1972, 361.5375, 357.1806, 355.6957, 350.9530, 353.7620, - 350.9252, 350.2909, - 357.2528, 350.4599, 350.7203, 351.4393, 349.4832, 348.9141, 350.8004, 349.2567, 348.6628, 348.8442, 350.8513, 355.6557, - 348.9383, 349.2411, - 348.5117, 349.9738, 349.5833, 350.9835, 348.5326, 348.9863, 349.5978, 352.2430, 353.9921, 348.7386, 353.8779, 349.3807, - 348.6024, 348.7450, - 348.7609, 348.5573, 351.4074, 348.7916, 348.9138, 349.4394, 355.9450, 351.0741, 350.4301, 351.6454, 348.8297, 348.4810, - 348.9067, 348.8078, - 351.3754, 348.2212, 350.6068, 351.8590, 349.4409, 353.8832, 352.1167, 351.4719, 348.9147, 350.5190, 350.8566, 348.5493, - 348.3130, 354.7536, - 348.5446, 349.7245, 349.3147, 349.2842, 351.3039, 351.6084, 349.1143, 348.4140, 350.6863, 355.6851, 348.4323, 348.6842, - 348.9533, 353.1712, - 350.9491, 353.3528, 350.9518, 352.7969, 348.3916, 355.0802, 348.2997, 348.7019, 348.7373, 348.3435, 348.3017, 351.7710, - 353.3918, 349.3426, - 349.3856, 348.9890, 348.6114, 352.0596, 348.4195, 357.1810, 350.6999, 349.2421, 350.7046, 349.0863, 350.1624, 350.1976, - 348.3483, 348.4665, - 352.4801, 348.7607, 350.7053, 349.3357, 352.0602, 348.2372, 349.0550, 348.8323, 348.3879, 349.4077, 348.6808, 348.4246, - 348.4569, 349.1250, - 348.9443, 348.9642, 348.3971, 356.3534, 348.2787, 349.8091, 348.5167, 348.4438, 348.3847, 348.5627, 348.6409, 348.3877, - 352.1406, 348.4861, - 348.7049, 350.7922, 352.7203, 348.9340, 348.4139, 349.4621, 348.8676, 349.7629, 350.9608, 348.2334, 348.8159, 348.5077, - 348.1488, 348.4427, - 348.4489, 348.6434, 348.7352, 348.5058, 348.3966, 353.2068, 352.4687, 349.0576, 349.9138, 350.8908, 349.1250, 351.0939, - 351.1674, 348.2952, - 348.6345, 348.3658, 354.7745, 348.0121, 349.5867, 350.2797, 348.4887, 348.4441, 374.9498, 348.2829, 349.4644, 349.5089, - 349.8994, 349.0551, - 351.5127, 351.2810, 351.3763, 349.8671, 349.1344, 349.1842, 349.9880, 353.1967, 350.0169, 351.4005, 364.2627, 351.5116, - 350.4371, 349.6280, - 354.5077, 351.2293, 351.2565, 349.7923, 351.1246, 351.4023, 350.9489, 373.6979, 348.9797, 349.4231, 351.6052, 350.3881, - 372.3069, 348.8843, - 355.4068, 350.8477, 350.6771, 348.3057, 350.2932, 348.4213, 349.1609, 348.3758, 350.7920, 350.6115, 350.7276, 349.8305, - 348.2517, 349.7037, - 348.3101, 349.3411, 348.5030, 356.8544, 348.9204, 350.1835, 348.1639, 349.5208, 348.4460, 348.4046, 349.9324, 348.5018, - 348.6524, 349.5684, - 348.4567, 348.6487, 348.3740, 348.2163, 348.5270, 358.3175, 353.3845, 349.2999, 348.9381, 349.1241, 348.6709, 348.4846, - 357.6281, 348.9662, - 348.9404, 348.8550, 348.5498, 349.3227, 348.6938, 353.9863, 349.5527, 349.7617, 349.0910, 348.6906, 351.2841, 349.5185, - 350.0456, 349.2823, - 351.1163, 354.7285, 350.5538, 353.3063, 349.2634, 348.4159, 348.4064, 360.4929, 350.9462, 348.4895, 348.3143, 349.4910, - 352.0636, 352.6701, - 348.6482, 348.3380, 348.3338, 354.2525, 371.4057, 348.8699, 349.3964, 352.7111, 352.3196, 349.5104, 348.7168, 356.8885, - 352.2493, 348.8851, - 352.0130, 349.3317, 351.2397, 348.4217, 351.9049, 348.6496 + 353.4302, 350.9193, 396.4676, 354.2004, 349.9472, 350.1746, 351.1972, 361.5375, 357.1806, 355.6957, + 350.9530, 353.7620, 350.9252, 350.2909, 357.2528, 350.4599, 350.7203, 351.4393, 349.4832, 348.9141, + 350.8004, 349.2567, 348.6628, 348.8442, 350.8513, 355.6557, 348.9383, 349.2411, 348.5117, 349.9738, + 349.5833, 350.9835, 348.5326, 348.9863, 349.5978, 352.2430, 353.9921, 348.7386, 353.8779, 349.3807, + 348.6024, 348.7450, 348.7609, 348.5573, 351.4074, 348.7916, 348.9138, 349.4394, 355.9450, 351.0741, + 350.4301, 351.6454, 348.8297, 348.4810, 348.9067, 348.8078, 351.3754, 348.2212, 350.6068, 351.8590, + 349.4409, 353.8832, 352.1167, 351.4719, 348.9147, 350.5190, 350.8566, 348.5493, 348.3130, 354.7536, + 348.5446, 349.7245, 349.3147, 349.2842, 351.3039, 351.6084, 349.1143, 348.4140, 350.6863, 355.6851, + 348.4323, 348.6842, 348.9533, 353.1712, 350.9491, 353.3528, 350.9518, 352.7969, 348.3916, 355.0802, + 348.2997, 348.7019, 348.7373, 348.3435, 348.3017, 351.7710, 353.3918, 349.3426, 349.3856, 348.9890, + 348.6114, 352.0596, 348.4195, 357.1810, 350.6999, 349.2421, 350.7046, 349.0863, 350.1624, 350.1976, + 348.3483, 348.4665, 352.4801, 348.7607, 350.7053, 349.3357, 352.0602, 348.2372, 349.0550, 348.8323, + 348.3879, 349.4077, 348.6808, 348.4246, 348.4569, 349.1250, 348.9443, 348.9642, 348.3971, 356.3534, + 348.2787, 349.8091, 348.5167, 348.4438, 348.3847, 348.5627, 348.6409, 348.3877, 352.1406, 348.4861, + 348.7049, 350.7922, 352.7203, 348.9340, 348.4139, 349.4621, 348.8676, 349.7629, 350.9608, 348.2334, + 348.8159, 348.5077, 348.1488, 348.4427, 348.4489, 348.6434, 348.7352, 348.5058, 348.3966, 353.2068, + 352.4687, 349.0576, 349.9138, 350.8908, 349.1250, 351.0939, 351.1674, 348.2952, 348.6345, 348.3658, + 354.7745, 348.0121, 349.5867, 350.2797, 348.4887, 348.4441, 374.9498, 348.2829, 349.4644, 349.5089, + 349.8994, 349.0551, 351.5127, 351.2810, 351.3763, 349.8671, 349.1344, 349.1842, 349.9880, 353.1967, + 350.0169, 351.4005, 364.2627, 351.5116, 350.4371, 349.6280, 354.5077, 351.2293, 351.2565, 349.7923, + 351.1246, 351.4023, 350.9489, 373.6979, 348.9797, 349.4231, 351.6052, 350.3881, 372.3069, 348.8843, + 355.4068, 350.8477, 350.6771, 348.3057, 350.2932, 348.4213, 349.1609, 348.3758, 350.7920, 350.6115, + 350.7276, 349.8305, 348.2517, 349.7037, 348.3101, 349.3411, 348.5030, 356.8544, 348.9204, 350.1835, + 348.1639, 349.5208, 348.4460, 348.4046, 349.9324, 348.5018, 348.6524, 349.5684, 348.4567, 348.6487, + 348.3740, 348.2163, 348.5270, 358.3175, 353.3845, 349.2999, 348.9381, 349.1241, 348.6709, 348.4846, + 357.6281, 348.9662, 348.9404, 348.8550, 348.5498, 349.3227, 348.6938, 353.9863, 349.5527, 349.7617, + 349.0910, 348.6906, 351.2841, 349.5185, 350.0456, 349.2823, 351.1163, 354.7285, 350.5538, 353.3063, + 349.2634, 348.4159, 348.4064, 360.4929, 350.9462, 348.4895, 348.3143, 349.4910, 352.0636, 352.6701, + 348.6482, 348.3380, 348.3338, 354.2525, 371.4057, 348.8699, 349.3964, 352.7111, 352.3196, 349.5104, + 348.7168, 356.8885, 352.2493, 348.8851, 352.0130, 349.3317, 351.2397, 348.4217, 351.9049, 348.6496 }; double[] y = { - 352.6772, 352.0694, 351.7422, 353.0724, 351.2761, 355.6030, 351.3673, 351.6215, 351.0600, 353.0083, 351.2111, 352.7424, - 350.7463, 350.8995, - 350.9788, 351.5278, 347.2926, 347.2408, 347.3430, 350.2349, 348.9812, 349.5674, 348.9471, 351.0041, 350.2227, 350.8498, - 350.5350, 351.6645, - 352.8947, 347.5184, 347.7925, 347.5337, 347.7620, 347.8514, 351.8277, 347.6886, 353.8707, 347.7313, 348.5841, 348.9841, - 347.5774, 347.9248, - 348.3379, 347.9148, 347.5209, 350.3050, 348.2506, 352.2775, 349.0242, 347.6859, 347.9177, 348.2372, 347.6983, 347.9181, - 362.5876, 349.5010, - 347.3098, 347.7068, 347.6530, 348.8905, 349.1875, 347.7322, 347.8935, 350.1428, 347.8078, 347.4217, 347.9083, 351.6060, - 364.3534, 362.0277, - 347.6155, 348.8701, 350.0820, 350.4459, 347.7448, 347.9179, 348.2846, 357.5326, 348.5791, 348.1976, 348.2805, 347.8833, - 351.8235, 348.4769, - 348.1972, 354.2277, 348.7812, 347.5858, 347.5523, 347.9894, 348.4267, 347.7855, 352.2337, 348.1318, 347.5751, 348.5764, - 347.8220, 351.9602, - 347.4684, 348.3530, 348.3222, 348.4553, 347.7500, 347.9536, 348.3003, 348.6743, 350.6669, 348.7904, 348.7561, 349.0508, - 347.6311, 348.3785, - 348.3788, 352.3984, 347.4498, 349.6662, 349.0177, 347.5398, 355.2611, 347.6171, 349.0511, 347.3273, 347.3592, 347.8611, - 347.5964, 347.5564, - 347.6554, 348.2081, 348.2998, 347.7288, 347.8346, 347.7421, 352.2372, 348.1965, 349.0271, 347.9774, 351.8596, 347.5298, - 347.6308, 347.6561, - 347.8762, 347.3469, 347.6736, 352.0812, 349.5322, 349.2015, 348.5227, 352.9716, 347.7573, 353.2357, 348.8211, 347.6180, - 349.7692, 348.1887, - 347.7775, 348.8799, 351.0806, 349.5768, 357.9883, 348.7598, 351.0769, 347.7897, 349.8290, 349.0401, 349.8934, 347.3818, - 347.4127, 347.2534, - 347.2993, 347.3662, 348.7725, 349.3746, 347.6021, 348.2902, 348.5113, 347.4069, 349.7877, 351.7751, 351.3461, 347.1249, - 349.4440, 348.1466, - 347.3497, 347.8770, 350.7545, 349.9675, 347.6966, 347.6298, 349.0210, 347.7346, 347.1405, 349.4634, 348.7612, 352.6694, - 349.1828, 348.0399, - 347.9772, 348.2017, 348.2891, 355.4806, 347.5127, 348.5503, 347.4944, 349.2060, 347.8054, 351.7816, 353.4037, 347.9604, - 350.0297, 347.5814, - 347.5632, 347.8805, 348.2123, 348.7926, 347.5597, 347.5986, 352.1924, 374.1594, 349.3394, 349.7771, 347.5244, 348.7254, - 356.8709, 348.2475, - 347.4074, 351.4388, 348.5651, 347.5950, 347.9851, 350.7727, 353.7092, 347.5937, 347.4309, 347.8552, 350.4125, 350.6890, - 350.5143, 347.3917, - 348.2776, 354.7649, 349.4661, 347.4174, 348.2034, 350.1242, 347.3456, 347.8254, 351.1231, 347.5047, 347.4512, 352.3139, - 347.5133, 348.6214, - 347.2056, 349.9073, 347.4756, 348.4515, 348.0344, 348.9201, 350.7587, 351.4907, 350.3649, 352.5194, 348.8017, 348.0644, - 349.5104, 348.0675, - 347.8671, 351.0880, 349.6343, 347.5890, 347.6001, 348.6831, 348.7830, 348.0030, 348.3257, 347.6348, 347.3166, 347.3369, - 349.0966, 348.0506, - 355.5348, 347.4069, 348.8350, 348.2581, 347.2280, 347.5418, 348.6233, 348.1083, 347.7203, 348.0728, 347.4089, 347.5658, - 347.6575, 347.2848, - 347.4562, 347.3834, 349.0502, 350.6191, 347.4489, 348.6910 + 352.6772, 352.0694, 351.7422, 353.0724, 351.2761, 355.6030, 351.3673, 351.6215, 351.0600, 353.0083, + 351.2111, 352.7424, 350.7463, 350.8995, 350.9788, 351.5278, 347.2926, 347.2408, 347.3430, 350.2349, + 348.9812, 349.5674, 348.9471, 351.0041, 350.2227, 350.8498, 350.5350, 351.6645, 352.8947, 347.5184, + 347.7925, 347.5337, 347.7620, 347.8514, 351.8277, 347.6886, 353.8707, 347.7313, 348.5841, 348.9841, + 347.5774, 347.9248, 348.3379, 347.9148, 347.5209, 350.3050, 348.2506, 352.2775, 349.0242, 347.6859, + 347.9177, 348.2372, 347.6983, 347.9181, 362.5876, 349.5010, 347.3098, 347.7068, 347.6530, 348.8905, + 349.1875, 347.7322, 347.8935, 350.1428, 347.8078, 347.4217, 347.9083, 351.6060, 364.3534, 362.0277, + 347.6155, 348.8701, 350.0820, 350.4459, 347.7448, 347.9179, 348.2846, 357.5326, 348.5791, 348.1976, + 348.2805, 347.8833, 351.8235, 348.4769, 348.1972, 354.2277, 348.7812, 347.5858, 347.5523, 347.9894, + 348.4267, 347.7855, 352.2337, 348.1318, 347.5751, 348.5764, 347.8220, 351.9602, 347.4684, 348.3530, + 348.3222, 348.4553, 347.7500, 347.9536, 348.3003, 348.6743, 350.6669, 348.7904, 348.7561, 349.0508, + 347.6311, 348.3785, 348.3788, 352.3984, 347.4498, 349.6662, 349.0177, 347.5398, 355.2611, 347.6171, + 349.0511, 347.3273, 347.3592, 347.8611, 347.5964, 347.5564, 347.6554, 348.2081, 348.2998, 347.7288, + 347.8346, 347.7421, 352.2372, 348.1965, 349.0271, 347.9774, 351.8596, 347.5298, 347.6308, 347.6561, + 347.8762, 347.3469, 347.6736, 352.0812, 349.5322, 349.2015, 348.5227, 352.9716, 347.7573, 353.2357, + 348.8211, 347.6180, 349.7692, 348.1887, 347.7775, 348.8799, 351.0806, 349.5768, 357.9883, 348.7598, + 351.0769, 347.7897, 349.8290, 349.0401, 349.8934, 347.3818, 347.4127, 347.2534, 347.2993, 347.3662, + 348.7725, 349.3746, 347.6021, 348.2902, 348.5113, 347.4069, 349.7877, 351.7751, 351.3461, 347.1249, + 349.4440, 348.1466, 347.3497, 347.8770, 350.7545, 349.9675, 347.6966, 347.6298, 349.0210, 347.7346, + 347.1405, 349.4634, 348.7612, 352.6694, 349.1828, 348.0399, 347.9772, 348.2017, 348.2891, 355.4806, + 347.5127, 348.5503, 347.4944, 349.2060, 347.8054, 351.7816, 353.4037, 347.9604, 350.0297, 347.5814, + 347.5632, 347.8805, 348.2123, 348.7926, 347.5597, 347.5986, 352.1924, 374.1594, 349.3394, 349.7771, + 347.5244, 348.7254, 356.8709, 348.2475, 347.4074, 351.4388, 348.5651, 347.5950, 347.9851, 350.7727, + 353.7092, 347.5937, 347.4309, 347.8552, 350.4125, 350.6890, 350.5143, 347.3917, 348.2776, 354.7649, + 349.4661, 347.4174, 348.2034, 350.1242, 347.3456, 347.8254, 351.1231, 347.5047, 347.4512, 352.3139, + 347.5133, 348.6214, 347.2056, 349.9073, 347.4756, 348.4515, 348.0344, 348.9201, 350.7587, 351.4907, + 350.3649, 352.5194, 348.8017, 348.0644, 349.5104, 348.0675, 347.8671, 351.0880, 349.6343, 347.5890, + 347.6001, 348.6831, 348.7830, 348.0030, 348.3257, 347.6348, 347.3166, 347.3369, 349.0966, 348.0506, + 355.5348, 347.4069, 348.8350, 348.2581, 347.2280, 347.5418, 348.6233, 348.1083, 347.7203, 348.0728, + 347.4089, 347.5658, 347.6575, 347.2848, 347.4562, 347.3834, 349.0502, 350.6191, 347.4489, 348.6910 }; - CheckIsGreater(x, y, new AbsoluteThreshold(t), u, pValue); } @@ -347,17 +276,16 @@ public void Issue_948(double t, double u, double pValue) { double[] x = { - 117.428150690794, 114.372210364342, 113.014660415649, 112.944919220209, 112.383144277334, 112.360203094482, 111.301456588507, - 110.338494982719, - 116.107656214237, 115.510661094189, 114.018304386139, 118.674752711058, 110.82060939908, 110.29270001173, 111.751715534925, - 112.928226778507, - 109.582877969742, 112.403332518339, 115.566637580395 + 117.428150690794, 114.372210364342, 113.014660415649, 112.944919220209, 112.383144277334, 112.360203094482, + 111.301456588507, 110.338494982719, 116.107656214237, 115.510661094189, 114.018304386139, 118.674752711058, + 110.82060939908, 110.29270001173, 111.751715534925, 112.928226778507, 109.582877969742, 112.403332518339, + 115.566637580395 }; double[] y = { - 87.7416295671463, 88.7655491733551, 88.2931792974472, 88.481253298521, 90.3308806920052, 90.3109546351433, 89.6487628591061, - 88.9729374647141, - 89.3410451281071, 88.1987927174568, 88.7037609219551, 89.2515526676178, 87.8502615296841, 87.7515925955772 + 87.7416295671463, 88.7655491733551, 88.2931792974472, 88.481253298521, 90.3308806920052, 90.3109546351433, + 89.6487628591061, 88.9729374647141, 89.3410451281071, 88.1987927174568, 88.7037609219551, 89.2515526676178, + 87.8502615296841, 87.7515925955772 }; CheckIsGreater(x, y, new RelativeThreshold(t), u, pValue); @@ -368,13 +296,13 @@ public void DifferentHypothesisTest01() { double[] x = { - 0.215316161646013, 0.183747835508391, 1.08604925494274, 0.028712984405315, 1.04225410029393, 0.0882655319405911, - -1.40843189023372, 0.055221362792148, -0.0713054097993633, 0.360867185953497 + 0.215316161646013, 0.183747835508391, 1.08604925494274, 0.028712984405315, 1.04225410029393, + 0.0882655319405911, -1.40843189023372, 0.055221362792148, -0.0713054097993633, 0.360867185953497 }; double[] y = { - 0.188399785254256, -0.556189264665308, 0.0867961527603394, -0.641029410393987, 0.363449731766391, -0.677465788588321, - 0.532818404290161, -0.326790597853394, 0.520969706406657, -0.313712240924816 + 0.188399785254256, -0.556189264665308, 0.0867961527603394, -0.641029410393987, 0.363449731766391, + -0.677465788588321, 0.532818404290161, -0.326790597853394, 0.520969706406657, -0.313712240924816 }; Threshold threshold = AbsoluteThreshold.Zero; Check(x, y, threshold, 61, 0.217936088679123, AlternativeHypothesis.Greater); @@ -387,13 +315,13 @@ public void DifferentHypothesisTest02() { double[] x = { - 10.215316161646, 10.1837478355084, 11.0860492549427, 10.0287129844053, 11.0422541002939, 10.0882655319406, 8.59156810976628, - 10.0552213627921, 9.92869459020064, 10.3608671859535 + 10.215316161646, 10.1837478355084, 11.0860492549427, 10.0287129844053, 11.0422541002939, 10.0882655319406, + 8.59156810976628, 10.0552213627921, 9.92869459020064, 10.3608671859535 }; double[] y = { - 0.188399785254256, -0.556189264665308, 0.0867961527603394, -0.641029410393987, 0.363449731766391, -0.677465788588321, - 0.532818404290161, -0.326790597853394, 0.520969706406657, -0.313712240924816 + 0.188399785254256, -0.556189264665308, 0.0867961527603394, -0.641029410393987, 0.363449731766391, + -0.677465788588321, 0.532818404290161, -0.326790597853394, 0.520969706406657, -0.313712240924816 }; Threshold threshold = AbsoluteThreshold.Zero; Check(x, y, threshold, 100, 5.41254411223451e-06, AlternativeHypothesis.Greater); @@ -406,34 +334,32 @@ public void DifferentHypothesisTest03() { double[] x = { - 2.37095844714667, 0.435301828603911, 1.36312841133734, 1.63286260496104, 1.404268323141, 0.893875483908516, 2.51152199743894, - 0.905340961586902, 3.01842371387704, 0.937285900947579 + 2.37095844714667, 0.435301828603911, 1.36312841133734, 1.63286260496104, 1.404268323141, 0.893875483908516, + 2.51152199743894, 0.905340961586902, 3.01842371387704, 0.937285900947579 }; double[] y = { - 1.30486965422349, 2.28664539270111, -1.38886070111234, -0.278788766817371, -0.133321336393658, 0.635950398070074, - -0.284252921416072, -2.65645542090478, -2.44046692857552, 1.32011334573019 + 1.30486965422349, 2.28664539270111, -1.38886070111234, -0.278788766817371, -0.133321336393658, + 0.635950398070074, -0.284252921416072, -2.65645542090478, -2.44046692857552, 1.32011334573019 }; Threshold threshold = new AbsoluteThreshold(1); Check(x, y, threshold, 66, 0.123725345861569, AlternativeHypothesis.Greater); Check(x, y, threshold, 66, 0.891218688432311, AlternativeHypothesis.Less); Check(x, y, threshold, 66, 0.247450691723138, AlternativeHypothesis.TwoSides); } - + [Fact] public void DifferentHypothesisTest04() { double[] x = { - -1.62645381074233, -0.816356675777918, -1.83562861241005, 0.595280802137792, - -0.670492228184639, -1.82046838411802, -0.512570947571515, -0.261675294870783, - -0.424218648346508, -1.30538838715636 + -1.62645381074233, -0.816356675777918, -1.83562861241005, 0.595280802137792, -0.670492228184639, + -1.82046838411802, -0.512570947571515, -0.261675294870783, -0.424218648346508, -1.30538838715636 }; double[] y = { - 1.51178116845085, 0.389843236411431, -0.621240580541804, -2.2146998871775, - 1.12493091814311, -0.0449336090152309, -0.0161902630989461, 0.943836210685299, - 0.821221195098089, 0.593901321217509 + 1.51178116845085, 0.389843236411431, -0.621240580541804, -2.2146998871775, 1.12493091814311, + -0.0449336090152309, -0.0161902630989461, 0.943836210685299, 0.821221195098089, 0.593901321217509 }; Threshold threshold = new AbsoluteThreshold(1); Check(x, y, threshold, 7, 0.999837623676633, AlternativeHypothesis.Greater); @@ -448,10 +374,10 @@ private void CheckIsGreater(double[] x, double[] y, Threshold t, double u, doubl } [AssertionMethod] - private void Check(double[] x, double[] y, Threshold t, double u, double pValue, AlternativeHypothesis alternativeHypothesis) + private void Check(double[] x, double[] y, Threshold t, double u, double pValue, AlternativeHypothesis alternative) { - var result = MannWhitneyTest.Instance.Run(x.ToSample(), y.ToSample(), alternativeHypothesis, t); - output.WriteLine("Alternative = " + alternativeHypothesis); + var result = MannWhitneyTest.Instance.Perform(x.ToSample(), y.ToSample(), alternative, t); + output.WriteLine("Alternative = " + alternative); output.WriteLine("Ux = " + result.Ux); output.WriteLine("Uy = " + result.Uy); output.WriteLine("p-value = " + result.PValue); diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/WelchTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/WelchTests.cs index 22c693d5..82105671 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/WelchTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/WelchTests.cs @@ -10,7 +10,7 @@ namespace Perfolizer.Tests.Mathematics.SignificanceTesting; public class WelchTests { private static readonly AbsoluteEqualityComparer EqualityComparer = new(1e-5); - + private readonly ITestOutputHelper output; public WelchTests(ITestOutputHelper output) => this.output = output; @@ -244,7 +244,7 @@ private void CheckGreater(double[] x, double[] y, Threshold threshold, double t, private void Check(double[] x, double[] y, Threshold threshold, double t, double df, double pValue, AlternativeHypothesis alternativeHypothesis) { - var welch = WelchTest.Instance.Run(x.ToSample(), y.ToSample(), alternativeHypothesis, threshold); + var welch = WelchTest.Instance.Perform(x.ToSample(), y.ToSample(), alternativeHypothesis, threshold); output.WriteLine("Alternative = " + alternativeHypothesis); output.WriteLine("T = " + welch.T); output.WriteLine("Df = " + welch.Df); diff --git a/src/Perfolizer/Perfolizer.sln.DotSettings b/src/Perfolizer/Perfolizer.sln.DotSettings index a64837e6..02c2a1fe 100644 --- a/src/Perfolizer/Perfolizer.sln.DotSettings +++ b/src/Perfolizer/Perfolizer.sln.DotSettings @@ -8,16 +8,19 @@ 0 4 True - True + False 120 UseExplicitType <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> True True True True True + True True True True diff --git a/src/Perfolizer/Perfolizer/Mathematics/Common/MathExtensions.cs b/src/Perfolizer/Perfolizer/Mathematics/Common/MathExtensions.cs index 64fdbc4c..3e9cb2bb 100644 --- a/src/Perfolizer/Perfolizer/Mathematics/Common/MathExtensions.cs +++ b/src/Perfolizer/Perfolizer/Mathematics/Common/MathExtensions.cs @@ -4,9 +4,11 @@ internal static class MathExtensions { public static double Sqr(this double x) => x * x; public static double Sqrt(this double x) => Math.Sqrt(x); + public static double Pow(this int x, double k) => Math.Pow(x, k); public static double Pow(this double x, double k) => Math.Pow(x, k); public static double Clamp(this double x, double min, double max) => Math.Min(Math.Max(x, min), max); public static int Clamp(this int x, int min, int max) => Math.Min(Math.Max(x, min), max); + public static int RoundToInt(this double x) => (int)Round(x); public static IEnumerable Clamp(this IEnumerable values, double min, double max) => values.Select(x => Clamp(x, min, max)); diff --git a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/Base/ISignificanceTwoSampleTest.cs b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/Base/ISignificanceTwoSampleTest.cs index fdabfeaf..e26da39f 100644 --- a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/Base/ISignificanceTwoSampleTest.cs +++ b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/Base/ISignificanceTwoSampleTest.cs @@ -5,5 +5,5 @@ namespace Perfolizer.Mathematics.SignificanceTesting.Base; public interface ISignificanceTwoSampleTest where T : SignificanceTwoSampleResult { - T Run(Sample x, Sample y, AlternativeHypothesis alternativeHypothesis = AlternativeHypothesis.Greater, Threshold? threshold = null); + T Perform(Sample x, Sample y, AlternativeHypothesis alternative, Threshold threshold); } \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/BrunnerMunzelTest.cs b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/BrunnerMunzelTest.cs index 7557d39f..60b32626 100644 --- a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/BrunnerMunzelTest.cs +++ b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/BrunnerMunzelTest.cs @@ -15,7 +15,7 @@ private BrunnerMunzelTest() { } - public BrunnerMunzelResult Run(Sample x, Sample y, AlternativeHypothesis alternativeHypothesis = AlternativeHypothesis.Greater, + public BrunnerMunzelResult Perform(Sample x, Sample y, AlternativeHypothesis alternative = AlternativeHypothesis.Greater, Threshold? threshold = null) { Assertion.NotNullOrEmpty(nameof(x), x); @@ -61,12 +61,12 @@ public BrunnerMunzelResult Run(Sample x, Sample y, AlternativeHypothesis alterna return Result(0.5, 0, double.NaN); double w = diff > 0 ? double.PositiveInfinity : double.NegativeInfinity; - return alternativeHypothesis switch + return alternative switch { AlternativeHypothesis.TwoSides => Result(0, w, double.NaN), AlternativeHypothesis.Less => Result(rxMean > ryMean ? 1 : 0, w, double.NaN), AlternativeHypothesis.Greater => Result(rxMean < ryMean ? 1 : 0, w, double.NaN), - _ => throw new ArgumentOutOfRangeException(nameof(alternativeHypothesis), alternativeHypothesis, null) + _ => throw new ArgumentOutOfRangeException(nameof(alternative), alternative, null) }; } else @@ -74,12 +74,12 @@ public BrunnerMunzelResult Run(Sample x, Sample y, AlternativeHypothesis alterna double w = (rxMean - ryMean) / Sqrt(sigma2 * (n + m)); double df = (sx2 / m + sy2 / n).Sqr() / ((sx2 / m).Sqr() / (n - 1) + (sy2 / n).Sqr() / (m - 1)); double cdf = new StudentDistribution(df).Cdf(w); - double pValue = SignificanceTestHelper.CdfToPValue(cdf, alternativeHypothesis); + double pValue = SignificanceTestHelper.CdfToPValue(cdf, alternative); return Result(pValue, w, df); } BrunnerMunzelResult Result(double pValueResult, double wResult, double dfResult) => - new(x, y, threshold, alternativeHypothesis, pValueResult, wResult, dfResult); + new(x, y, threshold, alternative, pValueResult, wResult, dfResult); } } \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/IMannWhitneyCdf.cs b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/IMannWhitneyCdf.cs new file mode 100644 index 00000000..9a560adc --- /dev/null +++ b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/IMannWhitneyCdf.cs @@ -0,0 +1,6 @@ +namespace Perfolizer.Mathematics.SignificanceTesting.MannWhitney; + +public interface IMannWhitneyCdf +{ + double Cdf(int n, int m, int u); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyClassicExactCdf.cs b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyClassicExactCdf.cs new file mode 100644 index 00000000..3d73d4aa --- /dev/null +++ b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyClassicExactCdf.cs @@ -0,0 +1,49 @@ +using Perfolizer.Mathematics.Common; + +namespace Perfolizer.Mathematics.SignificanceTesting.MannWhitney; + +public class MannWhitneyClassicExactCdf : IMannWhitneyCdf +{ + public static readonly MannWhitneyClassicExactCdf Instance = new(); + + public double Cdf(int n, int m, int u) + { + u -= 1; + + int q = (int)Floor(u + 1e-9); + int nm = Max(n, m); + long[,,] w = new long[nm + 1, nm + 1, q + 1]; + for (int i = 0; i <= nm; i++) + for (int j = 0; j <= nm; j++) + for (int k = 0; k <= q; k++) + { + if (i == 0 || j == 0 || k == 0) + w[i, j, k] = k == 0 ? 1 : 0; + else if (k > i * j) + w[i, j, k] = 0; + else if (i > j) + w[i, j, k] = w[j, i, k]; + else if (j > 0 && k < j) + w[i, j, k] = w[i, k, k]; + else + w[i, j, k] = w[i - 1, j, k - j] + w[i, j - 1, k]; + } + + long denominator = BinomialCoefficientHelper.BinomialCoefficient(n + m, m); + long p = 0; + if (q <= n * m / 2) + { + for (int i = 0; i <= q; i++) + p += w[n, m, i]; + } + else + { + q = n * m - q; + for (int i = 0; i < q; i++) + p += w[n, m, i]; + p = denominator - p; + } + + return p * 1.0 / denominator; + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyEdgeworthApproxCdf.cs b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyEdgeworthApproxCdf.cs new file mode 100644 index 00000000..9e912cf6 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyEdgeworthApproxCdf.cs @@ -0,0 +1,67 @@ +using System.Diagnostics.CodeAnalysis; +using Perfolizer.Mathematics.Common; +using Perfolizer.Mathematics.Distributions.ContinuousDistributions; +using Perfolizer.Mathematics.Functions; + +namespace Perfolizer.Mathematics.SignificanceTesting.MannWhitney; + +/// +/// https://aakinshin.net/posts/mw-edgeworth2/ +/// +public class MannWhitneyEdgeworthApproxCdf : IMannWhitneyCdf +{ + public static readonly MannWhitneyEdgeworthApproxCdf Instance = new(); + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public double Cdf(int n, int m, int u) + { + double mu = n * m / 2.0; + double su = Sqrt(n * m * (n + m + 1) / 12.0); + double z = (u - mu - 0.5) / su; + double phi = NormalDistribution.Standard.Pdf(z); + double Phi = NormalDistribution.Standard.Cdf(z); + + double mu2 = n * m * (n + m + 1) / 12.0; + double mu4 = + n * m * (n + m + 1) * + (0 + + 5 * m * n * (m + n) + - 2 * (m.Pow(2) + n.Pow(2)) + + 3 * m * n + - 2 * (n + m) + ) / 240.0; + + double mu6 = + n * m * (n + m + 1) * + (0 + + 35 * m.Pow(2) * n.Pow(2) * (m.Pow(2) + n.Pow(2)) + + 70 * m.Pow(3) * n.Pow(3) + - 42 * m * n * (m.Pow(3) + n.Pow(3)) + - 14 * m.Pow(2) * n.Pow(2) * (n + m) + + 16 * (n.Pow(4) + m.Pow(4)) + - 52 * n * m * (n.Pow(2) + m.Pow(2)) + - 43 * n.Pow(2) * m.Pow(2) + + 32 * (m.Pow(3) + n.Pow(3)) + + 14 * m * n * (n + m) + + 8 * (n.Pow(2) + m.Pow(2)) + + 16 * n * m + - 8 * (n + m) + ) / 4032.0; + + double e3 = (mu4 / Pow(mu2, 2) - 3) / Factorial(4); + double e5 = (mu6 / Pow(mu2, 3) - 15 * mu4 / Pow(mu2, 2) + 30) / Factorial(6); + double e7 = 35 * Pow((mu4 / Pow(mu2, 2) - 3), 2) / Factorial(8); + + double f3 = -phi * H3(z); + double f5 = -phi * H5(z); + double f7 = -phi * H7(z); + + double edgeworth = Phi + e3 * f3 + e5 * f5 + e7 * f7; + return Min(Max(edgeworth, 0), 1); + + double H3(double x) => x.Pow(3) - 3 * x; + double H5(double x) => x.Pow(5) - 10 * x.Pow(3) + 15 * x; + double H7(double x) => x.Pow(7) - 21 * x.Pow(5) + 105 * x.Pow(3) - 105 * x; + double Factorial(int x) => FactorialFunction.Value(x); + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyLoefflerExactCdf.cs b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyLoefflerExactCdf.cs new file mode 100644 index 00000000..f406617a --- /dev/null +++ b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyLoefflerExactCdf.cs @@ -0,0 +1,44 @@ +using Perfolizer.Mathematics.Common; + +namespace Perfolizer.Mathematics.SignificanceTesting.MannWhitney; + +/// +/// https://aakinshin.net/posts/mw-loeffler/ +/// +public class MannWhitneyLoefflerExactCdf : IMannWhitneyCdf +{ + public static readonly MannWhitneyLoefflerExactCdf Instance = new(); + + public double Cdf(int n, int m, int u) + { + return SumCdf(n, m, u) * 1.0 / BinomialCoefficientHelper.BinomialCoefficient(n + m, m); + } + + private long SumCdf(int n, int m, int u) => u <= 0 ? 0 : FullCdf(n, m, u).Sum(); + + // TODO: support big numbers + // TODO: research the maximum values of n and m that we can handle + private long[] FullCdf(int n, int m, int u) + { + u -= 1; + + int[] sigma = new int[u + 1]; + for (int d = 1; d <= n; d++) + for (int i = d; i <= u; i += d) + sigma[i] += d; + for (int d = m + 1; d <= m + n; d++) + for (int i = d; i <= u; i += d) + sigma[i] -= d; + + long[] p = new long[u + 1]; + p[0] = 1; + for (int a = 1; a <= u; a++) + { + for (int i = 0; i < a; i++) + p[a] += p[i] * sigma[a - i]; + p[a] /= a; + } + + return p; + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyNormalApproxCdf.cs b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyNormalApproxCdf.cs new file mode 100644 index 00000000..7c5b19b7 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyNormalApproxCdf.cs @@ -0,0 +1,16 @@ +using Perfolizer.Mathematics.Distributions.ContinuousDistributions; + +namespace Perfolizer.Mathematics.SignificanceTesting.MannWhitney; + +public class MannWhitneyNormalApproxCdf : IMannWhitneyCdf +{ + public static readonly MannWhitneyNormalApproxCdf Instance = new(); + + public double Cdf(int n, int m, int u) + { + double mu = n * m / 2.0; + double su = Sqrt(n * m * (n + m + 1) / 12.0); + double z = (u - mu) / su; + return NormalDistribution.Gauss(z); + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitneyResult.cs b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyResult.cs similarity index 90% rename from src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitneyResult.cs rename to src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyResult.cs index fb3e4b33..a4622bd3 100644 --- a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitneyResult.cs +++ b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyResult.cs @@ -3,7 +3,7 @@ using Perfolizer.Mathematics.SignificanceTesting.Base; using Perfolizer.Mathematics.Thresholds; -namespace Perfolizer.Mathematics.SignificanceTesting; +namespace Perfolizer.Mathematics.SignificanceTesting.MannWhitney; public class MannWhitneyResult : SignificanceTwoSampleResult { diff --git a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyStrategy.cs b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyStrategy.cs new file mode 100644 index 00000000..b0bf759f --- /dev/null +++ b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyStrategy.cs @@ -0,0 +1,10 @@ +namespace Perfolizer.Mathematics.SignificanceTesting.MannWhitney; + +public enum MannWhitneyStrategy +{ + Auto, + ClassicExact, + LoefflerExact, + NormalApprox, + EdgeworthApprox +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyTest.cs b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyTest.cs new file mode 100644 index 00000000..b169ca03 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitney/MannWhitneyTest.cs @@ -0,0 +1,114 @@ +using JetBrains.Annotations; +using Perfolizer.Common; +using Perfolizer.Mathematics.Common; +using Perfolizer.Mathematics.SignificanceTesting.Base; +using Perfolizer.Mathematics.Thresholds; + +namespace Perfolizer.Mathematics.SignificanceTesting.MannWhitney; + +public class MannWhitneyTest : ISignificanceTwoSampleTest +{ + public static readonly MannWhitneyTest Instance = new(); + + private static IMannWhitneyCdf GetCdfEstimator(MannWhitneyStrategy mannWhitneyStrategy, int n, int m) => + mannWhitneyStrategy switch + { + MannWhitneyStrategy.Auto => n + m <= BinomialCoefficientHelper.MaxAcceptableN // TODO: improve + ? MannWhitneyLoefflerExactCdf.Instance + : MannWhitneyEdgeworthApproxCdf.Instance, + MannWhitneyStrategy.ClassicExact => MannWhitneyClassicExactCdf.Instance, + MannWhitneyStrategy.LoefflerExact => MannWhitneyLoefflerExactCdf.Instance, + MannWhitneyStrategy.NormalApprox => MannWhitneyNormalApproxCdf.Instance, + MannWhitneyStrategy.EdgeworthApprox => MannWhitneyEdgeworthApproxCdf.Instance, + _ => throw new ArgumentOutOfRangeException(nameof(mannWhitneyStrategy), mannWhitneyStrategy, null) + }; + + private static MannWhitneyResult PerformGreater( + Sample x, + Sample y, + Threshold threshold, + MannWhitneyStrategy strategy) + { + // TODO: support weighted samples, see https://aakinshin.net/tags/research-wmw/ + Assertion.NonWeighted(nameof(x), x); + Assertion.NonWeighted(nameof(y), y); + + Assertion.NotNullOrEmpty(nameof(x), x); + Assertion.NotNullOrEmpty(nameof(y), y); + + int n = x.Count, m = y.Count; + double[] xy = new double[n + m]; + for (int i = 0; i < n; i++) + xy[i] = x.Values[i]; + for (int i = 0; i < m; i++) + xy[n + i] = y.Values[i]; + int[] index = new int[n + m]; + for (int i = 0; i < n + m; i++) + index[i] = i; + Array.Sort(index, (i, j) => xy[i].CompareTo(xy[j])); + + double[] ranks = new double[n + m]; + for (int i = 0; i < n + m;) + { + int j = i; + while (j + 1 < n + m && Abs(xy[index[j + 1]] - xy[index[i]]) < 1e-9) + j++; + double rank = (i + j + 2) / 2.0; + for (int k = i; k <= j; k++) + ranks[k] = rank; + i = j + 1; + } + + double ux = 0; + for (int i = 0; i < n + m; i++) + if (index[i] < n) + ux += ranks[i]; + ux -= n * (n + 1) / 2.0; + double uy = n * m - ux; + + + var cdfEstimator = GetCdfEstimator(strategy, n, m); + double cdf = cdfEstimator.Cdf(n, m, ux.RoundToInt()); + double pValue = 1 - cdf; + + return new MannWhitneyResult(x, y, threshold, AlternativeHypothesis.Greater, pValue, ux, uy); + } + + public MannWhitneyResult Perform(Sample x, Sample y, AlternativeHypothesis alternative, Threshold threshold) + { + return PerformStrategy(x, y, alternative, threshold); + } + + [PublicAPI] + public MannWhitneyResult PerformStrategy( + Sample x, + Sample y, + AlternativeHypothesis alternative, + Threshold threshold, + MannWhitneyStrategy mannWhitneyStrategy = MannWhitneyStrategy.Auto) + { + switch (alternative) + { + case AlternativeHypothesis.TwoSides: + { + var result1 = PerformGreater(x, threshold.Apply(y), threshold, mannWhitneyStrategy); + var result2 = PerformGreater(threshold.Apply(y), x, threshold, mannWhitneyStrategy); + double pValue = Min(Min(result1.PValue, result2.PValue) * 2, 1); + return new MannWhitneyResult(x, y, threshold, alternative, pValue, result1.Ux, result1.Uy); + } + case AlternativeHypothesis.Less: + { + var result = PerformGreater(threshold.Apply(y), x, threshold, mannWhitneyStrategy); + return new MannWhitneyResult(x, y, threshold, alternative, result.PValue, result.Uy, result.Ux); + } + case AlternativeHypothesis.Greater: + { + return PerformGreater(x, threshold.Apply(y), threshold, mannWhitneyStrategy); + } + default: + { + throw new ArgumentOutOfRangeException(nameof(alternative), alternative, null); + } + } + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitneyTest.cs b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitneyTest.cs deleted file mode 100644 index 708672e6..00000000 --- a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/MannWhitneyTest.cs +++ /dev/null @@ -1,154 +0,0 @@ -using Perfolizer.Common; -using Perfolizer.Mathematics.Common; -using Perfolizer.Mathematics.Distributions.ContinuousDistributions; -using Perfolizer.Mathematics.SignificanceTesting.Base; -using Perfolizer.Mathematics.Thresholds; - -namespace Perfolizer.Mathematics.SignificanceTesting; - -public class MannWhitneyTest : ISignificanceTwoSampleTest -{ - public enum Strategy - { - Auto, - Exact, - NormalApproximation - } - - public static readonly MannWhitneyTest Instance = new(); - - private static double ExactCdfSmallN(int n, int m, double u) - { - int q = (int)Floor(u + 1e-9); - int nm = Max(n, m); - long[,,] w = new long[nm + 1, nm + 1, q + 1]; - for (int i = 0; i <= nm; i++) - for (int j = 0; j <= nm; j++) - for (int k = 0; k <= q; k++) - { - if (i == 0 || j == 0 || k == 0) - w[i, j, k] = k == 0 ? 1 : 0; - else if (k > i * j) - w[i, j, k] = 0; - else if (i > j) - w[i, j, k] = w[j, i, k]; - else if (j > 0 && k < j) - w[i, j, k] = w[i, k, k]; - else - w[i, j, k] = w[i - 1, j, k - j] + w[i, j - 1, k]; - } - - long denominator = BinomialCoefficientHelper.BinomialCoefficient(n + m, m); - long p = 0; - if (q <= n * m / 2) - { - for (int i = 0; i <= q; i++) - p += w[n, m, i]; - } - else - { - q = n * m - q; - for (int i = 0; i < q; i++) - p += w[n, m, i]; - p = denominator - p; - } - - return p * 1.0 / denominator; - } - - private static double CdfNormalApproximation(double n, double m, double ux) - { - double mu = n * m / 2.0; - double su = Sqrt(n * m * (n + m + 1) / 12.0); - double z = (ux - mu) / su; - return NormalDistribution.Gauss(z); - } - - private static MannWhitneyResult RunGreater(Sample x, Sample y, Threshold threshold, Strategy strategy) - { - int n = x.Count, m = y.Count; - double[] xy = new double[n + m]; - for (int i = 0; i < n; i++) - xy[i] = x.Values[i]; - for (int i = 0; i < m; i++) - xy[n + i] = y.Values[i]; - int[] index = new int[n + m]; - for (int i = 0; i < n + m; i++) - index[i] = i; - Array.Sort(index, (i, j) => xy[i].CompareTo(xy[j])); - - var ranks = new double[n + m]; - for (int i = 0; i < n + m;) - { - int j = i; - while (j + 1 < n + m && Abs(xy[index[j + 1]] - xy[index[i]]) < 1e-9) - j++; - double rank = (i + j + 2) / 2.0; - for (int k = i; k <= j; k++) - ranks[k] = rank; - i = j + 1; - } - - double ux = 0; - for (int i = 0; i < n + m; i++) - if (index[i] < n) - ux += ranks[i]; - ux -= n * (n + 1) / 2.0; - double uy = n * m - ux; - - - if (strategy == Strategy.Auto) - strategy = n + m <= BinomialCoefficientHelper.MaxAcceptableN ? Strategy.Exact : Strategy.NormalApproximation; // TODO: improve - - double cdf = strategy switch - { - Strategy.Exact => ExactCdfSmallN(n, m, ux - 1), // TODO: support big N - Strategy.NormalApproximation => CdfNormalApproximation(n, m, ux), - _ => throw new ArgumentOutOfRangeException(nameof(strategy), strategy, null) - }; - double pValue = 1 - cdf; - - return new MannWhitneyResult(x, y, threshold, AlternativeHypothesis.Greater, pValue, ux, uy); - } - - public MannWhitneyResult Run(Sample x, Sample y, - AlternativeHypothesis alternativeHypothesis = AlternativeHypothesis.Greater, - Threshold? threshold = null) => - Run(x, y, alternativeHypothesis, threshold, Strategy.Auto); - - public MannWhitneyResult Run(Sample x, Sample y, - AlternativeHypothesis alternativeHypothesis = AlternativeHypothesis.Greater, - Threshold? threshold = null, - Strategy strategy = Strategy.Auto) - { - // TODO: support weighted case - Assertion.NonWeighted(nameof(x), x); - Assertion.NonWeighted(nameof(y), y); - - threshold ??= AbsoluteThreshold.Zero; - - int n = x.Count, m = y.Count; - if (Min(n, m) < 3 || Max(n, m) < 5) // TODO: adjust requirements - throw new NotSupportedException("Samples are two small"); - - switch (alternativeHypothesis) - { - case AlternativeHypothesis.TwoSides: - { - var result1 = RunGreater(x, threshold.Apply(y), threshold, strategy); - var result2 = RunGreater(threshold.Apply(y), x, threshold, strategy); - double pValue = Min(Min(result1.PValue, result2.PValue) * 2, 1); - return new MannWhitneyResult(x, y, threshold, alternativeHypothesis, pValue, result1.Ux, result1.Uy); - } - case AlternativeHypothesis.Less: - { - var result = RunGreater(threshold.Apply(y), x, threshold, strategy); - return new MannWhitneyResult(x, y, threshold, alternativeHypothesis, result.PValue, result.Uy, result.Ux); - } - case AlternativeHypothesis.Greater: - return RunGreater(x, threshold.Apply(y), threshold, strategy); - default: - throw new ArgumentOutOfRangeException(nameof(alternativeHypothesis), alternativeHypothesis, null); - } - } -} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/WelchTest.cs b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/WelchTest.cs index c8ad26b9..8764db22 100644 --- a/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/WelchTest.cs +++ b/src/Perfolizer/Perfolizer/Mathematics/SignificanceTesting/WelchTest.cs @@ -10,7 +10,7 @@ public class WelchTest : ISignificanceTwoSampleTest { public static readonly WelchTest Instance = new(); - public WelchTResult Run(Sample x, Sample y, AlternativeHypothesis alternativeHypothesis = AlternativeHypothesis.Greater, + public WelchTResult Perform(Sample x, Sample y, AlternativeHypothesis alternative = AlternativeHypothesis.Greater, Threshold? threshold = null) { Assertion.SizeLargerThan(nameof(x), x, 1); @@ -28,8 +28,8 @@ public WelchTResult Run(Sample x, Sample y, AlternativeHypothesis alternativeHyp double df = (v1 / n1 + v2 / n2).Sqr() / ((v1 / n1).Sqr() / (n1 - 1) + (v2 / n2).Sqr() / (n2 - 1)); double cdf = new StudentDistribution(df).Cdf(t); - double pValue = SignificanceTestHelper.CdfToPValue(cdf, alternativeHypothesis); + double pValue = SignificanceTestHelper.CdfToPValue(cdf, alternative); - return new WelchTResult(x, y, threshold, alternativeHypothesis, pValue, t, df); + return new WelchTResult(x, y, threshold, alternative, pValue, t, df); } } \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/Thresholds/Threshold.cs b/src/Perfolizer/Perfolizer/Mathematics/Thresholds/Threshold.cs index eee729a4..ad36944d 100644 --- a/src/Perfolizer/Perfolizer/Mathematics/Thresholds/Threshold.cs +++ b/src/Perfolizer/Perfolizer/Mathematics/Thresholds/Threshold.cs @@ -11,6 +11,6 @@ public Sample Apply(Sample sample) double[] values = new double[sample.Count]; for (int i = 0; i < sample.Count; i++) values[i] = Apply(sample.Values[i]); - return new Sample(values, sample.Weights); + return sample.IsWeighted ? new Sample(values, sample.Weights) : new Sample(values); } } \ No newline at end of file