001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.math.stat.correlation;
019
020 import org.apache.commons.math.MathRuntimeException;
021 import org.apache.commons.math.linear.BlockRealMatrix;
022 import org.apache.commons.math.linear.RealMatrix;
023 import org.apache.commons.math.stat.ranking.NaturalRanking;
024 import org.apache.commons.math.stat.ranking.RankingAlgorithm;
025
026 /**
027 * <p>Spearman's rank correlation. This implementation performs a rank
028 * transformation on the input data and then computes {@link PearsonsCorrelation}
029 * on the ranked data.</p>
030 *
031 * <p>By default, ranks are computed using {@link NaturalRanking} with default
032 * strategies for handling NaNs and ties in the data (NaNs maximal, ties averaged).
033 * The ranking algorithm can be set using a constructor argument.</p>
034 *
035 * @since 2.0
036 * @version $Revision: 811685 $ $Date: 2009-09-05 13:36:48 -0400 (Sat, 05 Sep 2009) $
037 */
038
039 public class SpearmansCorrelation {
040
041 /** Input data */
042 private final RealMatrix data;
043
044 /** Ranking algorithm */
045 private final RankingAlgorithm rankingAlgorithm;
046
047 /** Rank correlation */
048 private final PearsonsCorrelation rankCorrelation;
049
050 /**
051 * Create a SpearmansCorrelation with the given input data matrix
052 * and ranking algorithm.
053 *
054 * @param dataMatrix matrix of data with columns representing
055 * variables to correlate
056 * @param rankingAlgorithm ranking algorithm
057 */
058 public SpearmansCorrelation(final RealMatrix dataMatrix, final RankingAlgorithm rankingAlgorithm) {
059 this.data = dataMatrix.copy();
060 this.rankingAlgorithm = rankingAlgorithm;
061 rankTransform(data);
062 rankCorrelation = new PearsonsCorrelation(data);
063 }
064
065 /**
066 * Create a SpearmansCorrelation from the given data matrix.
067 *
068 * @param dataMatrix matrix of data with columns representing
069 * variables to correlate
070 */
071 public SpearmansCorrelation(final RealMatrix dataMatrix) {
072 this(dataMatrix, new NaturalRanking());
073 }
074
075 /**
076 * Create a SpearmansCorrelation without data.
077 */
078 public SpearmansCorrelation() {
079 data = null;
080 this.rankingAlgorithm = new NaturalRanking();
081 rankCorrelation = null;
082 }
083
084 /**
085 * Calculate the Spearman Rank Correlation Matrix.
086 *
087 * @return Spearman Rank Correlation Matrix
088 */
089 public RealMatrix getCorrelationMatrix() {
090 return rankCorrelation.getCorrelationMatrix();
091 }
092
093 /**
094 * Returns a {@link PearsonsCorrelation} instance constructed from the
095 * ranked input data. That is,
096 * <code>new SpearmansCorrelation(matrix).getRankCorrelation()</code>
097 * is equivalent to
098 * <code>new PearsonsCorrelation(rankTransform(matrix))</code> where
099 * <code>rankTransform(matrix)</code> is the result of applying the
100 * configured <code>RankingAlgorithm</code> to each of the columns of
101 * <code>matrix.</code>
102 *
103 * @return PearsonsCorrelation among ranked column data
104 */
105 public PearsonsCorrelation getRankCorrelation() {
106 return rankCorrelation;
107 }
108
109 /**
110 * Computes the Spearman's rank correlation matrix for the columns of the
111 * input matrix.
112 *
113 * @param matrix matrix with columns representing variables to correlate
114 * @return correlation matrix
115 */
116 public RealMatrix computeCorrelationMatrix(RealMatrix matrix) {
117 RealMatrix matrixCopy = matrix.copy();
118 rankTransform(matrixCopy);
119 return new PearsonsCorrelation().computeCorrelationMatrix(matrixCopy);
120 }
121
122 /**
123 * Computes the Spearman's rank correlation matrix for the columns of the
124 * input rectangular array. The columns of the array represent values
125 * of variables to be correlated.
126 *
127 * @param matrix matrix with columns representing variables to correlate
128 * @return correlation matrix
129 */
130 public RealMatrix computeCorrelationMatrix(double[][] matrix) {
131 return computeCorrelationMatrix(new BlockRealMatrix(matrix));
132 }
133
134 /**
135 * Computes the Spearman's rank correlation coefficient between the two arrays.
136 *
137 * </p>Throws IllegalArgumentException if the arrays do not have the same length
138 * or their common length is less than 2</p>
139 *
140 * @param xArray first data array
141 * @param yArray second data array
142 * @return Returns Spearman's rank correlation coefficient for the two arrays
143 * @throws IllegalArgumentException if the arrays lengths do not match or
144 * there is insufficient data
145 */
146 public double correlation(final double[] xArray, final double[] yArray)
147 throws IllegalArgumentException {
148 if (xArray.length == yArray.length && xArray.length > 1) {
149 return new PearsonsCorrelation().correlation(rankingAlgorithm.rank(xArray),
150 rankingAlgorithm.rank(yArray));
151 }
152 else {
153 throw MathRuntimeException.createIllegalArgumentException(
154 "invalid array dimensions. xArray has size {0}; yArray has {1} elements",
155 xArray.length, yArray.length);
156 }
157 }
158
159 /**
160 * Applies rank transform to each of the columns of <code>matrix</code>
161 * using the current <code>rankingAlgorithm</code>
162 *
163 * @param matrix matrix to transform
164 */
165 private void rankTransform(RealMatrix matrix) {
166 for (int i = 0; i < matrix.getColumnDimension(); i++) {
167 matrix.setColumn(i, rankingAlgorithm.rank(matrix.getColumn(i)));
168 }
169 }
170 }