SpearmansCorrelation.java

  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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */

  21. package org.hipparchus.stat.correlation;

  22. import org.hipparchus.exception.LocalizedCoreFormats;
  23. import org.hipparchus.exception.MathIllegalArgumentException;
  24. import org.hipparchus.linear.BlockRealMatrix;
  25. import org.hipparchus.linear.RealMatrix;
  26. import org.hipparchus.stat.LocalizedStatFormats;
  27. import org.hipparchus.stat.ranking.NaNStrategy;
  28. import org.hipparchus.stat.ranking.NaturalRanking;
  29. import org.hipparchus.stat.ranking.RankingAlgorithm;
  30. import org.hipparchus.util.MathArrays;

  31. /**
  32.  * Spearman's rank correlation. This implementation performs a rank
  33.  * transformation on the input data and then computes {@link PearsonsCorrelation}
  34.  * on the ranked data.
  35.  * <p>
  36.  * By default, ranks are computed using {@link NaturalRanking} with default
  37.  * strategies for handling NaNs and ties in the data (NaNs maximal, ties averaged).
  38.  * The ranking algorithm can be set using a constructor argument.
  39.  */
  40. public class SpearmansCorrelation {

  41.     /** Input data */
  42.     private final RealMatrix data;

  43.     /** Ranking algorithm  */
  44.     private final RankingAlgorithm rankingAlgorithm;

  45.     /** Rank correlation */
  46.     private final PearsonsCorrelation rankCorrelation;

  47.     /**
  48.      * Create a SpearmansCorrelation without data.
  49.      */
  50.     public SpearmansCorrelation() {
  51.         this(new NaturalRanking());
  52.     }

  53.     /**
  54.      * Create a SpearmansCorrelation with the given ranking algorithm.
  55.      *
  56.      * @param rankingAlgorithm ranking algorithm
  57.      * @throws MathIllegalArgumentException if the provided {@link RankingAlgorithm} is of
  58.      * type {@link NaturalRanking} and uses a {@link NaNStrategy#REMOVED} strategy
  59.      */
  60.     public SpearmansCorrelation(final RankingAlgorithm rankingAlgorithm)
  61.         throws MathIllegalArgumentException {

  62.         if (rankingAlgorithm instanceof NaturalRanking &&
  63.             NaNStrategy.REMOVED == ((NaturalRanking) rankingAlgorithm).getNanStrategy()) {
  64.             throw new MathIllegalArgumentException(LocalizedStatFormats.NOT_SUPPORTED_NAN_STRATEGY,
  65.                                                    NaNStrategy.REMOVED);
  66.         }

  67.         data = null;
  68.         this.rankingAlgorithm = rankingAlgorithm;
  69.         rankCorrelation = null;
  70.     }

  71.     /**
  72.      * Create a SpearmansCorrelation from the given data matrix.
  73.      *
  74.      * @param dataMatrix matrix of data with columns representing
  75.      * variables to correlate
  76.      */
  77.     public SpearmansCorrelation(final RealMatrix dataMatrix) {
  78.         this(dataMatrix, new NaturalRanking());
  79.     }

  80.     /**
  81.      * Create a SpearmansCorrelation with the given input data matrix
  82.      * and ranking algorithm.
  83.      *
  84.      * @param dataMatrix matrix of data with columns representing
  85.      * variables to correlate
  86.      * @param rankingAlgorithm ranking algorithm
  87.      * @throws MathIllegalArgumentException if the provided {@link RankingAlgorithm} is of
  88.      * type {@link NaturalRanking} and uses a {@link NaNStrategy#REMOVED} strategy
  89.      */
  90.     public SpearmansCorrelation(final RealMatrix dataMatrix, final RankingAlgorithm rankingAlgorithm)
  91.         throws MathIllegalArgumentException {

  92.         if (rankingAlgorithm instanceof NaturalRanking &&
  93.             NaNStrategy.REMOVED == ((NaturalRanking) rankingAlgorithm).getNanStrategy()) {
  94.             throw new MathIllegalArgumentException(LocalizedStatFormats.NOT_SUPPORTED_NAN_STRATEGY,
  95.                                                    NaNStrategy.REMOVED);
  96.         }

  97.         this.rankingAlgorithm = rankingAlgorithm;
  98.         this.data = rankTransform(dataMatrix);
  99.         rankCorrelation = new PearsonsCorrelation(data);
  100.     }

  101.     /**
  102.      * Calculate the Spearman Rank Correlation Matrix.
  103.      *
  104.      * @return Spearman Rank Correlation Matrix
  105.      * @throws NullPointerException if this instance was created with no data
  106.      */
  107.     public RealMatrix getCorrelationMatrix() {
  108.         return rankCorrelation.getCorrelationMatrix();
  109.     }

  110.     /**
  111.      * Returns a {@link PearsonsCorrelation} instance constructed from the
  112.      * ranked input data. That is,
  113.      * <code>new SpearmansCorrelation(matrix).getRankCorrelation()</code>
  114.      * is equivalent to
  115.      * <code>new PearsonsCorrelation(rankTransform(matrix))</code> where
  116.      * <code>rankTransform(matrix)</code> is the result of applying the
  117.      * configured <code>RankingAlgorithm</code> to each of the columns of
  118.      * <code>matrix.</code>
  119.      *
  120.      * <p>Returns null if this instance was created with no data.</p>
  121.      *
  122.      * @return PearsonsCorrelation among ranked column data
  123.      */
  124.     public PearsonsCorrelation getRankCorrelation() {
  125.         return rankCorrelation;
  126.     }

  127.     /**
  128.      * Computes the Spearman's rank correlation matrix for the columns of the
  129.      * input matrix.
  130.      *
  131.      * @param matrix matrix with columns representing variables to correlate
  132.      * @return correlation matrix
  133.      */
  134.     public RealMatrix computeCorrelationMatrix(final RealMatrix matrix) {
  135.         final RealMatrix matrixCopy = rankTransform(matrix);
  136.         return new PearsonsCorrelation().computeCorrelationMatrix(matrixCopy);
  137.     }

  138.     /**
  139.      * Computes the Spearman's rank correlation matrix for the columns of the
  140.      * input rectangular array.  The columns of the array represent values
  141.      * of variables to be correlated.
  142.      *
  143.      * @param matrix matrix with columns representing variables to correlate
  144.      * @return correlation matrix
  145.      */
  146.     public RealMatrix computeCorrelationMatrix(final double[][] matrix) {
  147.        return computeCorrelationMatrix(new BlockRealMatrix(matrix));
  148.     }

  149.     /**
  150.      * Computes the Spearman's rank correlation coefficient between the two arrays.
  151.      *
  152.      * @param xArray first data array
  153.      * @param yArray second data array
  154.      * @return Returns Spearman's rank correlation coefficient for the two arrays
  155.      * @throws MathIllegalArgumentException if the arrays lengths do not match
  156.      * @throws MathIllegalArgumentException if the array length is less than 2
  157.      */
  158.     public double correlation(final double[] xArray, final double[] yArray) {
  159.         MathArrays.checkEqualLength(xArray, yArray);
  160.         if (xArray.length < 2) {
  161.             throw new MathIllegalArgumentException(LocalizedCoreFormats.INSUFFICIENT_DIMENSION,
  162.                                                    xArray.length, 2);
  163.         }

  164.         return new PearsonsCorrelation().correlation(rankingAlgorithm.rank(xArray),
  165.                                                      rankingAlgorithm.rank(yArray));
  166.     }

  167.     /**
  168.      * Applies rank transform to each of the columns of <code>matrix</code>
  169.      * using the current <code>rankingAlgorithm</code>.
  170.      *
  171.      * @param matrix matrix to transform
  172.      * @return a rank-transformed matrix
  173.      */
  174.     private RealMatrix rankTransform(final RealMatrix matrix) {
  175.         RealMatrix transformed = matrix.copy();
  176.         for (int i = 0; i < transformed.getColumnDimension(); i++) {
  177.             transformed.setColumn(i, rankingAlgorithm.rank(transformed.getColumn(i)));
  178.         }

  179.         return transformed;
  180.     }

  181. }