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