This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-statistics.git
The following commit(s) were added to refs/heads/master by this push:
new 1d9cddcc STATISTICS-98: Check the p-value is valid before significance
test
1d9cddcc is described below
commit 1d9cddccf47a0230a6f57e24e67999dd1046ef8e
Author: Alex Herbert <[email protected]>
AuthorDate: Thu Aug 27 14:23:40 2026 +0100
STATISTICS-98: Check the p-value is valid before significance test
---
.../commons/statistics/inference/Arguments.java | 14 ++++++++++
.../statistics/inference/SignificanceResult.java | 8 ++++--
.../statistics/inference/ArgumentsTest.java | 15 ++++++++++
.../inference/SignificanceResultTest.java | 19 ++++++++++++-
.../commons/statistics/inference/TTestTest.java | 32 ++++++++++++++++++++++
src/changes/changes.xml | 5 ++++
6 files changed, 90 insertions(+), 3 deletions(-)
diff --git
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/Arguments.java
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/Arguments.java
index b15ddb4f..c054e52b 100644
---
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/Arguments.java
+++
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/Arguments.java
@@ -45,6 +45,20 @@ final class Arguments {
throw new InferenceException(InferenceException.INVALID_SIGNIFICANCE,
alpha);
}
+ /**
+ * Check the probability {@code p} is in the interval {@code [0, 1]}.
+ *
+ * @param p Probability
+ * @throws IllegalArgumentException if {@code p < 0} or {@code p > 1}
+ */
+ static void checkProbability(double p) {
+ if (p >= 0 && p <= 1) {
+ return;
+ }
+ // Out-of-range or NaN
+ throw new InferenceException(InferenceException.INVALID_PROBABILITY,
p);
+ }
+
/**
* Check that the value is {@code >= 0}.
*
diff --git
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/SignificanceResult.java
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/SignificanceResult.java
index b8ac9045..c1b36e3a 100644
---
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/SignificanceResult.java
+++
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/SignificanceResult.java
@@ -47,10 +47,14 @@ public interface SignificanceResult {
*
* @param alpha Significance level of the test.
* @return true iff null hypothesis can be rejected with confidence {@code
1 - alpha}
- * @throws IllegalArgumentException if {@code alpha} is not in the range
{@code (0, 0.5]}.
+ * @throws IllegalArgumentException if {@code alpha} is not in the
interval {@code (0, 0.5]};
+ * or if the computed {@code p} is not in the interval {@code [0, 1]}
+ * @see #getPValue()
*/
default boolean reject(double alpha) {
Arguments.checkSignificance(alpha);
- return getPValue() < alpha;
+ final double p = getPValue();
+ Arguments.checkProbability(p);
+ return p < alpha;
}
}
diff --git
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/ArgumentsTest.java
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/ArgumentsTest.java
index 8dbc0a22..792f06a2 100644
---
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/ArgumentsTest.java
+++
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/ArgumentsTest.java
@@ -36,6 +36,21 @@ class ArgumentsTest {
Assertions.assertTrue(ex.getMessage().contains(Double.toString(alpha)));
}
+ @ParameterizedTest
+ @ValueSource(doubles = {-0.0, 0.0, Double.MIN_VALUE, 1 - 0x1.0p-53, 1})
+ void testCheckProbability(double p) {
+ Assertions.assertDoesNotThrow(() -> Arguments.checkProbability(p), ()
-> Double.toString(p));
+ }
+
+ @ParameterizedTest
+ @ValueSource(doubles = {-Double.MIN_VALUE, -0.1, -1, -2, 1.0 + 0x1.0p-52,
1.1, -Double.MAX_VALUE,
+ Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN})
+ void testCheckProbabilityThrows(double p) {
+ final IllegalArgumentException ex =
Assertions.assertThrows(IllegalArgumentException.class,
+ () -> Arguments.checkProbability(p), () -> Double.toString(p));
+ Assertions.assertTrue(ex.getMessage().contains(Double.toString(p)));
+ }
+
@ParameterizedTest
@ValueSource(ints = {Integer.MIN_VALUE, -1})
void testCheckNonNegativeIntThrows(double v) {
diff --git
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/SignificanceResultTest.java
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/SignificanceResultTest.java
index 83eff026..0f42ddd8 100644
---
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/SignificanceResultTest.java
+++
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/SignificanceResultTest.java
@@ -40,7 +40,7 @@ class SignificanceResultTest {
@ParameterizedTest
@ValueSource(doubles = {-1, -Double.MIN_VALUE, 0, 0.5000000000000001})
- void testRejectThrows(double alpha) {
+ void testRejectThrowsWithBadAlpha(double alpha) {
final SignificanceResult r = new SignificanceResult() {
@Override
public double getStatistic() {
@@ -54,6 +54,23 @@ class SignificanceResultTest {
Assertions.assertThrows(IllegalArgumentException.class, () ->
r.reject(alpha));
}
+ @ParameterizedTest
+ @ValueSource(doubles = {-Double.MIN_VALUE, -0.1, -1, -2, 1.0 + 0x1.0p-52,
1.1, -Double.MAX_VALUE,
+ Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN})
+ void testRejectThrowsWithBadPValue(double p) {
+ final SignificanceResult r = new SignificanceResult() {
+ @Override
+ public double getStatistic() {
+ return 0;
+ }
+ @Override
+ public double getPValue() {
+ return p;
+ }
+ };
+ Assertions.assertThrows(IllegalArgumentException.class, () ->
r.reject(0.05));
+ }
+
@ParameterizedTest
@ValueSource(doubles = {0, Double.MIN_VALUE, 1e-6, 0.01, 0.05, 0.5})
void testReject(double p) {
diff --git
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/TTestTest.java
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/TTestTest.java
index 376e1a61..37740a9f 100644
---
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/TTestTest.java
+++
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/TTestTest.java
@@ -448,4 +448,36 @@ class TTestTest {
new double[] {0.50953294711549812, 0.25476647355774906,
0.74523352644225094}));
return builder.build();
}
+
+ /**
+ * This test asserts the current behaviour of the TTest when a mean is not
finite.
+ * The TTest rejects non-finite samples in array arguments, but it cannot
reject
+ * non-finite means as this can produce a valid result (t=infinity; p=0)
because
+ * the variance can be provided as strictly positive finite.
+ * When the statistic is NaN, the p-value is NaN and the significance
result
+ * will raise an exception when attempting to reject the null hypothesis.
+ *
+ * See STATISTICS-97; STATISTICS-98.
+ */
+ @Test
+ void testRejectWithInvalidStatistic() {
+ final TTest test = TTest.withDefaults();
+ // Single infinity in one sample can compute a valid statistic
+ // thus we cannot validate the mean is finite for the paired test.
+ final SignificanceResult r1 = test.test(Double.POSITIVE_INFINITY, 1, 2,
+ 1, 1, 2);
+ Assertions.assertEquals(Double.POSITIVE_INFINITY, r1.getStatistic());
+ Assertions.assertEquals(0, r1.getPValue());
+ // The two samples are not the same
+ Assertions.assertTrue(() -> r1.reject(0.05), "p=0 should reject any
non-zero significance level");
+
+ // Infinity mean in both samples cannot compute a valid statistic.
+ // This is returned and will throw if the result is used to reject a
null hypothesis.
+ final SignificanceResult r2 = test.test(Double.POSITIVE_INFINITY, 1, 2,
+ Double.POSITIVE_INFINITY, 1,
2);
+ Assertions.assertEquals(Double.NaN, r2.getStatistic());
+ Assertions.assertEquals(Double.NaN, r2.getPValue());
+ Assertions.assertThrows(IllegalArgumentException.class,
+ () -> r2.reject(0.05));
+ }
}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 390ba746..58768a9d 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -53,6 +53,11 @@ If the output is not quite correct, check for invisible
trailing spaces!
</properties>
<body>
<release version="1.4" date="TBD" description="Adds new features (requires
Java 8).">
+ <action dev="aherbert" type="fix" due-to="Security scan, Aex Herbert"
issue="STATISTICS-98">
+ "SignificanceResult": Raise an exception if the p-value is invalid when
+ comparing the p-value to a significance level. Corrects silent
rejection
+ of the null hypothesis if the computed p-value is NaN.
+ </action>
<action dev="aherbert" type="fix" due-to="Security scan, Aex Herbert"
issue="STATISTICS-97">
"TTest/OneWayAnova": Checks added for arguments that invalidate the
test
statistic: non-finite values in input arrays; and NaN mean arguments.