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 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 23 package org.hipparchus.stat.correlation; 24 25 import org.hipparchus.exception.LocalizedCoreFormats; 26 import org.hipparchus.exception.MathIllegalArgumentException; 27 import org.hipparchus.linear.BlockRealMatrix; 28 import org.hipparchus.linear.RealMatrix; 29 import org.hipparchus.stat.LocalizedStatFormats; 30 import org.hipparchus.stat.ranking.NaNStrategy; 31 import org.hipparchus.stat.ranking.NaturalRanking; 32 import org.hipparchus.stat.ranking.RankingAlgorithm; 33 import org.hipparchus.util.MathArrays; 34 35 /** 36 * Spearman's rank correlation. This implementation performs a rank 37 * transformation on the input data and then computes {@link PearsonsCorrelation} 38 * on the ranked data. 39 * <p> 40 * By default, ranks are computed using {@link NaturalRanking} with default 41 * strategies for handling NaNs and ties in the data (NaNs maximal, ties averaged). 42 * The ranking algorithm can be set using a constructor argument. 43 */ 44 public class SpearmansCorrelation { 45 46 /** Input data */ 47 private final RealMatrix data; 48 49 /** Ranking algorithm */ 50 private final RankingAlgorithm rankingAlgorithm; 51 52 /** Rank correlation */ 53 private final PearsonsCorrelation rankCorrelation; 54 55 /** 56 * Create a SpearmansCorrelation without data. 57 */ 58 public SpearmansCorrelation() { 59 this(new NaturalRanking()); 60 } 61 62 /** 63 * Create a SpearmansCorrelation with the given ranking algorithm. 64 * 65 * @param rankingAlgorithm ranking algorithm 66 * @throws MathIllegalArgumentException if the provided {@link RankingAlgorithm} is of 67 * type {@link NaturalRanking} and uses a {@link NaNStrategy#REMOVED} strategy 68 */ 69 public SpearmansCorrelation(final RankingAlgorithm rankingAlgorithm) 70 throws MathIllegalArgumentException { 71 72 if (rankingAlgorithm instanceof NaturalRanking && 73 NaNStrategy.REMOVED == ((NaturalRanking) rankingAlgorithm).getNanStrategy()) { 74 throw new MathIllegalArgumentException(LocalizedStatFormats.NOT_SUPPORTED_NAN_STRATEGY, 75 NaNStrategy.REMOVED); 76 } 77 78 data = null; 79 this.rankingAlgorithm = rankingAlgorithm; 80 rankCorrelation = null; 81 } 82 83 /** 84 * Create a SpearmansCorrelation from the given data matrix. 85 * 86 * @param dataMatrix matrix of data with columns representing 87 * variables to correlate 88 */ 89 public SpearmansCorrelation(final RealMatrix dataMatrix) { 90 this(dataMatrix, new NaturalRanking()); 91 } 92 93 /** 94 * Create a SpearmansCorrelation with the given input data matrix 95 * and ranking algorithm. 96 * 97 * @param dataMatrix matrix of data with columns representing 98 * variables to correlate 99 * @param rankingAlgorithm ranking algorithm 100 * @throws MathIllegalArgumentException if the provided {@link RankingAlgorithm} is of 101 * type {@link NaturalRanking} and uses a {@link NaNStrategy#REMOVED} strategy 102 */ 103 public SpearmansCorrelation(final RealMatrix dataMatrix, final RankingAlgorithm rankingAlgorithm) 104 throws MathIllegalArgumentException { 105 106 if (rankingAlgorithm instanceof NaturalRanking && 107 NaNStrategy.REMOVED == ((NaturalRanking) rankingAlgorithm).getNanStrategy()) { 108 throw new MathIllegalArgumentException(LocalizedStatFormats.NOT_SUPPORTED_NAN_STRATEGY, 109 NaNStrategy.REMOVED); 110 } 111 112 this.rankingAlgorithm = rankingAlgorithm; 113 this.data = rankTransform(dataMatrix); 114 rankCorrelation = new PearsonsCorrelation(data); 115 } 116 117 /** 118 * Calculate the Spearman Rank Correlation Matrix. 119 * 120 * @return Spearman Rank Correlation Matrix 121 * @throws NullPointerException if this instance was created with no data 122 */ 123 public RealMatrix getCorrelationMatrix() { 124 return rankCorrelation.getCorrelationMatrix(); 125 } 126 127 /** 128 * Returns a {@link PearsonsCorrelation} instance constructed from the 129 * ranked input data. That is, 130 * <code>new SpearmansCorrelation(matrix).getRankCorrelation()</code> 131 * is equivalent to 132 * <code>new PearsonsCorrelation(rankTransform(matrix))</code> where 133 * <code>rankTransform(matrix)</code> is the result of applying the 134 * configured <code>RankingAlgorithm</code> to each of the columns of 135 * <code>matrix.</code> 136 * 137 * <p>Returns null if this instance was created with no data.</p> 138 * 139 * @return PearsonsCorrelation among ranked column data 140 */ 141 public PearsonsCorrelation getRankCorrelation() { 142 return rankCorrelation; 143 } 144 145 /** 146 * Computes the Spearman's rank correlation matrix for the columns of the 147 * input matrix. 148 * 149 * @param matrix matrix with columns representing variables to correlate 150 * @return correlation matrix 151 */ 152 public RealMatrix computeCorrelationMatrix(final RealMatrix matrix) { 153 final RealMatrix matrixCopy = rankTransform(matrix); 154 return new PearsonsCorrelation().computeCorrelationMatrix(matrixCopy); 155 } 156 157 /** 158 * Computes the Spearman's rank correlation matrix for the columns of the 159 * input rectangular array. The columns of the array represent values 160 * of variables to be correlated. 161 * 162 * @param matrix matrix with columns representing variables to correlate 163 * @return correlation matrix 164 */ 165 public RealMatrix computeCorrelationMatrix(final double[][] matrix) { 166 return computeCorrelationMatrix(new BlockRealMatrix(matrix)); 167 } 168 169 /** 170 * Computes the Spearman's rank correlation coefficient between the two arrays. 171 * 172 * @param xArray first data array 173 * @param yArray second data array 174 * @return Returns Spearman's rank correlation coefficient for the two arrays 175 * @throws MathIllegalArgumentException if the arrays lengths do not match 176 * @throws MathIllegalArgumentException if the array length is less than 2 177 */ 178 public double correlation(final double[] xArray, final double[] yArray) { 179 MathArrays.checkEqualLength(xArray, yArray); 180 if (xArray.length < 2) { 181 throw new MathIllegalArgumentException(LocalizedCoreFormats.INSUFFICIENT_DIMENSION, 182 xArray.length, 2); 183 } 184 185 return new PearsonsCorrelation().correlation(rankingAlgorithm.rank(xArray), 186 rankingAlgorithm.rank(yArray)); 187 } 188 189 /** 190 * Applies rank transform to each of the columns of <code>matrix</code> 191 * using the current <code>rankingAlgorithm</code>. 192 * 193 * @param matrix matrix to transform 194 * @return a rank-transformed matrix 195 */ 196 private RealMatrix rankTransform(final RealMatrix matrix) { 197 RealMatrix transformed = matrix.copy(); 198 for (int i = 0; i < transformed.getColumnDimension(); i++) { 199 transformed.setColumn(i, rankingAlgorithm.rank(transformed.getColumn(i))); 200 } 201 202 return transformed; 203 } 204 205 }