package hep.aida.util;
import hep.aida.IAnalysisFactory;
import hep.aida.ICloud1D;
import hep.aida.IHistogram1D;
/**
*
* @author The FreeHEP team @ SLAC.
* @version $Id: HistUtils.java 13360 2007-10-02 23:13:06Z serbo $
*/
public class HistUtils {
/**
* Test the distribution of h1 and h2 using the Kolmogorov algorithm.
* The result is between 0 and 1; 1 is for identical histograms.
*
*/
public static double kolmogorovTest( ICloud1D c1, ICloud1D c2) {
return kolmogorovTest(c1, c2, 50);
}
public static double kolmogorovTest( ICloud1D c1, ICloud1D c2, int nBins) {
double result = 0;
if (!c1.isConverted() && !c2.isConverted()) {
double[] bins1 = null;
double[] bins2 = null;
double xMin = c1.lowerEdge();
double xMax = c1.upperEdge();
if (c2.lowerEdge() < xMin) xMin = c2.lowerEdge();
if (c2.upperEdge() > xMax) xMax = c2.upperEdge();
bins1 = getBins(c1, nBins, xMin, xMax);
bins2 = getBins(c1, nBins, xMin, xMax);
result = kolmogorovTest(bins1, bins2);
} else if (c1.isConverted() && !c2.isConverted()) {
IHistogram1D h1 = c1.histogram();
IHistogram1D h2 = IAnalysisFactory.create().createHistogramFactory(null).createHistogram1D(c2.title(), h1.axis().bins(), h1.axis().lowerEdge(), h1.axis().upperEdge());
result = kolmogorovTest(h1, h2);
} else if (!c1.isConverted() && c2.isConverted()) {
IHistogram1D h2 = c2.histogram();
IHistogram1D h1 = IAnalysisFactory.create().createHistogramFactory(null).createHistogram1D(c1.title(), h2.axis().bins(), h2.axis().lowerEdge(), h2.axis().upperEdge());
result = kolmogorovTest(h1, h2);
} else if (c1.isConverted() && c2.isConverted()) {
result = kolmogorovTest(c1.histogram(), c2.histogram());
}
return result;
}
// Use for unconverted ICloud1D only
private static double[] getBins(ICloud1D c, int nBins, double xMin, double xMax) {
if ( c.isConverted() )
throw new IllegalArgumentException("ICloud1D must be not converted!");
double[] bins = new double[nBins];
double x = 0;
double binWidth = (xMax - xMin)/nBins;
for (int i=0; i<nBins; i++) {
x = c.value(i);
if (x >= xMin && x< xMax) {
int index = (int) Math.floor((x - xMin)/binWidth);
bins[index] += c.weight(i);
}
}
return bins;
}
public static double kolmogorovTest( IHistogram1D h1, IHistogram1D h2 ) {
if ( h1 == null || h2 == null )
throw new IllegalArgumentException("Null Histogram!");
if ( ! h1.axis().equals( h2.axis() ) )
throw new IllegalArgumentException("The two histograms must have the same binning!");
int nBins = h1.axis().bins();
double[] bins1 = new double[nBins];
double[] bins2 = new double[nBins];
for ( int i = 0; i < nBins; i++ ) {
bins1[i] = h1.binHeight(i);
bins2[i] = h2.binHeight(i);
}
return kolmogorovTest(bins1, bins2);
}
/**
* Test the distribution of h1 and h2 using the Kolmogorov algorithm.
* The result is between 0 and 1; 1 is for identical histograms.
*
*/
public static double kolmogorovTest( double[] bins1, double[] bins2 ) {
if ( bins1.length != bins2.length )
throw new IllegalArgumentException("The two histograms must have the same number of bins!");
int nBins = bins1.length;
double sumOfHeights1 = 0;
double sumOfHeights2 = 0;
for ( int i = 0; i < nBins; i++ ) {
sumOfHeights1 += bins1[i];
sumOfHeights2 += bins2[i];
}
if ( sumOfHeights1 == 0 || sumOfHeights2 == 0 )
throw new IllegalArgumentException("The histograms cannot have zero integral!");
double norm1 = 1./sumOfHeights1;
double norm2 = 1./sumOfHeights2;
// Find largest difference for Kolmogorov Test
double diff = 0, normSum1 = 0, normSum2 = 0;
for ( int i = 0; i < nBins; i++ ) {
normSum1 += norm1*bins1[i];
normSum2 += norm2*bins2[i];
double tmpDiff = Math.abs( normSum1 - normSum2 );
if ( tmpDiff > diff ) diff = tmpDiff;
}
double prob = diff*Math.sqrt(sumOfHeights1*sumOfHeights2/(sumOfHeights1+sumOfHeights2));
double p = 0;
if ( prob < 0.2 ) return 1;
if ( prob > 1 ) {
// jf2[j] = -2* j**2
double[] fj2 = {-2. , -8. , -18. , -32. , -50.};
double s = -2;
double p2 = prob*prob;
for ( int i = 0; i < 5; i++ ) {
s *= -1;
double c = fj2[i] *p2;
if (c < -100) return p;
p += s*Math.exp(c);
}
return p;
}
double[] cons = { -1.233700550136 , -11.10330496 , -30.84251376};
double sqr2pi = Math.sqrt( 2*Math.PI );
double zinv = 1./prob;
double a = sqr2pi*zinv;
double zinv2 = zinv*zinv;
double arg;
for ( int i =0; i < 3; i++) {
arg = cons[i]*zinv2;
if (arg < -30) continue;
p += Math.exp(arg);
}
p = 1 - a*p;
return p;
}
}