/*
Copyright (C) 2008 Praneet Tiwari
Copyright (C) 2010 Richard Gomes
This source code is release under the BSD License.
This file is part of JQuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://jquantlib.org/
JQuantLib is free software: you can redistribute it and/or modify it
under the terms of the JQuantLib license. You should have received a
copy of the license along with this program; if not, please email
. The license is also available online at
.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
JQuantLib is based on QuantLib. http://quantlib.org/
When applicable, the original copyright notice follows this notice.
*/
/*
Copyright (C) 2003 Ferdinando Ametrano
Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/
QuantLib is free software: you can redistribute it and/or modify it
under the terms of the QuantLib license. You should have received a
copy of the license along with this program; if not, please email
. The license is also available online at
.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
*/
package org.jquantlib.math.statistics;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jquantlib.QL;
import org.jquantlib.lang.annotation.QualityAssurance;
import org.jquantlib.lang.annotation.QualityAssurance.Quality;
import org.jquantlib.lang.annotation.QualityAssurance.Version;
import org.jquantlib.math.Constants;
import org.jquantlib.math.Ops;
import org.jquantlib.math.functions.Bind2nd;
import org.jquantlib.math.functions.ComposedFunction;
import org.jquantlib.math.functions.Cube;
import org.jquantlib.math.functions.Everywhere;
import org.jquantlib.math.functions.Fourth;
import org.jquantlib.math.functions.Identity;
import org.jquantlib.math.functions.Minus;
import org.jquantlib.math.functions.Square;
import org.jquantlib.math.matrixutilities.Array;
import org.jquantlib.util.ComparablePair;
import org.jquantlib.util.Pair;
/**
* Statistics tool
*
* This class accumulates a set of data and returns their
* statistics (e.g: mean, variance, skewness, kurtosis,
* error estimation, percentile, etc.) based on the empirical
* distribution (no gaussian assumption)
*
* It doesn't suffer the numerical instability problem of
* IncrementalStatistics. The downside is that it stores all
* samples, thus increasing the memory requirements.
*
* @author Praneet Tiwari
* @author Richard Gomes
*/
@QualityAssurance(quality = Quality.Q4_UNIT, reviewers = { "Richard Gomes" }, version = Version.V097)
public abstract class GeneralStatistics {
private List> samples;
private boolean sorted;
private static final String EMPTY_SAMPLE_SET = "empty sample set";
private static final String NEGATIVE_WEIGHT_NOT_ALLOWED = "negative weight not allowed";
private static final String INCOMPATIBLE_ARRAY_SIZES = "incompatible array sizes";
public GeneralStatistics() {
reset();
}
//
// public methods
//
/**
* adds a sequence of data to the set, with default weight
*/
public void addSequence(final double[] datum) {
for (int i=0; i>();
sorted = true;
}
/**
* sort the data set in increasing order
*/
public void sort() /*@ReadOnly*/ {
if (!sorted) {
Collections.sort(samples);
sorted = true;
}
}
public /*@Size*/ int samples() /*@ReadOnly*/ {
return samples.size();
}
public List> data() /*@ReadOnly*/ {
return Collections.unmodifiableList(samples);
}
public /*@Real*/ double weightSum() /*@ReadOnly*/ {
/*@Real*/ double result = 0.0;
for (final ComparablePair it : samples) {
result += it.second();
}
return result;
}
public /*@Real*/ double mean() /*@ReadOnly*/ {
/*@Size*/ final int N = samples();
QL.require(N != 0, "empty sample set");
// eat our own dog food
return expectationValue(new Identity(), new Everywhere()).first();
}
public /*@Real*/ double variance() /*@ReadOnly*/ {
/*@Size*/ final int N = samples();
QL.require(N > 1, "sample number <=1, unsufficient");
// Subtract the mean and square. Repeat on the whole range.
// Hopefully, the whole thing will be inlined in a single loop.
/*@Real*/ final double s2 = expectationValue(
new ComposedFunction(
new Square(), new Bind2nd(
new Minus(), mean())),
new Everywhere()).first();
return s2*N/(N-1.0);
}
public /*@Real*/ double standardDeviation() /*@ReadOnly*/ {
return Math.sqrt(variance());
}
public /*@Real*/ double errorEstimate() /*@ReadOnly*/ {
return Math.sqrt(variance()/samples());
}
public /*@Real*/ double skewness() /*@ReadOnly*/ {
/*@Size*/ final int N = samples();
QL.require(N > 2, "sample number <=2, unsufficient");
/*@Real*/ final double x = expectationValue(
new ComposedFunction(
new Cube(), new Bind2nd(
new Minus(), mean())),
new Everywhere()).first();
/*@Real*/ final double sigma = standardDeviation();
return (x/(sigma*sigma*sigma))*(N/(N-1.0))*(N/(N-2.0));
}
public /*@Real*/ double kurtosis() /*@ReadOnly*/ {
/*@Size*/ final int N = samples();
QL.require(N > 3, "sample number <=3, unsufficient");
/*@Real*/ final double x = expectationValue(
new ComposedFunction(
new Fourth(), new Bind2nd(
new Minus(), mean())),
new Everywhere()).first();
/*@Real*/ final double sigma2 = variance();
/*@Real*/ final double c1 = (N/(N-1.0)) * (N/(N-2.0)) * ((N+1.0)/(N-3.0));
/*@Real*/ final double c2 = 3.0 * ((N-1.0)/(N-2.0)) * ((N-1.0)/(N-3.0));
return c1*(x/(sigma2*sigma2))-c2;
}
public /*@Real*/ double min() /*@ReadOnly*/ {
QL.require(samples()>0, EMPTY_SAMPLE_SET);
return Collections.min(samples).first();
}
public /*@Real*/ double max() /*@ReadOnly*/ {
QL.require(samples()>0, EMPTY_SAMPLE_SET);
return Collections.max(samples).first();
}
public final Pair expectationValue(final Ops.DoubleOp f, final Ops.DoublePredicate inRange) {
double num = 0.0;
double den = 0.0;
int n = 0;
for (final ComparablePair element : samples) {
final double x = element.first();
final double w = element.second();
if (inRange.op(x)) {
num += f.op(x) * w;
den += w;
n++;
}
}
if (n == 0)
return new Pair(Constants.NULL_REAL, 0);
else
return new Pair(num/den, n);
}
public /*@Real*/ double percentile(final /*@Real*/ double percent) /*@ReadOnly*/ {
QL.require(percent > 0.0 && percent <= 1.0, "percentile must be in (0.0, 1.0]");
/*@Real*/ final double sampleWeight = weightSum();
QL.require(sampleWeight>0.0, "empty sample set");
sort();
int k = 0; final int l = samples.size()-1;
/* the sum of weight is non null, therefore there's at least one sample */
/*@Real*/ double integral = samples.get(k).second();
/*@Real*/ final double target = percent*sampleWeight;
while (integral < target && k != l) {
k++;
integral += samples.get(k).second();
}
return samples.get(k).first();
}
public /*@Real*/ double topPercentile(final /*@Real*/ double percent) /*@ReadOnly*/ {
QL.require(percent > 0.0 && percent <= 1.0, "percentile must be in (0.0, 1.0]");
/*@Real*/ final double sampleWeight = weightSum();
QL.require(sampleWeight > 0.0, "empty sample set");
sort();
int k = samples.size()-1; final int l = 0;
/* the sum of weight is non null, therefore there's at least one sample */
/*@Real*/ double integral = samples.get(k).second();
/*@Real*/ final double target = percent*sampleWeight;
while (integral < target && k != l) {
k--;
integral += samples.get(k).second();
}
return samples.get(k).first();
}
public void add(/*@Real*/ final double value) {
add(value, 1.0);
}
public void add(/*@Real*/ final double value, /*@Real*/ final double weight) {
QL.require(weight>=0.0, NEGATIVE_WEIGHT_NOT_ALLOWED);
samples.add(new ComparablePair(value, weight));
sorted = false;
}
}
Ads help maintain this website.