    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    }