Stats mannwhitney test - CyrilB1531/lodestar GitHub Wiki
Home โบ Stats โบ Hypothesis tests
Compares two independent samples by their ranks.
public static TestResult Test(ReadOnlySpan<double> x, ReadOnlySpan<double> y, Alternative alternative = Alternative.TwoSided, Continuity continuity = Continuity.Applied, ExactMethod method = ExactMethod.Auto, NanPolicy nanPolicy = NanPolicy.Propagate)Parameters โ x and y are the two samples, an empty one answering NaN; both spans are read,
never modified. alternative says which tail the p-value covers. continuity says whether the
normal approximation gets the half-unit correction; it is ignored on the exact branch, where
there is nothing to approximate. method chooses the exact null distribution, its normal
approximation, or a choice between them by sample size and ties. nanPolicy says what to do with
a NaN; scipy's nan_policy, defaulting to NanPolicy.Propagate.
Returns โ TestResult: U for x, and the p-value.
Exceptions โ ArgumentException when nanPolicy is
NanPolicy.Raise and either sample holds a NaN. ArgumentOutOfRangeException when method
is ExactMethod.Exact and x.Length * y.Length exceeds 20,000. An empty sample, given or left by omission, answers NaN for the statistic and the p-value
(decision 0010), and so does
the normal approximation's p-value on a pool tied throughout, which leaves it no variance โ two-sided,
or without the continuity correction; one-sided with it, the half-unit shift over a zero variance
reads 1, as scipy's does. ArgumentOutOfRangeException when an option is outside its enumeration.
Example โ a control group and a treated group, one value tied across them.
using Lodestar.Stats;
double[] control = [7.0, 3.0, 6.0, 2.0, 8.0, 5.0];
double[] treated = [9.0, 12.0, 8.0, 11.0, 15.0, 10.0];
TestResult result = MannWhitney.Test(control, treated);
double u = result.Statistic; // => 0.5
double p = Math.Round(result.PValue, 6); // => 0.006392Remarks โ control and treated share the value 8, so the pooled sample holds a tie and
ExactMethod.Auto falls straight to the normal approximation โ untied, both samples here are
small enough (six values each, at or under the eight-value bound Auto checks) that it would
have taken the exact route instead. Asking for ExactMethod.Exact explicitly still answers, just
not the same number: on this data it gives 0.004329 rather than 0.006392, because scipy
computes an exact p-value on tied data too instead of refusing, and this package matches that
rather than raising on a case scipy accepts.
Under NanPolicy.Propagate, a NaN reaches the statistic and the p-value. The check runs
before either sample is ranked โ unguarded, a NaN would sort to one end of its sample and take a
finite rank like any other value.
The exact route has a size bound Auto cannot cross. x.Length * y.Length above 20,000 costs
tens of seconds to enumerate โ the table is (m + 1) ร (nยทm + 1) and grows with the square of
that product. Passing ExactMethod.Exact past the bound throws; ExactMethod.Auto never does,
falling back to the asymptotic answer instead, because nothing the caller wrote asked for an
exact result. The bound on the smaller sample alone is eight, not both: x.Length = 8, y.Length = 10_000 still qualifies for Auto's exact route by that rule and would build a
multi-gigabyte table if the product bound did not also apply.
Applies to โ net10.0, netstandard2.0.
See also โ TTest.Independent for the parametric counterpart,
Wilcoxon.Paired for paired measurements,
ExactMethod, the Python equivalence table.