/*
Copyright (C) 2009 Ueli Hofstetter
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 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.matrixutilities.Array;
/**
* Statistics tool based on incremental accumulation
*
* It can accumulate a set of data and return statistics (e.g: mean,
* variance, skewness, kurtosis, error estimation, etc.)
*
* @warning high moments are numerically unstable for high
* average/standardDeviation ratios.
*
* @author Ueli Hofstetter
* @author Richard Gomes
*/
@QualityAssurance(quality = Quality.Q4_UNIT, reviewers = { "Richard Gomes" }, version = Version.V097)
public class IncrementalStatistics extends GenericRiskStatistics {
private static final String UNSUFFICIENT_SAMPLE_WEIGHT = "sampleWeight_=0, unsufficient";
private static final String UNSUFFICIENT_SAMPLE_NUMBER = "sample number <=1, unsufficient";
private static final String UNSUFFICIENT_SAMPLE_NUMBER_2 = "sample number <=2, unsufficient";
private static final String UNSUFFICIENT_SAMPLE_NUMBER_3 = "sample number <=3, unsufficient";
private static final String NEGATIVE_VARIANCE = "negative variance";
private static final String EMPTY_SAMPLE_SET = "empty sample set";
private static final String MAX_NUMBER_OF_SAMPLES_REACHED = "maximum number of samples reached";
private static final String INCOMPATIBLE_ARRAY_SIZES = "incompatible array sizes";
protected /*@Size*/ int sampleNumber_;
protected /*@Size*/ int downsideSampleNumber_;
protected /*@Real*/ double sampleWeight_, downsideSampleWeight_;
protected /*@Real*/ double sum_, quadraticSum_, downsideQuadraticSum_;
protected /*@Real*/ double cubicSum_, fourthPowerSum_;
protected /*@Real*/ double min_, max_;
public IncrementalStatistics() {
super();
reset();
}
//
// public methods
//
/**
* number of samples collected
*/
@Override
public /*@Size*/ int samples() /*@ReadOnly*/ {
return sampleNumber_;
}
/**
* sum of data weights
*/
@Override
public /*@Real*/ double weightSum() /*@ReadOnly*/ {
return sampleWeight_;
}
/**
* returns the mean, defined as
* {@latex[ \langle x \rangle = \frac{\sum w_i x_i}{\sum w_i}. }
*/
@Override
public /*@Real*/ double mean() /*@ReadOnly*/ {
QL.require(sampleWeight_>0.0, UNSUFFICIENT_SAMPLE_WEIGHT);
return sum_/sampleWeight_;
}
/**
* returns the variance, defined as
* {@latex[ \frac{N}{N-1} \left\langle \left(
* x-\langle x \rangle \right)^2 \right\rangle. }
*/
@Override
public /*@Real*/ double variance() /*@ReadOnly*/ {
QL.require(sampleWeight_>0.0, UNSUFFICIENT_SAMPLE_WEIGHT);
QL.require(sampleNumber_>1, UNSUFFICIENT_SAMPLE_NUMBER);
/*@Real*/ double m = mean();
/*@Real*/ double v = quadraticSum_/sampleWeight_;
v -= m*m;
v *= sampleNumber_/(sampleNumber_-1.0);
QL.ensure(v >= 0.0, NEGATIVE_VARIANCE);
return v;
}
/**
* returns the standard deviation {@latex$ \sigma }, defined as the
* square root of the variance.
*/
@Override
public /*@Real*/ double standardDeviation() /*@ReadOnly*/ {
return Math.sqrt(variance());
}
/**
* returns the error estimate {@latex$ \epsilon }, defined as the
* square root of the ratio of the variance to the number of
* samples.
*/
@Override
public /*@Real*/ double errorEstimate() /*@ReadOnly*/ {
/*@Real*/ double var = variance();
QL.require(samples() > 0, EMPTY_SAMPLE_SET);
return Math.sqrt(var/samples());
}
/**
* returns the downside deviation, defined as the
* square root of the downside variance.
*/
@Override
public /*@Real*/ double downsideDeviation() /*@ReadOnly*/ {
return Math.sqrt(downsideVariance());
}
/**
* returns the downside variance, defined as
* {@latex[ \frac{N}{N-1} \times \frac{ \sum_{i=1}^{N}
* \theta \times x_i^{2}}{ \sum_{i=1}^{N} w_i} },
* where {@latex$ \theta } = 0 if x > 0 and
* {@latex$ \theta } =1 if x <0
*/
@Override
public /*@Real*/ double downsideVariance() /*@ReadOnly*/ {
if (downsideSampleWeight_==0.0) {
QL.require(sampleWeight_>0.0, UNSUFFICIENT_SAMPLE_WEIGHT);
return 0.0;
}
QL.require(downsideSampleNumber_>1, "sample number below zero <=1, unsufficient");
return (downsideSampleNumber_/(downsideSampleNumber_-1.0))*
(downsideQuadraticSum_ /downsideSampleWeight_);
}
/**
* returns the skewness, defined as
* {@latex[ \frac{N^2}{(N-1)(N-2)} \frac{\left\langle \left(
* x-\langle x \rangle \right)^3 \right\rangle}{\sigma^3}. }
* The above evaluates to 0 for a Gaussian distribution.
*/
@Override
public /*@Real*/ double skewness() /*@ReadOnly*/ {
QL.require(sampleNumber_>2, UNSUFFICIENT_SAMPLE_NUMBER_2);
/*@Real*/ double s = standardDeviation();
if (s==0.0) return 0.0;
/*@Real*/ double m = mean();
/*@Real*/ double result = cubicSum_/sampleWeight_;
result -= 3.0*m*(quadraticSum_/sampleWeight_);
result += 2.0*m*m*m;
result /= s*s*s;
result *= sampleNumber_/(sampleNumber_-1.0);
result *= sampleNumber_/(sampleNumber_-2.0);
return result;
}
/**
* returns the excess kurtosis, defined as
* {@latex[ \frac{N^2(N+1)}{(N-1)(N-2)(N-3)}
* \frac{\left\langle \left(x-\langle x \rangle \right)^4
* \right\rangle}{\sigma^4} - \frac{3(N-1)^2}{(N-2)(N-3)}. }
* The above evaluates to 0 for a Gaussian distribution.
*/
@Override
public /*@Real*/ double kurtosis() /*@ReadOnly*/ {
QL.require(sampleNumber_>3, UNSUFFICIENT_SAMPLE_NUMBER_3);
/*@Real*/ double m = mean();
/*@Real*/ double v = variance();
/*@Real*/ double c = (sampleNumber_-1.0)/(sampleNumber_-2.0);
c *= (sampleNumber_-1.0)/(sampleNumber_-3.0);
c *= 3.0;
if (v==0) return c;
/*@Real*/ double result = fourthPowerSum_/sampleWeight_;
result -= 4.0*m*(cubicSum_/sampleWeight_);
result += 6.0*m*m*(quadraticSum_/sampleWeight_);
result -= 3.0*m*m*m*m;
result /= v*v;
result *= sampleNumber_/(sampleNumber_-1.0);
result *= sampleNumber_/(sampleNumber_-2.0);
result *= (sampleNumber_+1.0)/(sampleNumber_-3.0);
return result-c;
}
/**
* returns the minimum sample value
*/
@Override
public /*@Real*/ double min() /*@ReadOnly*/ {
QL.require(samples() > 0, EMPTY_SAMPLE_SET);
return min_;
}
/**
* returns the maximum sample value
*/
@Override
public /*@Real*/ double max() /*@ReadOnly*/ {
QL.require(samples() > 0, EMPTY_SAMPLE_SET);
return max_;
}
/**
* adds a sequence of data to the set, with default weight
*/
@Override
public void addSequence(final double[] datum) {
for (int i=0; i
* weights must be positive or null
*/
@Override
public void addSequence(final double[] datum, final double[] weights) {
QL.require(datum.length==weights.length, INCOMPATIBLE_ARRAY_SIZES);
for (int i=0; i
* weights must be positive or null
*/
@Override
public void addSequence(final Array datum, final Array weights) {
QL.require(datum.size()==weights.size(), INCOMPATIBLE_ARRAY_SIZES);
for (int i=0; i
* weight must be positive or null
*/
@Override
public void add(final /*@Real*/ double value) {
add(value, 1.0);
}
@Override
public void add(final /*@Real*/ double value, final /*@Real*/ double weight) {
QL.require(weight>=0.0, "negative weight not allowed");
/*@Size*/ int oldSamples = sampleNumber_;
sampleNumber_++;
QL.ensure(sampleNumber_ > oldSamples, MAX_NUMBER_OF_SAMPLES_REACHED);
sampleWeight_ += weight;
/*@Real*/ double temp = weight*value;
sum_ += temp;
temp *= value;
quadraticSum_ += temp;
if (value<0.0) {
downsideQuadraticSum_ += temp;
downsideSampleNumber_++;
downsideSampleWeight_ += weight;
}
temp *= value;
cubicSum_ += temp;
temp *= value;
fourthPowerSum_ += temp;
if (oldSamples == 0) {
min_ = max_ = value;
} else {
min_ = Math.min(value, min_);
max_ = Math.max(value, max_);
}
}
/**
* resets the data to a null set
*/
@Override
public void reset() {
min_ = Constants.DBL_MAX;
max_ = Constants.DBL_MIN;
sampleNumber_ = 0;
downsideSampleNumber_ = 0;
sampleWeight_ = 0.0;
downsideSampleWeight_ = 0.0;
sum_ = 0.0;
quadraticSum_ = 0.0;
downsideQuadraticSum_ = 0.0;
cubicSum_ = 0.0;
fourthPowerSum_ = 0.0;
}
}
Ads help maintain this website.