/**
 * Copyright (C) 2013-2017 Vasilis Vryniotis 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.datumbox.framework.core.statistics.descriptivestatistics;

import com.datumbox.framework.common.dataobjects.*;

import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

/**
 * This class provides several methods to estimate Descriptive Statistics about
 * a particular list of values.
 * 
 * @author Vasilis Vryniotis 
 */
public class Descriptives {

    /**
     * Returns the number of not-null items in the iteratable.
     *
     * @param it
     * @return
     */
    public static int count(Iterable it) {
        int n = 0;

        for(Object v: it) {
            if(v != null) {
                ++n;
            }
        }

        return n;
    }

    /**
     * Returns the sum of a Collection
     * 
     * @param flatDataCollection
     * @return
     */
    public static double sum(FlatDataCollection flatDataCollection) {
        double sum = 0.0;
        
        Iterator it = flatDataCollection.iteratorDouble();
        while(it.hasNext()) {
            Double value = it.next();
            if(value != null) {
                sum+= value;
            }
        }
        
        return sum;
    }
    
    /**
     * Calculates the simple mean
     * 
     * @param flatDataCollection
     * @return
     */
    public static double mean(FlatDataCollection flatDataCollection) {
        int n = 0;
        double mean = 0.0;
        Iterator it = flatDataCollection.iteratorDouble();
        while(it.hasNext()) {
            Double value = it.next();
            if(value != null) {
                ++n;
                mean += value;
            }
        }

        if(n==0) {
            throw new IllegalArgumentException("No not null values where found in the collection.");
        }
        mean /= n;
        
        return mean;
    }
    
    /**
     * Calculates Standard Error of Mean under SRS
     * 
     * @param flatDataCollection
     * @return 
     */
    public static double meanSE(FlatDataCollection flatDataCollection) {
        double std = std(flatDataCollection, true);
        double meanSE = std/Math.sqrt(count(flatDataCollection));
        
        return meanSE;
    }
    
    /**
     * Calculates the median.
     * 
     * @param flatDataCollection
     * @return
     */
    public static double median(FlatDataCollection flatDataCollection) {
        double[] doubleArray = flatDataCollection.stream().filter(x -> x!=null).mapToDouble(TypeInference::toDouble).toArray();
        int n = doubleArray.length;
        if(n==0) {
            throw new IllegalArgumentException("The provided collection can't be empty.");
        }
        Arrays.sort(doubleArray);
        
        double median;
        if(n%2==0) {
            median = (doubleArray[n/2 - 1] + doubleArray[n/2])/2.0;
        }
        else {
            median = doubleArray[n/2];
        }
        
        return median;
    }
    
    /**
     * Calculates Minimum.
     * 
     * @param flatDataCollection
     * @return
     */
    public static double min(FlatDataCollection flatDataCollection) {
        double min=Double.POSITIVE_INFINITY;
        
        Iterator it = flatDataCollection.iteratorDouble();
        while(it.hasNext()) {
            Double v = it.next();
            if(v != null && min > v) {
                min=v;
            }
        } 
        
        return min;
    }
    
    /**
     * Calculates Maximum.
     * 
     * @param flatDataCollection
     * @return
     */
    public static double max(FlatDataCollection flatDataCollection) {
        double max=Double.NEGATIVE_INFINITY;
            
        Iterator it = flatDataCollection.iteratorDouble();
        while(it.hasNext()) {
            Double v = it.next();
            if(v != null && max < v) {
                max=v;
            }
        }          
        
        return max;
    }

    /**
     * Calculates Minimum absolute value.
     *
     * @param flatDataCollection
     * @return
     */
    public static double minAbsolute(FlatDataCollection flatDataCollection) {
        double minAbs=Double.POSITIVE_INFINITY;

        Iterator it = flatDataCollection.iteratorDouble();
        while(it.hasNext()) {
            Double v = it.next();
            if(v != null) {
                minAbs= Math.min(minAbs, Math.abs(v));
            }
        }

        return minAbs;
    }

    /**
     * Calculates Maximum absolute value.
     *
     * @param flatDataCollection
     * @return
     */
    public static double maxAbsolute(FlatDataCollection flatDataCollection) {
        double maxAbs=0.0;

        Iterator it = flatDataCollection.iteratorDouble();
        while(it.hasNext()) {
            Double v = it.next();
            if(v != null) {
                maxAbs= Math.max(maxAbs, Math.abs(v));
            }
        }

        return maxAbs;
    }
    
    /**
     * Calculates Range
     * 
     * @param flatDataCollection
     * @return 
     */
    public static double range(FlatDataCollection flatDataCollection) {
        double range=max(flatDataCollection)-min(flatDataCollection);
        
        return range;
    }
    
    /**
     * Calculates Geometric Mean
     * 
     * @param flatDataCollection
     * @return
     */
    public static double geometricMean(FlatDataCollection flatDataCollection) {
	    int n = 0;
        double geometricMean = 0.0;

        Iterator it = flatDataCollection.iteratorDouble();
        while(it.hasNext()) {
            Double v = it.next();
            if(v != null) {
                if(v <= 0.0) {
                    throw new IllegalArgumentException("Negative or zero values are not allowed.");
                }
                ++n;
                geometricMean+= Math.log(v);
            }
        }
        
        geometricMean= Math.exp(geometricMean/n);
        
        return geometricMean;
    }
    
    /**
     * Calculates Harmonic Mean
     * 
     * @param flatDataCollection
     * @return
     */
    public static double harmonicMean(FlatDataCollection flatDataCollection) {
	    int n = 0;
        double harmonicMean = 0.0;
        
        Iterator it = flatDataCollection.iteratorDouble();
        while(it.hasNext()) {
            Double v = it.next();
            if(v!=null) {
                ++n;
                harmonicMean+=1.0/v;
            }
        }
        
        harmonicMean=n/harmonicMean;
        
        return harmonicMean;
    }
    
    /**
     * Calculates the Variance 
     * 
     * @param flatDataCollection
     * @param isSample
     * @return
     */
    public static double variance(FlatDataCollection flatDataCollection, boolean isSample) {
        /* Uses the formal Variance = E(X^2) - mean^2 */
        int n = 0;
        double mean = 0.0;
        double squaredMean = 0.0;
        Iterator it = flatDataCollection.iteratorDouble();
        while(it.hasNext()) {
            Double v = it.next();
            if(v != null) {
                ++n;
                mean+=v;
                squaredMean+=v*v;
            }
        }

        if(n<=1) {
            throw new IllegalArgumentException("The provided collection must have more than 1 elements.");
        }
        
        mean/=n;
        squaredMean/=n;
        
        double variance = squaredMean - mean*mean;
        
        /* Unbiased for sample */
        if(isSample) {
            variance*=n/(n-1.0);
        }
        
        return variance;
    }
    
    /**
     * Calculates the Standard Deviation
     * 
     * @param flatDataCollection
     * @param isSample
     * @return 
     */
    public static double std(FlatDataCollection flatDataCollection, boolean isSample) {
        double variance = variance(flatDataCollection, isSample);
        double std = Math.sqrt(variance);
        
        return std;
    }
    
    /**
     * Calculates Coefficient of variation
     * 
     * @param std
     * @param mean
     * @return 
     */
    public static double cv(double std, double mean) {
        if(mean==0) {
            return Double.POSITIVE_INFINITY;
        }
        
        double cv = std/mean;
        
        return cv;
    }
    
    /**
     * Calculates Moment R if the mean is not known.
     * 
     * @param flatDataCollection
     * @param r
     * @return 
     */
    public static double moment(FlatDataCollection flatDataCollection, int r) {
        double mean = mean(flatDataCollection);
        return moment(flatDataCollection, r, mean);
    }
    
    /**
     * Calculates Moment R if the mean is known.
     * 
     * @param flatDataCollection
     * @param r
     * @param mean
     * @return
     */
    public static double moment(FlatDataCollection flatDataCollection, int r, double mean) {
        int n = 0;
        double moment=0.0;
        
        Iterator it = flatDataCollection.iteratorDouble();
        while(it.hasNext()) {
            Double v = it.next();
            if(v != null) {
                ++n;
                moment+=Math.pow(v-mean, r);
            }
        }

        if(n<=1) {
            throw new IllegalArgumentException("The provided collection must have more than 1 elements.");
        }
        
        moment/=n;
        
        return moment;
    }
    
    /**
     * Calculates Kurtosis. Uses a formula similar to SPSS as suggested in their documentation (local help)
     * 
     * @param flatDataCollection
     * @return
     */
    public static double kurtosis(FlatDataCollection flatDataCollection) {
	    int n = count(flatDataCollection);
        if(n<=3) {
            throw new IllegalArgumentException("The provided collection must have more than 3 elements.");
        }
        
        double mean = mean(flatDataCollection);
        
        double m4 = moment(flatDataCollection, 4, mean);
        double m2 = moment(flatDataCollection, 2, mean);        

        /*
        g2=m4/(m2*m2);

        kurtosis=((n-1.0)/((n-2.0)*(n-3.0)))*((n+1.0)*g2 +6.0);
        //kurtosis=g2/(n-1.0)-3.0;
        //kurtosis=g2;
        */
        
        double s = (n/(n-1.0))*m2;
        
        double Mcapital4=n*m4;
        double Mcapital2=n*m2;
        double kurtosis=(n*(n+1.0)*Mcapital4-3*Mcapital2*Mcapital2*(n-1.0))/((n-1.0)*(n-2.0)*(n-3.0)*s*s);
        
        return kurtosis;
    }
    
    /**
     * Calculates Standard Error of Kurtosis. Uses a formula similar to SPSS as 
     * suggested by http://suite101.com/article/kurtosis-and-how-it-is-calculated-by-statistics-software-packages-a235641
     * 
     * @param flatDataCollection
     * @return
     */
    public static double kurtosisSE(FlatDataCollection flatDataCollection) {
	    int n = count(flatDataCollection);
        if(n<=3) {
            throw new IllegalArgumentException("The provided collection must have more than 3 elements.");
        }
        
        double kurtosisSE = Math.sqrt(24.0/n);
        
        return kurtosisSE;
    }
    
    /**
     * Calculates Skewness. Uses a formula as suggested by http://en.wikipedia.org/wiki/Skewness
     * 
     * @param flatDataCollection
     * @return
     */
    public static double skewness(FlatDataCollection flatDataCollection) {
	    int n = count(flatDataCollection);
        if(n<=1) {
            throw new IllegalArgumentException("The provided collection must have more than 1 elements.");
        }
        
        double mean = mean(flatDataCollection);
        
        double m3 = moment(flatDataCollection, 3, mean);
        double variance = variance(flatDataCollection, false);        
    
        double skewness=m3/Math.pow(variance, 3.0/2.0);
        
        return skewness;
    }
    
    /**
     * Calculates Standard Error of Skweness. Uses a formula as suggested by 
     * http://en.wikipedia.org/wiki/Skewness
     * 
     * @param flatDataCollection
     * @return
     */
    public static double skewnessSE(FlatDataCollection flatDataCollection) {
	    int n = count(flatDataCollection);
        if(n<=2) {
            throw new IllegalArgumentException("The provided collection must have more than 2 elements.");
        }
        
        double skewnessSE=Math.sqrt((6.0*n*(n-1.0))/((n-2.0)*(n+1.0)*(n+3.0)));
        
        return skewnessSE;
    }
    
    /**
     * Calculates the percentiles given a number of cutPoints
     * 
     * @param flatDataCollection
     * @param cutPoints
     * @return
     */
    public static AssociativeArray percentiles(FlatDataCollection flatDataCollection, int cutPoints) {
        double[] doubleArray = flatDataCollection.stream().filter(x -> x!=null).mapToDouble(TypeInference::toDouble).toArray();
        int n = doubleArray.length;
        if(n<=0 || cutPoints<=0 || n iteratorX = flatDataCollectionX.iteratorDouble();
        Iterator iteratorY = flatDataCollectionY.iteratorDouble();
        while (iteratorX.hasNext() && iteratorY.hasNext()) {
            covariance+=(iteratorX.next()-meanX)*
                        (iteratorY.next()-meanY);
        }
        
        if(isSample) {
            covariance/=(n-1.0);
        }
        else {
            covariance/=n;
        }
        
        return covariance;
    }
    
    /**
     * Calculates the autocorrelation of a flatDataCollection for a predifined lag
     * 
     * @param flatDataList
     * @param lags
     * @return
     */
    public static double autocorrelation(FlatDataList flatDataList, int lags) {
        int n = count(flatDataList);
        if(n<=0 || lags<=0 || n entry : frequencies.entrySet()) {
            Object key = entry.getKey();
            int count = ((Number)entry.getValue()).intValue();
            if(maxCounter==count) {
                modeList.add(key);
            }
            else if(maxCounter entry : associativeArray.entrySet()) {
            Double value = TypeInference.toDouble(entry.getValue());
            associativeArray.put(entry.getKey(), value);

            sum += value;
        }
        
        if(sum!=0.0) {
            for(Map.Entry entry : associativeArray.entrySet()) {
                associativeArray.put(entry.getKey(), TypeInference.toDouble(entry.getValue())/sum);
            }
        }
    }
    
    /**
     * Normalizes the exponentials of provided associative array by using the 
     * log-sum-exp trick.
     * 
     * @param associativeArray 
     */
    public static void normalizeExp(AssociativeArray associativeArray) {
        double max = max(associativeArray.toFlatDataCollection());

        double sum = 0.0;
        //Prevents numeric underflow by subtracting the max. References: http://www.youtube.com/watch?v=-RVM21Voo7Q
        for(Map.Entry entry : associativeArray.entrySet()) {
            Double value = Math.exp(TypeInference.toDouble(entry.getValue())-max);
            associativeArray.put(entry.getKey(), value);

            sum += value;
        }
        
        if(sum!=0.0) {
            for(Map.Entry entry : associativeArray.entrySet()) {
                associativeArray.put(entry.getKey(), TypeInference.toDouble(entry.getValue())/sum);
            }
        }
    }
}

 

Ads help maintain this website.