package com.compomics.util.math.statistics.distributions;
import com.compomics.util.math.BasicMathFunctions;
import com.compomics.util.math.statistics.Distribution;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import org.apache.commons.math.MathException;
import org.apache.commons.math.distribution.NormalDistributionImpl;
/**
* This class represents a normal distribution. A dirac distribution if the
* standard deviation is null.
*
* @author Marc Vaudel
*/
public class NormalDistribution implements Distribution {
/**
* Serial version number for backward compatibility.
*/
static final long serialVersionUID = -4944773548279233917L;
/**
* The apache normal distribution implementation.
*/
private NormalDistributionImpl normalDistributionImpl;
/**
* The mean of the distribution.
*/
private double mean;
/**
* The standard deviation of the distribution.
*/
private double std;
/**
* Constructor.
*
* @param mean the mean
* @param std the standard deviation
*/
public NormalDistribution(double mean, double std) {
this.mean = mean;
this.std = std;
if (std > 0) {
this.normalDistributionImpl = new NormalDistributionImpl(mean, std);
}
}
/**
* Returns the normal distribution corresponding to a given list of double
* calibrated on mean and standard deviation.
*
* @param input the input as a list of double
*
* @return the normal distribution calibrated on the mean and standard
* deviation
*/
public static NormalDistribution getNormalDistribution(ArrayList<Double> input) {
return new NormalDistribution(BasicMathFunctions.mean(input), BasicMathFunctions.std(input));
}
/**
* Returns the normal distribution corresponding to a given list of double
* calibrated on median and 34.1% percentile to median distance
*
* @param input the input as list of double
*
* @return a normal distribution calibrated on median and 34.1% percentile
* to median distance
*/
public static NormalDistribution getRobustNormalDistribution(ArrayList<Double> input) {
double std = (BasicMathFunctions.percentile(input, 0.841) - BasicMathFunctions.percentile(input, 0.159)) / 2;
return new NormalDistribution(BasicMathFunctions.median(input), std);
}
@Override
public Double getProbabilityAt(double x) {
if (std == 0) {
if (x == mean) {
return 1.0;
} else {
return 0.0;
}
}
double xNorm = (x - mean) / std;
double result = Math.pow(Math.E, -Math.pow(xNorm, 2) / 2) / (Math.pow(2 * Math.PI, 0.5)); //@TODO: verify that xNorm is not too big/small
return result;
}
@Override
public Double getMaxValueForProbability(double p) {
if (std == 0) {
return mean;
}
if (p >= 1) {
throw new IllegalArgumentException("Probability >= 1");
}
if (p <= 0) {
throw new IllegalArgumentException("Probability <= 0");
}
double x = Math.pow(-2 * Math.log(p * Math.pow(2 * Math.PI, 0.5)), 0.5); //@TODO: verify that p is not too small
return mean + std * x;
}
@Override
public Double getMinValueForProbability(double p) {
if (std == 0) {
return mean;
}
if (p >= 1) {
throw new IllegalArgumentException("Probability >= 1");
}
if (p <= 0) {
throw new IllegalArgumentException("Probability <= 0");
}
double x = Math.pow(-2 * Math.log(p * Math.pow(2 * Math.PI, 0.5)), 0.5); //@TODO: verify that p is not too small
return mean - std * x;
}
@Override
public Double getCumulativeProbabilityAt(double x) throws MathException {
if (std == 0) {
// Note: this is my personal interpretation of the cumulative distribution in this case
if (x < mean) {
return 0.0;
} else if (x == mean) {
return 0.5;
} else {
return 1.0;
}
}
return normalDistributionImpl.cumulativeProbability(x);
}
@Override
public Double getValueAtCumulativeProbability(double p) throws MathException {
if (std == 0) {
// Note: this is my personal interpretation of the cumulative distribution in this case
if (p < 0.5) {
return Double.NEGATIVE_INFINITY;
} else if (p == 0.5) {
return mean;
} else {
return Double.POSITIVE_INFINITY;
}
}
return normalDistributionImpl.inverseCumulativeProbability(p);
}
@Override
public Double getDescendingCumulativeProbabilityAt(double x) throws MathException {
if (std == 0) {
// Note: this is my personal interpretation of the cumulative distribution in this case
if (x > mean) {
return 0.0;
} else if (x == mean) {
return 0.5;
} else {
return 1.0;
}
}
return 1.0 - getCumulativeProbabilityAt(x);
}
@Override
public Double getSmallestCumulativeProbabilityAt(double x) throws MathException {
if (x > mean) {
return getDescendingCumulativeProbabilityAt(x);
} else {
getCumulativeProbabilityAt(x);
}
return 0.5;
}
@Override
public Double getValueAtDescendingCumulativeProbability(double p) throws MathException {
if (std == 0) {
// Note: this is my personal interpretation of the cumulative distribution in this case
if (p < 0.5) {
return Double.POSITIVE_INFINITY;
} else if (p == 0.5) {
return mean;
} else {
return Double.NEGATIVE_INFINITY;
}
}
return getValueAtCumulativeProbability(1 - p);
}
}