View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.math3.stat.inference;
18  
19  import org.apache.commons.math3.distribution.ChiSquaredDistribution;
20  import org.apache.commons.math3.exception.DimensionMismatchException;
21  import org.apache.commons.math3.exception.MaxCountExceededException;
22  import org.apache.commons.math3.exception.NotPositiveException;
23  import org.apache.commons.math3.exception.NotStrictlyPositiveException;
24  import org.apache.commons.math3.exception.NullArgumentException;
25  import org.apache.commons.math3.exception.OutOfRangeException;
26  import org.apache.commons.math3.exception.ZeroException;
27  import org.apache.commons.math3.exception.util.LocalizedFormats;
28  import org.apache.commons.math3.util.FastMath;
29  import org.apache.commons.math3.util.MathArrays;
30  
31  /**
32   * Implements Chi-Square test statistics.
33   *
34   * <p>This implementation handles both known and unknown distributions.</p>
35   *
36   * <p>Two samples tests can be used when the distribution is unknown <i>a priori</i>
37   * but provided by one sample, or when the hypothesis under test is that the two
38   * samples come from the same underlying distribution.</p>
39   *
40   * @version $Id: ChiSquareTest.java 1416643 2012-12-03 19:37:14Z tn $
41   */
42  public class ChiSquareTest {
43  
44      /**
45       * Construct a ChiSquareTest
46       */
47      public ChiSquareTest() {
48          super();
49      }
50  
51      /**
52       * Computes the <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
53       * Chi-Square statistic</a> comparing <code>observed</code> and <code>expected</code>
54       * frequency counts.
55       * <p>
56       * This statistic can be used to perform a Chi-Square test evaluating the null
57       * hypothesis that the observed counts follow the expected distribution.</p>
58       * <p>
59       * <strong>Preconditions</strong>: <ul>
60       * <li>Expected counts must all be positive.
61       * </li>
62       * <li>Observed counts must all be &ge; 0.
63       * </li>
64       * <li>The observed and expected arrays must have the same length and
65       * their common length must be at least 2.
66       * </li></ul></p><p>
67       * If any of the preconditions are not met, an
68       * <code>IllegalArgumentException</code> is thrown.</p>
69       * <p><strong>Note: </strong>This implementation rescales the
70       * <code>expected</code> array if necessary to ensure that the sum of the
71       * expected and observed counts are equal.</p>
72       *
73       * @param observed array of observed frequency counts
74       * @param expected array of expected frequency counts
75       * @return chiSquare test statistic
76       * @throws NotPositiveException if <code>observed</code> has negative entries
77       * @throws NotStrictlyPositiveException if <code>expected</code> has entries that are
78       * not strictly positive
79       * @throws DimensionMismatchException if the arrays length is less than 2
80       */
81      public double chiSquare(final double[] expected, final long[] observed)
82          throws NotPositiveException, NotStrictlyPositiveException,
83          DimensionMismatchException {
84  
85          if (expected.length < 2) {
86              throw new DimensionMismatchException(expected.length, 2);
87          }
88          if (expected.length != observed.length) {
89              throw new DimensionMismatchException(expected.length, observed.length);
90          }
91          MathArrays.checkPositive(expected);
92          MathArrays.checkNonNegative(observed);
93  
94          double sumExpected = 0d;
95          double sumObserved = 0d;
96          for (int i = 0; i < observed.length; i++) {
97              sumExpected += expected[i];
98              sumObserved += observed[i];
99          }
100         double ratio = 1.0d;
101         boolean rescale = false;
102         if (FastMath.abs(sumExpected - sumObserved) > 10E-6) {
103             ratio = sumObserved / sumExpected;
104             rescale = true;
105         }
106         double sumSq = 0.0d;
107         for (int i = 0; i < observed.length; i++) {
108             if (rescale) {
109                 final double dev = observed[i] - ratio * expected[i];
110                 sumSq += dev * dev / (ratio * expected[i]);
111             } else {
112                 final double dev = observed[i] - expected[i];
113                 sumSq += dev * dev / expected[i];
114             }
115         }
116         return sumSq;
117 
118     }
119 
120     /**
121      * Returns the <i>observed significance level</i>, or <a href=
122      * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
123      * p-value</a>, associated with a
124      * <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
125      * Chi-square goodness of fit test</a> comparing the <code>observed</code>
126      * frequency counts to those in the <code>expected</code> array.
127      * <p>
128      * The number returned is the smallest significance level at which one can reject
129      * the null hypothesis that the observed counts conform to the frequency distribution
130      * described by the expected counts.</p>
131      * <p>
132      * <strong>Preconditions</strong>: <ul>
133      * <li>Expected counts must all be positive.
134      * </li>
135      * <li>Observed counts must all be &ge; 0.
136      * </li>
137      * <li>The observed and expected arrays must have the same length and
138      * their common length must be at least 2.
139      * </li></ul></p><p>
140      * If any of the preconditions are not met, an
141      * <code>IllegalArgumentException</code> is thrown.</p>
142      * <p><strong>Note: </strong>This implementation rescales the
143      * <code>expected</code> array if necessary to ensure that the sum of the
144      * expected and observed counts are equal.</p>
145      *
146      * @param observed array of observed frequency counts
147      * @param expected array of expected frequency counts
148      * @return p-value
149      * @throws NotPositiveException if <code>observed</code> has negative entries
150      * @throws NotStrictlyPositiveException if <code>expected</code> has entries that are
151      * not strictly positive
152      * @throws DimensionMismatchException if the arrays length is less than 2
153      * @throws MaxCountExceededException if an error occurs computing the p-value
154      */
155     public double chiSquareTest(final double[] expected, final long[] observed)
156         throws NotPositiveException, NotStrictlyPositiveException,
157         DimensionMismatchException, MaxCountExceededException {
158 
159         ChiSquaredDistribution distribution =
160             new ChiSquaredDistribution(expected.length - 1.0);
161         return 1.0 - distribution.cumulativeProbability(chiSquare(expected, observed));
162     }
163 
164     /**
165      * Performs a <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
166      * Chi-square goodness of fit test</a> evaluating the null hypothesis that the
167      * observed counts conform to the frequency distribution described by the expected
168      * counts, with significance level <code>alpha</code>.  Returns true iff the null
169      * hypothesis can be rejected with 100 * (1 - alpha) percent confidence.
170      * <p>
171      * <strong>Example:</strong><br>
172      * To test the hypothesis that <code>observed</code> follows
173      * <code>expected</code> at the 99% level, use </p><p>
174      * <code>chiSquareTest(expected, observed, 0.01) </code></p>
175      * <p>
176      * <strong>Preconditions</strong>: <ul>
177      * <li>Expected counts must all be positive.
178      * </li>
179      * <li>Observed counts must all be &ge; 0.
180      * </li>
181      * <li>The observed and expected arrays must have the same length and
182      * their common length must be at least 2.
183      * <li> <code> 0 &lt; alpha &lt; 0.5 </code>
184      * </li></ul></p><p>
185      * If any of the preconditions are not met, an
186      * <code>IllegalArgumentException</code> is thrown.</p>
187      * <p><strong>Note: </strong>This implementation rescales the
188      * <code>expected</code> array if necessary to ensure that the sum of the
189      * expected and observed counts are equal.</p>
190      *
191      * @param observed array of observed frequency counts
192      * @param expected array of expected frequency counts
193      * @param alpha significance level of the test
194      * @return true iff null hypothesis can be rejected with confidence
195      * 1 - alpha
196      * @throws NotPositiveException if <code>observed</code> has negative entries
197      * @throws NotStrictlyPositiveException if <code>expected</code> has entries that are
198      * not strictly positive
199      * @throws DimensionMismatchException if the arrays length is less than 2
200      * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
201      * @throws MaxCountExceededException if an error occurs computing the p-value
202      */
203     public boolean chiSquareTest(final double[] expected, final long[] observed,
204                                  final double alpha)
205         throws NotPositiveException, NotStrictlyPositiveException,
206         DimensionMismatchException, OutOfRangeException, MaxCountExceededException {
207 
208         if ((alpha <= 0) || (alpha > 0.5)) {
209             throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
210                                           alpha, 0, 0.5);
211         }
212         return chiSquareTest(expected, observed) < alpha;
213 
214     }
215 
216     /**
217      *  Computes the Chi-Square statistic associated with a
218      * <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm">
219      *  chi-square test of independence</a> based on the input <code>counts</code>
220      *  array, viewed as a two-way table.
221      * <p>
222      * The rows of the 2-way table are
223      * <code>count[0], ... , count[count.length - 1] </code></p>
224      * <p>
225      * <strong>Preconditions</strong>: <ul>
226      * <li>All counts must be &ge; 0.
227      * </li>
228      * <li>The count array must be rectangular (i.e. all count[i] subarrays
229      *  must have the same length).
230      * </li>
231      * <li>The 2-way table represented by <code>counts</code> must have at
232      *  least 2 columns and at least 2 rows.
233      * </li>
234      * </li></ul></p><p>
235      * If any of the preconditions are not met, an
236      * <code>IllegalArgumentException</code> is thrown.</p>
237      *
238      * @param counts array representation of 2-way table
239      * @return chiSquare test statistic
240      * @throws NullArgumentException if the array is null
241      * @throws DimensionMismatchException if the array is not rectangular
242      * @throws NotPositiveException if {@code counts} has negative entries
243      */
244     public double chiSquare(final long[][] counts)
245         throws NullArgumentException, NotPositiveException,
246         DimensionMismatchException {
247 
248         checkArray(counts);
249         int nRows = counts.length;
250         int nCols = counts[0].length;
251 
252         // compute row, column and total sums
253         double[] rowSum = new double[nRows];
254         double[] colSum = new double[nCols];
255         double total = 0.0d;
256         for (int row = 0; row < nRows; row++) {
257             for (int col = 0; col < nCols; col++) {
258                 rowSum[row] += counts[row][col];
259                 colSum[col] += counts[row][col];
260                 total += counts[row][col];
261             }
262         }
263 
264         // compute expected counts and chi-square
265         double sumSq = 0.0d;
266         double expected = 0.0d;
267         for (int row = 0; row < nRows; row++) {
268             for (int col = 0; col < nCols; col++) {
269                 expected = (rowSum[row] * colSum[col]) / total;
270                 sumSq += ((counts[row][col] - expected) *
271                         (counts[row][col] - expected)) / expected;
272             }
273         }
274         return sumSq;
275 
276     }
277 
278     /**
279      * Returns the <i>observed significance level</i>, or <a href=
280      * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
281      * p-value</a>, associated with a
282      * <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm">
283      * chi-square test of independence</a> based on the input <code>counts</code>
284      * array, viewed as a two-way table.
285      * <p>
286      * The rows of the 2-way table are
287      * <code>count[0], ... , count[count.length - 1] </code></p>
288      * <p>
289      * <strong>Preconditions</strong>: <ul>
290      * <li>All counts must be &ge; 0.
291      * </li>
292      * <li>The count array must be rectangular (i.e. all count[i] subarrays must have
293      *     the same length).
294      * </li>
295      * <li>The 2-way table represented by <code>counts</code> must have at least 2
296      *     columns and at least 2 rows.
297      * </li>
298      * </li></ul></p><p>
299      * If any of the preconditions are not met, an
300      * <code>IllegalArgumentException</code> is thrown.</p>
301      *
302      * @param counts array representation of 2-way table
303      * @return p-value
304      * @throws NullArgumentException if the array is null
305      * @throws DimensionMismatchException if the array is not rectangular
306      * @throws NotPositiveException if {@code counts} has negative entries
307      * @throws MaxCountExceededException if an error occurs computing the p-value
308      */
309     public double chiSquareTest(final long[][] counts)
310         throws NullArgumentException, DimensionMismatchException,
311         NotPositiveException, MaxCountExceededException {
312 
313         checkArray(counts);
314         double df = ((double) counts.length -1) * ((double) counts[0].length - 1);
315         ChiSquaredDistribution distribution;
316         distribution = new ChiSquaredDistribution(df);
317         return 1 - distribution.cumulativeProbability(chiSquare(counts));
318 
319     }
320 
321     /**
322      * Performs a <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm">
323      * chi-square test of independence</a> evaluating the null hypothesis that the
324      * classifications represented by the counts in the columns of the input 2-way table
325      * are independent of the rows, with significance level <code>alpha</code>.
326      * Returns true iff the null hypothesis can be rejected with 100 * (1 - alpha) percent
327      * confidence.
328      * <p>
329      * The rows of the 2-way table are
330      * <code>count[0], ... , count[count.length - 1] </code></p>
331      * <p>
332      * <strong>Example:</strong><br>
333      * To test the null hypothesis that the counts in
334      * <code>count[0], ... , count[count.length - 1] </code>
335      *  all correspond to the same underlying probability distribution at the 99% level, use</p>
336      * <p><code>chiSquareTest(counts, 0.01)</code></p>
337      * <p>
338      * <strong>Preconditions</strong>: <ul>
339      * <li>All counts must be &ge; 0.
340      * </li>
341      * <li>The count array must be rectangular (i.e. all count[i] subarrays must have the
342      *     same length).</li>
343      * <li>The 2-way table represented by <code>counts</code> must have at least 2 columns and
344      *     at least 2 rows.</li>
345      * </li></ul></p><p>
346      * If any of the preconditions are not met, an
347      * <code>IllegalArgumentException</code> is thrown.</p>
348      *
349      * @param counts array representation of 2-way table
350      * @param alpha significance level of the test
351      * @return true iff null hypothesis can be rejected with confidence
352      * 1 - alpha
353      * @throws NullArgumentException if the array is null
354      * @throws DimensionMismatchException if the array is not rectangular
355      * @throws NotPositiveException if {@code counts} has any negative entries
356      * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
357      * @throws MaxCountExceededException if an error occurs computing the p-value
358      */
359     public boolean chiSquareTest(final long[][] counts, final double alpha)
360         throws NullArgumentException, DimensionMismatchException,
361         NotPositiveException, OutOfRangeException, MaxCountExceededException {
362 
363         if ((alpha <= 0) || (alpha > 0.5)) {
364             throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
365                                           alpha, 0, 0.5);
366         }
367         return chiSquareTest(counts) < alpha;
368 
369     }
370 
371     /**
372      * <p>Computes a
373      * <a href="http://www.itl.nist.gov/div898/software/dataplot/refman1/auxillar/chi2samp.htm">
374      * Chi-Square two sample test statistic</a> comparing bin frequency counts
375      * in <code>observed1</code> and <code>observed2</code>.  The
376      * sums of frequency counts in the two samples are not required to be the
377      * same.  The formula used to compute the test statistic is</p>
378      * <code>
379      * &sum;[(K * observed1[i] - observed2[i]/K)<sup>2</sup> / (observed1[i] + observed2[i])]
380      * </code> where
381      * <br/><code>K = &sqrt;[&sum(observed2 / &sum;(observed1)]</code>
382      * </p>
383      * <p>This statistic can be used to perform a Chi-Square test evaluating the
384      * null hypothesis that both observed counts follow the same distribution.</p>
385      * <p>
386      * <strong>Preconditions</strong>: <ul>
387      * <li>Observed counts must be non-negative.
388      * </li>
389      * <li>Observed counts for a specific bin must not both be zero.
390      * </li>
391      * <li>Observed counts for a specific sample must not all be 0.
392      * </li>
393      * <li>The arrays <code>observed1</code> and <code>observed2</code> must have
394      * the same length and their common length must be at least 2.
395      * </li></ul></p><p>
396      * If any of the preconditions are not met, an
397      * <code>IllegalArgumentException</code> is thrown.</p>
398      *
399      * @param observed1 array of observed frequency counts of the first data set
400      * @param observed2 array of observed frequency counts of the second data set
401      * @return chiSquare test statistic
402      * @throws DimensionMismatchException the the length of the arrays does not match
403      * @throws NotPositiveException if any entries in <code>observed1</code> or
404      * <code>observed2</code> are negative
405      * @throws ZeroException if either all counts of <code>observed1</code> or
406      * <code>observed2</code> are zero, or if the count at some index is zero
407      * for both arrays
408      * @since 1.2
409      */
410     public double chiSquareDataSetsComparison(long[] observed1, long[] observed2)
411         throws DimensionMismatchException, NotPositiveException, ZeroException {
412 
413         // Make sure lengths are same
414         if (observed1.length < 2) {
415             throw new DimensionMismatchException(observed1.length, 2);
416         }
417         if (observed1.length != observed2.length) {
418             throw new DimensionMismatchException(observed1.length, observed2.length);
419         }
420 
421         // Ensure non-negative counts
422         MathArrays.checkNonNegative(observed1);
423         MathArrays.checkNonNegative(observed2);
424 
425         // Compute and compare count sums
426         long countSum1 = 0;
427         long countSum2 = 0;
428         boolean unequalCounts = false;
429         double weight = 0.0;
430         for (int i = 0; i < observed1.length; i++) {
431             countSum1 += observed1[i];
432             countSum2 += observed2[i];
433         }
434         // Ensure neither sample is uniformly 0
435         if (countSum1 == 0 || countSum2 == 0) {
436             throw new ZeroException();
437         }
438         // Compare and compute weight only if different
439         unequalCounts = countSum1 != countSum2;
440         if (unequalCounts) {
441             weight = FastMath.sqrt((double) countSum1 / (double) countSum2);
442         }
443         // Compute ChiSquare statistic
444         double sumSq = 0.0d;
445         double dev = 0.0d;
446         double obs1 = 0.0d;
447         double obs2 = 0.0d;
448         for (int i = 0; i < observed1.length; i++) {
449             if (observed1[i] == 0 && observed2[i] == 0) {
450                 throw new ZeroException(LocalizedFormats.OBSERVED_COUNTS_BOTTH_ZERO_FOR_ENTRY, i);
451             } else {
452                 obs1 = observed1[i];
453                 obs2 = observed2[i];
454                 if (unequalCounts) { // apply weights
455                     dev = obs1/weight - obs2 * weight;
456                 } else {
457                     dev = obs1 - obs2;
458                 }
459                 sumSq += (dev * dev) / (obs1 + obs2);
460             }
461         }
462         return sumSq;
463     }
464 
465     /**
466      * <p>Returns the <i>observed significance level</i>, or <a href=
467      * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
468      * p-value</a>, associated with a Chi-Square two sample test comparing
469      * bin frequency counts in <code>observed1</code> and
470      * <code>observed2</code>.
471      * </p>
472      * <p>The number returned is the smallest significance level at which one
473      * can reject the null hypothesis that the observed counts conform to the
474      * same distribution.
475      * </p>
476      * <p>See {@link #chiSquareDataSetsComparison(long[], long[])} for details
477      * on the formula used to compute the test statistic. The degrees of
478      * of freedom used to perform the test is one less than the common length
479      * of the input observed count arrays.
480      * </p>
481      * <strong>Preconditions</strong>: <ul>
482      * <li>Observed counts must be non-negative.
483      * </li>
484      * <li>Observed counts for a specific bin must not both be zero.
485      * </li>
486      * <li>Observed counts for a specific sample must not all be 0.
487      * </li>
488      * <li>The arrays <code>observed1</code> and <code>observed2</code> must
489      * have the same length and
490      * their common length must be at least 2.
491      * </li></ul><p>
492      * If any of the preconditions are not met, an
493      * <code>IllegalArgumentException</code> is thrown.</p>
494      *
495      * @param observed1 array of observed frequency counts of the first data set
496      * @param observed2 array of observed frequency counts of the second data set
497      * @return p-value
498      * @throws DimensionMismatchException the the length of the arrays does not match
499      * @throws NotPositiveException if any entries in <code>observed1</code> or
500      * <code>observed2</code> are negative
501      * @throws ZeroException if either all counts of <code>observed1</code> or
502      * <code>observed2</code> are zero, or if the count at the same index is zero
503      * for both arrays
504      * @throws MaxCountExceededException if an error occurs computing the p-value
505      * @since 1.2
506      */
507     public double chiSquareTestDataSetsComparison(long[] observed1, long[] observed2)
508         throws DimensionMismatchException, NotPositiveException, ZeroException,
509         MaxCountExceededException {
510 
511         ChiSquaredDistribution distribution;
512         distribution = new ChiSquaredDistribution((double) observed1.length - 1);
513         return 1 - distribution.cumulativeProbability(
514                 chiSquareDataSetsComparison(observed1, observed2));
515 
516     }
517 
518     /**
519      * <p>Performs a Chi-Square two sample test comparing two binned data
520      * sets. The test evaluates the null hypothesis that the two lists of
521      * observed counts conform to the same frequency distribution, with
522      * significance level <code>alpha</code>.  Returns true iff the null
523      * hypothesis can be rejected with 100 * (1 - alpha) percent confidence.
524      * </p>
525      * <p>See {@link #chiSquareDataSetsComparison(long[], long[])} for
526      * details on the formula used to compute the Chisquare statistic used
527      * in the test. The degrees of of freedom used to perform the test is
528      * one less than the common length of the input observed count arrays.
529      * </p>
530      * <strong>Preconditions</strong>: <ul>
531      * <li>Observed counts must be non-negative.
532      * </li>
533      * <li>Observed counts for a specific bin must not both be zero.
534      * </li>
535      * <li>Observed counts for a specific sample must not all be 0.
536      * </li>
537      * <li>The arrays <code>observed1</code> and <code>observed2</code> must
538      * have the same length and their common length must be at least 2.
539      * </li>
540      * <li> <code> 0 < alpha < 0.5 </code>
541      * </li></ul><p>
542      * If any of the preconditions are not met, an
543      * <code>IllegalArgumentException</code> is thrown.</p>
544      *
545      * @param observed1 array of observed frequency counts of the first data set
546      * @param observed2 array of observed frequency counts of the second data set
547      * @param alpha significance level of the test
548      * @return true iff null hypothesis can be rejected with confidence
549      * 1 - alpha
550      * @throws DimensionMismatchException the the length of the arrays does not match
551      * @throws NotPositiveException if any entries in <code>observed1</code> or
552      * <code>observed2</code> are negative
553      * @throws ZeroException if either all counts of <code>observed1</code> or
554      * <code>observed2</code> are zero, or if the count at the same index is zero
555      * for both arrays
556      * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
557      * @throws MaxCountExceededException if an error occurs performing the test
558      * @since 1.2
559      */
560     public boolean chiSquareTestDataSetsComparison(final long[] observed1,
561                                                    final long[] observed2,
562                                                    final double alpha)
563         throws DimensionMismatchException, NotPositiveException,
564         ZeroException, OutOfRangeException, MaxCountExceededException {
565 
566         if (alpha <= 0 ||
567             alpha > 0.5) {
568             throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
569                                           alpha, 0, 0.5);
570         }
571         return chiSquareTestDataSetsComparison(observed1, observed2) < alpha;
572 
573     }
574 
575     /**
576      * Checks to make sure that the input long[][] array is rectangular,
577      * has at least 2 rows and 2 columns, and has all non-negative entries.
578      *
579      * @param in input 2-way table to check
580      * @throws NullArgumentException if the array is null
581      * @throws DimensionMismatchException if the array is not valid
582      * @throws NotPositiveException if the array contains any negative entries
583      */
584     private void checkArray(final long[][] in)
585         throws NullArgumentException, DimensionMismatchException,
586         NotPositiveException {
587 
588         if (in.length < 2) {
589             throw new DimensionMismatchException(in.length, 2);
590         }
591 
592         if (in[0].length < 2) {
593             throw new DimensionMismatchException(in[0].length, 2);
594         }
595 
596         MathArrays.checkRectangular(in);
597         MathArrays.checkNonNegative(in);
598 
599     }
600 
601 }