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 }