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.math3.stat.correlation;
019    
020    import org.apache.commons.math3.exception.DimensionMismatchException;
021    import org.apache.commons.math3.exception.MathIllegalArgumentException;
022    import org.apache.commons.math3.exception.util.LocalizedFormats;
023    import org.apache.commons.math3.linear.BlockRealMatrix;
024    import org.apache.commons.math3.linear.RealMatrix;
025    import org.apache.commons.math3.stat.ranking.NaturalRanking;
026    import org.apache.commons.math3.stat.ranking.RankingAlgorithm;
027    
028    /**
029     * <p>Spearman's rank correlation. This implementation performs a rank
030     * transformation on the input data and then computes {@link PearsonsCorrelation}
031     * on the ranked data.</p>
032     *
033     * <p>By default, ranks are computed using {@link NaturalRanking} with default
034     * strategies for handling NaNs and ties in the data (NaNs maximal, ties averaged).
035     * The ranking algorithm can be set using a constructor argument.</p>
036     *
037     * @since 2.0
038     * @version $Id: SpearmansCorrelation.java 1422313 2012-12-15 18:53:41Z psteitz $
039     */
040    
041    public class SpearmansCorrelation {
042    
043        /** Input data */
044        private final RealMatrix data;
045    
046        /** Ranking algorithm  */
047        private final RankingAlgorithm rankingAlgorithm;
048    
049        /** Rank correlation */
050        private final PearsonsCorrelation rankCorrelation;
051    
052        /**
053         * Create a SpearmansCorrelation without data.
054         */
055        public SpearmansCorrelation() {
056            this(new NaturalRanking());
057        }
058    
059        /**
060         * Create a SpearmansCorrelation with the given ranking algorithm.
061         *
062         * @param rankingAlgorithm ranking algorithm
063         * @since 3.1
064         */
065        public SpearmansCorrelation(final RankingAlgorithm rankingAlgorithm) {
066            data = null;
067            this.rankingAlgorithm = rankingAlgorithm;
068            rankCorrelation = null;
069        }
070    
071        /**
072         * Create a SpearmansCorrelation from the given data matrix.
073         *
074         * @param dataMatrix matrix of data with columns representing
075         * variables to correlate
076         */
077        public SpearmansCorrelation(final RealMatrix dataMatrix) {
078            this(dataMatrix, new NaturalRanking());
079        }
080    
081        /**
082         * Create a SpearmansCorrelation with the given input data matrix
083         * and ranking algorithm.
084         *
085         * @param dataMatrix matrix of data with columns representing
086         * variables to correlate
087         * @param rankingAlgorithm ranking algorithm
088         */
089        public SpearmansCorrelation(final RealMatrix dataMatrix, final RankingAlgorithm rankingAlgorithm) {
090            this.data = dataMatrix.copy();
091            this.rankingAlgorithm = rankingAlgorithm;
092            rankTransform(data);
093            rankCorrelation = new PearsonsCorrelation(data);
094        }
095    
096        /**
097         * Calculate the Spearman Rank Correlation Matrix.
098         *
099         * @return Spearman Rank Correlation Matrix
100         */
101        public RealMatrix getCorrelationMatrix() {
102            return rankCorrelation.getCorrelationMatrix();
103        }
104    
105        /**
106         * Returns a {@link PearsonsCorrelation} instance constructed from the
107         * ranked input data. That is,
108         * <code>new SpearmansCorrelation(matrix).getRankCorrelation()</code>
109         * is equivalent to
110         * <code>new PearsonsCorrelation(rankTransform(matrix))</code> where
111         * <code>rankTransform(matrix)</code> is the result of applying the
112         * configured <code>RankingAlgorithm</code> to each of the columns of
113         * <code>matrix.</code>
114         *
115         * @return PearsonsCorrelation among ranked column data
116         */
117        public PearsonsCorrelation getRankCorrelation() {
118            return rankCorrelation;
119        }
120    
121        /**
122         * Computes the Spearman's rank correlation matrix for the columns of the
123         * input matrix.
124         *
125         * @param matrix matrix with columns representing variables to correlate
126         * @return correlation matrix
127         */
128        public RealMatrix computeCorrelationMatrix(RealMatrix matrix) {
129            RealMatrix matrixCopy = matrix.copy();
130            rankTransform(matrixCopy);
131            return new PearsonsCorrelation().computeCorrelationMatrix(matrixCopy);
132        }
133    
134        /**
135         * Computes the Spearman's rank correlation matrix for the columns of the
136         * input rectangular array.  The columns of the array represent values
137         * of variables to be correlated.
138         *
139         * @param matrix matrix with columns representing variables to correlate
140         * @return correlation matrix
141         */
142        public RealMatrix computeCorrelationMatrix(double[][] matrix) {
143           return computeCorrelationMatrix(new BlockRealMatrix(matrix));
144        }
145    
146        /**
147         * Computes the Spearman's rank correlation coefficient between the two arrays.
148         *
149         * @param xArray first data array
150         * @param yArray second data array
151         * @return Returns Spearman's rank correlation coefficient for the two arrays
152         * @throws DimensionMismatchException if the arrays lengths do not match
153         * @throws MathIllegalArgumentException if the array length is less than 2
154         */
155        public double correlation(final double[] xArray, final double[] yArray) {
156            if (xArray.length != yArray.length) {
157                throw new DimensionMismatchException(xArray.length, yArray.length);
158            } else if (xArray.length < 2) {
159                throw new MathIllegalArgumentException(LocalizedFormats.INSUFFICIENT_DIMENSION,
160                                                       xArray.length, 2);
161            } else {
162                return new PearsonsCorrelation().correlation(rankingAlgorithm.rank(xArray),
163                        rankingAlgorithm.rank(yArray));
164            }
165        }
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         */
173        private void rankTransform(RealMatrix matrix) {
174            for (int i = 0; i < matrix.getColumnDimension(); i++) {
175                matrix.setColumn(i, rankingAlgorithm.rank(matrix.getColumn(i)));
176            }
177        }
178    }