package gdsc.smlm.function;
import java.util.Arrays;
import org.apache.commons.math3.distribution.ChiSquaredDistribution;
import org.apache.commons.math3.special.Gamma;
/*-----------------------------------------------------------------------------
* GDSC SMLM Software
*
* Copyright (C) 2017 Alex Herbert
* Genome Damage and Stability Centre
* University of Sussex, UK
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*---------------------------------------------------------------------------*/
/**
* Computes probability values from the Chi-squared distribution
* <p>
* Note that Wilk's theorem states that the log-likelihood ratio asymptotically approaches a
* Chi-squared distribution with degrees of freedom equal to the difference in dimensionality of the two models
* (alternative and null).
*
* @see https://en.wikipedia.org/wiki/Likelihood-ratio_test#Wilks.27_theorem
*/
public class ChiSquaredDistributionTable
{
/**
* The p-value for computing the value using the cumulative probability (p=1-q)
*/
private final double p;
final double[] chiSquared;
/**
* Instantiates a new chi squared distribution table. Q is 1-p, with p the cumulative probability of the chi-squared
* distribution.
* <p>
* Setting q will cause {@link #getChiSquared(int)} to generate a value where the probability of a random
* chi-squared observation above that value is equal to q.
*
* @param q
* the q-value for significance (lower will produce a higher allowed chi-squared value)
* @param df
* the maximum degrees of freedom
*/
public ChiSquaredDistributionTable(double q, int df)
{
this.p = 1 - q;
chiSquared = new double[df + 1];
Arrays.fill(chiSquared, Double.NaN);
}
/**
* Get the q-value for significance.
* <p>
* Q refers to the probability of Chi squared being equal to or above a given value by chance.
*
* @return the q value
*/
public double getQValue()
{
return 1 - p;
}
/**
* Gets the chi squared value for the configured significance level and degrees of freedom.
* <p>
* This is the value of Chi squared where the chance of obtaining a value more extreme than this point is equal to
* the configured significance level (q-value).
*
* @param degreesOfFreedom
* the degrees of freedom
* @return the chi squared
*/
public double getChiSquared(int degreesOfFreedom)
{
if (degreesOfFreedom >= chiSquared.length)
throw new IllegalStateException("Maximum degrees of freedom = " + (chiSquared.length - 1));
if (Double.isNaN(chiSquared[degreesOfFreedom]))
chiSquared[degreesOfFreedom] = new ChiSquaredDistribution(null, degreesOfFreedom)
.inverseCumulativeProbability(p);
return chiSquared[degreesOfFreedom];
}
/**
* Checks if the value of chi-squared is significant at the configured significance level. Tests if Chi squared is
* below {@link #getChiSquared(int)}.
*
* @param chiSquared
* the chi squared
* @param degreesOfFreedom
* the degrees of freedom
* @return true, if is significant
*/
public boolean isSignificant(double chiSquared, int degreesOfFreedom)
{
double level = getChiSquared(degreesOfFreedom);
return chiSquared < level;
}
/**
* Gets the chi squared value for the configured significance level and degrees of freedom.
*
* @param p
* the p-value for significance (lower will produce a higher chi-squared value)
* @param degreesOfFreedom
* the degrees of freedom
* @return the chi squared
*/
public static double getChiSquared(double p, int degreesOfFreedom)
{
return new ChiSquaredDistribution(null, degreesOfFreedom).inverseCumulativeProbability(1 - p);
}
/**
* Compute the q-value of the Chi-squared distribution.
* <p>
* This is the probability of obtaining a value more extreme than this point by chance. A chi-squared value is
* significant if q is higher than the p-value for significance (e.g. 0.05).
*
* @param chiSquared
* the chi squared
* @param degreesOfFreedom
* the degrees of freedom
* @return the q-value
*/
public static double computeQValue(double chiSquared, int degreesOfFreedom)
{
if (chiSquared <= 0)
{
return 1;
}
else
{
return Gamma.regularizedGammaQ(degreesOfFreedom / 2.0, chiSquared / 2.0);
}
}
/**
* Compute the p-value of the Chi-squared distribution. This is the cumulative probability of the chi-squared
* distribution.
* <p>
* This is the probability of obtaining a value less extreme than this point by chance.
*
* @param chiSquared
* the chi squared
* @param degreesOfFreedom
* the degrees of freedom
* @return the p-value
*/
public static double computePValue(double chiSquared, int degreesOfFreedom)
{
if (chiSquared <= 0)
{
return 0;
}
else
{
return Gamma.regularizedGammaP(degreesOfFreedom / 2.0, chiSquared / 2.0);
}
}
}