Skip to content

Commit bd406bf

Browse files
committed
MATH-1671: Update stat.descriptive package to use Commons Statistics
Fix test coverage for refactored code.
1 parent 6488bc5 commit bd406bf

File tree

5 files changed

+183
-41
lines changed

5 files changed

+183
-41
lines changed

commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/stat/StatUtils.java

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
2222
import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
2323
import org.apache.commons.math4.legacy.exception.NoDataException;
24-
import org.apache.commons.math4.legacy.exception.NotPositiveException;
2524
import org.apache.commons.math4.legacy.exception.NullArgumentException;
2625
import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
2726
import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
@@ -648,9 +647,8 @@ public static double[] normalize(final double[] sample) {
648647
* @since 3.3
649648
*/
650649
public static double[] mode(double[] sample) throws MathIllegalArgumentException {
651-
if (sample == null) {
652-
throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
653-
}
650+
// Zero length is allowed
651+
verifyValues(sample);
654652
return getMode(sample, 0, sample.length);
655653
}
656654

@@ -678,18 +676,7 @@ public static double[] mode(double[] sample) throws MathIllegalArgumentException
678676
* @since 3.3
679677
*/
680678
public static double[] mode(double[] sample, final int begin, final int length) {
681-
if (sample == null) {
682-
throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
683-
}
684-
685-
if (begin < 0) {
686-
throw new NotPositiveException(LocalizedFormats.START_POSITION, Integer.valueOf(begin));
687-
}
688-
689-
if (length < 0) {
690-
throw new NotPositiveException(LocalizedFormats.LENGTH, Integer.valueOf(length));
691-
}
692-
679+
MathArrays.verifyValues(sample, begin, length);
693680
return getMode(sample, begin, length);
694681
}
695682

commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/stat/descriptive/SummaryStatistics.java

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,6 @@ public long getN() {
178178
return n;
179179
}
180180

181-
/**
182-
* Checks if empty.
183-
*
184-
* @return true if empty
185-
*/
186-
private boolean isEmpty() {
187-
return getN() == 0;
188-
}
189-
190181
/**
191182
* Gets the statistic from the overridden implementation, or from the default implementation.
192183
*
@@ -195,6 +186,9 @@ private boolean isEmpty() {
195186
* @return the value
196187
*/
197188
private double getStatistic(StorelessUnivariateStatistic imp, Statistic s) {
189+
if (getN() == 0) {
190+
return Double.NaN;
191+
}
198192
return imp != null ? imp.getResult() : values.getAsDouble(s);
199193
}
200194

@@ -204,9 +198,6 @@ private double getStatistic(StorelessUnivariateStatistic imp, Statistic s) {
204198
*/
205199
@Override
206200
public double getSum() {
207-
if (isEmpty()) {
208-
return Double.NaN;
209-
}
210201
return getStatistic(sumImpl, Statistic.SUM);
211202
}
212203

@@ -218,9 +209,6 @@ public double getSum() {
218209
* @return The sum of squares
219210
*/
220211
public double getSumsq() {
221-
if (isEmpty()) {
222-
return Double.NaN;
223-
}
224212
return getStatistic(sumsqImpl, Statistic.SUM_OF_SQUARES);
225213
}
226214

@@ -289,9 +277,6 @@ public double getVariance() {
289277
*/
290278
@Override
291279
public double getMax() {
292-
if (isEmpty()) {
293-
return Double.NaN;
294-
}
295280
return getStatistic(maxImpl, Statistic.MAX);
296281
}
297282

@@ -304,9 +289,6 @@ public double getMax() {
304289
*/
305290
@Override
306291
public double getMin() {
307-
if (isEmpty()) {
308-
return Double.NaN;
309-
}
310292
return getStatistic(minImpl, Statistic.MIN);
311293
}
312294

@@ -330,9 +312,6 @@ public double getGeometricMean() {
330312
* @since 1.2
331313
*/
332314
public double getSumOfLogs() {
333-
if (isEmpty()) {
334-
return Double.NaN;
335-
}
336315
return getStatistic(sumLogImpl, Statistic.SUM_OF_LOGS);
337316
}
338317

commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/stat/StatUtilsTest.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.apache.commons.numbers.core.Precision;
2626
import org.junit.Assert;
2727
import org.junit.Test;
28+
import org.junit.jupiter.api.Assertions;
2829

2930
/**
3031
* Test cases for the {@link StatUtils} class.
@@ -108,6 +109,41 @@ public void testArrayIndexConditions() {
108109
}
109110
}
110111

112+
@Test
113+
public void testSum() {
114+
double[] x = null;
115+
116+
// test null
117+
try {
118+
StatUtils.sum(x);
119+
Assert.fail("null is not a valid data array.");
120+
} catch (NullArgumentException ex) {
121+
// success
122+
}
123+
124+
try {
125+
StatUtils.sum(x, 0, 4);
126+
Assert.fail("null is not a valid data array.");
127+
} catch (NullArgumentException ex) {
128+
// success
129+
}
130+
131+
// test empty
132+
x = new double[] {};
133+
TestUtils.assertEquals(Double.NaN, StatUtils.sum(x), TOLERANCE);
134+
TestUtils.assertEquals(Double.NaN, StatUtils.sum(x, 0, 0), TOLERANCE);
135+
136+
// test one
137+
x = new double[] {TWO};
138+
TestUtils.assertEquals(2, StatUtils.sum(x), TOLERANCE);
139+
TestUtils.assertEquals(2, StatUtils.sum(x, 0, 1), TOLERANCE);
140+
141+
// test many
142+
x = new double[] {ONE, TWO, TWO, THREE};
143+
TestUtils.assertEquals(8, StatUtils.sum(x), TOLERANCE);
144+
TestUtils.assertEquals(4, StatUtils.sum(x, 1, 2), TOLERANCE);
145+
}
146+
111147
@Test
112148
public void testSumSq() {
113149
double[] x = null;
@@ -226,14 +262,17 @@ public void testMean() {
226262

227263
// test empty
228264
x = new double[] {};
265+
TestUtils.assertEquals(Double.NaN, StatUtils.mean(x), TOLERANCE);
229266
TestUtils.assertEquals(Double.NaN, StatUtils.mean(x, 0, 0), TOLERANCE);
230267

231268
// test one
232269
x = new double[] {TWO};
270+
TestUtils.assertEquals(TWO, StatUtils.mean(x), TOLERANCE);
233271
TestUtils.assertEquals(TWO, StatUtils.mean(x, 0, 1), TOLERANCE);
234272

235273
// test many
236274
x = new double[] {ONE, TWO, TWO, THREE};
275+
TestUtils.assertEquals(2, StatUtils.mean(x), TOLERANCE);
237276
TestUtils.assertEquals(2.5, StatUtils.mean(x, 2, 2), TOLERANCE);
238277
}
239278

@@ -250,14 +289,17 @@ public void testVariance() {
250289

251290
// test empty
252291
x = new double[] {};
292+
TestUtils.assertEquals(Double.NaN, StatUtils.variance(x), TOLERANCE);
253293
TestUtils.assertEquals(Double.NaN, StatUtils.variance(x, 0, 0), TOLERANCE);
254294

255295
// test one
256296
x = new double[] {TWO};
297+
TestUtils.assertEquals(0.0, StatUtils.variance(x), TOLERANCE);
257298
TestUtils.assertEquals(0.0, StatUtils.variance(x, 0, 1), TOLERANCE);
258299

259300
// test many
260301
x = new double[] {ONE, TWO, TWO, THREE};
302+
TestUtils.assertEquals(2.0 / 3, StatUtils.variance(x), TOLERANCE);
261303
TestUtils.assertEquals(0.5, StatUtils.variance(x, 2, 2), TOLERANCE);
262304
}
263305

@@ -274,14 +316,17 @@ public void testPopulationVariance() {
274316

275317
// test empty
276318
x = new double[] {};
319+
TestUtils.assertEquals(Double.NaN, StatUtils.populationVariance(x), TOLERANCE);
277320
TestUtils.assertEquals(Double.NaN, StatUtils.populationVariance(x, 0, 0), TOLERANCE);
278321

279322
// test one
280323
x = new double[] {TWO};
281324
TestUtils.assertEquals(0.0, StatUtils.populationVariance(x, 0, 1), TOLERANCE);
325+
TestUtils.assertEquals(0.0, StatUtils.populationVariance(x, 0, 1), TOLERANCE);
282326

283327
// test many
284328
x = new double[] {ONE, TWO, TWO, THREE};
329+
TestUtils.assertEquals(0.5, StatUtils.populationVariance(x), TOLERANCE);
285330
TestUtils.assertEquals(0.25, StatUtils.populationVariance(x, 0, 2), TOLERANCE);
286331
}
287332

@@ -299,14 +344,17 @@ public void testMax() {
299344

300345
// test empty
301346
x = new double[] {};
347+
TestUtils.assertEquals(Double.NaN, StatUtils.max(x), TOLERANCE);
302348
TestUtils.assertEquals(Double.NaN, StatUtils.max(x, 0, 0), TOLERANCE);
303349

304350
// test one
305351
x = new double[] {TWO};
352+
TestUtils.assertEquals(TWO, StatUtils.max(x), TOLERANCE);
306353
TestUtils.assertEquals(TWO, StatUtils.max(x, 0, 1), TOLERANCE);
307354

308355
// test many
309356
x = new double[] {ONE, TWO, TWO, THREE};
357+
TestUtils.assertEquals(THREE, StatUtils.max(x), TOLERANCE);
310358
TestUtils.assertEquals(THREE, StatUtils.max(x, 1, 3), TOLERANCE);
311359

312360
// Legacy behaviour
@@ -341,14 +389,17 @@ public void testMin() {
341389

342390
// test empty
343391
x = new double[] {};
392+
TestUtils.assertEquals(Double.NaN, StatUtils.min(x), TOLERANCE);
344393
TestUtils.assertEquals(Double.NaN, StatUtils.min(x, 0, 0), TOLERANCE);
345394

346395
// test one
347396
x = new double[] {TWO};
397+
TestUtils.assertEquals(TWO, StatUtils.min(x), TOLERANCE);
348398
TestUtils.assertEquals(TWO, StatUtils.min(x, 0, 1), TOLERANCE);
349399

350400
// test many
351401
x = new double[] {ONE, TWO, TWO, THREE};
402+
TestUtils.assertEquals(ONE, StatUtils.min(x), TOLERANCE);
352403
TestUtils.assertEquals(TWO, StatUtils.min(x, 1, 3), TOLERANCE);
353404

354405
// Legacy behaviour
@@ -422,6 +473,13 @@ public void testDifferenceStats() {
422473
} catch (MathIllegalArgumentException ex) {
423474
// expected
424475
}
476+
try {
477+
double[] empty = {};
478+
StatUtils.meanDifference(empty, empty);
479+
Assert.fail("Expecting MathIllegalArgumentException");
480+
} catch (MathIllegalArgumentException ex) {
481+
// expected
482+
}
425483
try {
426484
StatUtils.varianceDifference(sample1, small, meanDifference);
427485
Assert.fail("Expecting MathIllegalArgumentException");
@@ -539,5 +597,15 @@ public void testMode() {
539597
} catch (NullArgumentException ex) {
540598
// Expected
541599
}
600+
601+
// Range tests
602+
Assertions.assertArrayEquals(new double[] {0}, StatUtils.mode(singleMode, 0, 4));
603+
Assertions.assertArrayEquals(new double[] {0, 2, 7, 11}, StatUtils.mode(singleMode, 2, 4));
604+
Assertions.assertArrayEquals(new double[] {0}, StatUtils.mode(twoMode, 0, 4));
605+
Assertions.assertArrayEquals(new double[] {0, 2}, StatUtils.mode(twoMode, 0, 5));
606+
Assertions.assertThrows(NullArgumentException.class, () -> StatUtils.mode(null, 0, 5));
607+
Assertions.assertThrows(MathIllegalArgumentException.class, () -> StatUtils.mode(singleMode, -1, 1));
608+
Assertions.assertThrows(MathIllegalArgumentException.class, () -> StatUtils.mode(singleMode, 0, -1));
609+
Assertions.assertThrows(MathIllegalArgumentException.class, () -> StatUtils.mode(singleMode, 0, 100));
542610
}
543611
}

commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/stat/descriptive/StatisticsTest.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,15 @@
1616
*/
1717
package org.apache.commons.math4.legacy.stat.descriptive;
1818

19+
import org.apache.commons.math4.legacy.exception.NullArgumentException;
20+
import org.apache.commons.math4.legacy.exception.OutOfRangeException;
21+
import org.apache.commons.math4.legacy.stat.descriptive.Statistics.Percentile;
1922
import org.apache.commons.math4.legacy.stat.descriptive.Statistics.StorelessSumOfSquares;
2023
import org.apache.commons.math4.legacy.stat.descriptive.Statistics.SumOfSquares;
21-
import org.junit.Test;
2224
import org.junit.jupiter.api.Assertions;
25+
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.params.ParameterizedTest;
27+
import org.junit.jupiter.params.provider.ValueSource;
2328

2429
/**
2530
* Test cases for the {@link Statistics} class.
@@ -42,4 +47,22 @@ public void testUnsupportedStorelessMethods() {
4247
Assertions.assertThrows(IllegalStateException.class, () -> s.evaluate(x));
4348
Assertions.assertThrows(IllegalStateException.class, () -> s.evaluate(x, 0, 1));
4449
}
50+
51+
@ParameterizedTest
52+
@ValueSource(doubles = {-1, 101, Double.NaN})
53+
public void testInvalidPercentileThrows(double p) {
54+
final Percentile stat = Percentile.create(50);
55+
Assertions.assertThrows(OutOfRangeException.class, () -> stat.setQuantile(p));
56+
Assertions.assertThrows(OutOfRangeException.class, () -> Percentile.create(p));
57+
}
58+
59+
@Test
60+
public void testPercentile() {
61+
final Percentile stat = Percentile.create(50);
62+
Assertions.assertThrows(NullArgumentException.class, () -> stat.evaluate(null));
63+
Assertions.assertEquals(Double.NaN, stat.evaluate(new double[] {}));
64+
Assertions.assertEquals(1, stat.evaluate(new double[] {1}));
65+
Assertions.assertEquals(1.5, stat.evaluate(new double[] {1, 2}));
66+
Assertions.assertEquals(2, stat.evaluate(new double[] {1, 2, 3}));
67+
}
4568
}

0 commit comments

Comments
 (0)