MaCh3  2.6.0
Reference Guide
Functions
StatisticalUtils.cpp File Reference
#include "Fitters/StatisticalUtils.h"
#include <numeric>
#include "Math/QuantFuncMathCore.h"
Include dependency graph for StatisticalUtils.cpp:

Go to the source code of this file.

Functions

_MaCh3_Safe_Include_Start_ _MaCh3_Safe_Include_End_ std::string GetJeffreysScale (const double BayesFactor)
 KS: Following H. Jeffreys [22]. More...
 
std::string GetDunneKaboth (const double BayesFactor)
 Convert a Bayes factor into an approximate particle-physics significance level using the Dunne–Kaboth scale. More...
 
double GetSigmaValue (const int sigma)
 KS: Convert sigma from normal distribution into percentage. More...
 
double GetBIC (const double llh, const int data, const int nPars)
 Get the Bayesian Information Criterion (BIC) or Schwarz information criterion (also SIC, SBC, SBIC) More...
 
double GetNeffective (const int N1, const int N2)
 KS: See 14.3.10 in Numerical Recipes in C [30]. More...
 
void CheckBonferoniCorrectedpValue (const std::vector< std::string > &SampleNameVec, const std::vector< double > &PValVec, const double Threshold)
 KS: For more see https://www.t2k.org/docs/technotes/429/TN429_v8#page=63. More...
 
double GetAndersonDarlingTestStat (const double CumulativeData, const double CumulativeMC, const double CumulativeJoint)
 
int GetNumberOfRuns (const std::vector< int > &GroupClasifier)
 KS: https://esjeevanand.uccollege.edu.in/wp-content/uploads/sites/114/2020/08/NON-PARAMTERIC-TEST-6.pdf. More...
 
double GetBetaParameter (const double data, const double mc, const double w2, const TestStatistic TestStat)
 KS: Calculate Beta parameter which will be different based on specified test statistic. More...
 
double GetSubOptimality (const std::vector< double > &EigenValues, const int TotalTarameters)
 Based on [31]. More...
 
void GetArithmetic (TH1D *const hist, double &Mean, double &Error)
 CW: Get Arithmetic mean from posterior. More...
 
void GetGaussian (TH1D *&hist, TF1 *gauss, double &Mean, double &Error)
 CW: Fit Gaussian to posterior. More...
 
void GetHPD (TH1D *const hist, double &Mean, double &Error, double &Error_p, double &Error_m, const double coverage)
 Get Highest Posterior Density (HPD) More...
 
void GetCredibleInterval (const std::unique_ptr< TH1D > &hist, std::unique_ptr< TH1D > &hpost_copy, const double coverage)
 KS: Get 1D histogram within credible interval, hpost_copy has to have the same binning, I don't do Copy() as this will lead to problems if this is used under multithreading. More...
 
void GetCredibleIntervalSig (const std::unique_ptr< TH1D > &hist, std::unique_ptr< TH1D > &hpost_copy, const bool CredibleInSigmas, const double coverage)
 KS: Get 1D histogram within credible interval, hpost_copy has to have the same binning, I don't do Copy() as this will lead to problems if this is used under multithreading. More...
 
void GetCredibleRegion (std::unique_ptr< TH2D > &hist2D, const double coverage)
 KS: Set 2D contour within some coverage. More...
 
void GetCredibleRegionSig (std::unique_ptr< TH2D > &hist2D, const bool CredibleInSigmas, const double coverage)
 KS: Set 2D contour within some coverage. More...
 
double GetIQR (TH1D *Hist)
 Interquartile Range (IQR) More...
 
double ComputeKLDivergence (const std::vector< double > &Data, const std::vector< double > &MC)
 Compute the Kullback-Leibler divergence between two TH2Poly histograms. More...
 
double ComputeKLDivergence (TH2Poly *DataPoly, TH2Poly *PolyMC)
 Compute the Kullback-Leibler divergence between two TH2Poly histograms. More...
 
double FisherCombinedPValue (const std::vector< double > &pvalues)
 KS: Combine p-values using Fisher's method. More...
 
void ThinningMCMC (const std::string &FilePath, const int ThinningCut)
 Thin MCMC Chain, to save space and maintain low autocorrelations. More...
 
double GetZScore (const double value, const double mean, const double stddev)
 Compute the Z-score for a given value. More...
 
double GetPValueFromZScore (const double zScore)
 Compute the P-value from a given Z-score. More...
 
double GetModeError (TH1D *hpost)
 Get the mode error from a TH1D. More...
 
void Get2DBayesianpValue (TH2D *Histogram)
 Calculates the 2D Bayesian p-value and generates a visualization. More...
 
std::unique_ptr< TH1D > GetDeltaChi2 (TH1D *posterior_probability_hist)
 Convert a posterior probability histogram into a \(\Delta\chi^2\) distribution. Using the likelihood-ratio definition: More...
 
void PassErrorToRatioPlot (TH1D *RatioHist, TH1D *Hist1, TH1D *DataHist)
 Propagate numerator uncertainties to a ratio histogram. More...
 
std::unique_ptr< TGraphAsymmErrors > PoissonGraph (const TH1D *hist, double cl)
 Create a TGraphAsymmErrors from a histogram using exact Poisson confidence intervals instead of symmetric sqrt(N) uncertainties. More...
 
std::unique_ptr< TGraphAsymmErrors > PoissonGraphScaled (const TH1D *hist, double scale, double cl)
 Create a TGraphAsymmErrors from a histogram using exact Poisson confidence intervals instead of symmetric sqrt(N) uncertainties. Assume bin width scaling. More...
 

Function Documentation

◆ CheckBonferoniCorrectedpValue()

void CheckBonferoniCorrectedpValue ( const std::vector< std::string > &  SampleNameVec,
const std::vector< double > &  PValVec,
const double  Threshold = 0.05 
)

KS: For more see https://www.t2k.org/docs/technotes/429/TN429_v8#page=63.

Parameters
SampleNameVecvector of sample names
PValVecpvalue for each sample
Thresholdpvalue accepted threshold, usually 5%

Definition at line 96 of file StatisticalUtils.cpp.

98  {
99 // ****************
100  MACH3LOG_INFO("");
101  if(SampleNameVec.size() != PValVec.size())
102  {
103  MACH3LOG_ERROR("Size of vectors do not match");
104  throw MaCh3Exception(__FILE__ , __LINE__ );
105  }
106  const size_t NumberOfStatisticalTests = SampleNameVec.size();
107  //KS: 0.05 or 5% is value used by T2K.
108  const double StatisticalSignificanceDown = Threshold / double(NumberOfStatisticalTests);
109  const double StatisticalSignificanceUp = 1 - StatisticalSignificanceDown;
110  MACH3LOG_INFO("Bonferroni-corrected statistical significance level: {:.2f}", StatisticalSignificanceDown);
111 
112  int Counter = 0;
113  for(unsigned int i = 0; i < SampleNameVec.size(); i++)
114  {
115  if( (PValVec[i] < 0.5 && PValVec[i] < StatisticalSignificanceDown) ) {
116  MACH3LOG_INFO("Sample {} indicates disagreement between the model predictions and the data", SampleNameVec[i]);
117  MACH3LOG_INFO("Bonferroni-corrected statistical significance level: {:.2f} p-value: {:.2f}", StatisticalSignificanceDown, PValVec[i]);
118  Counter++;
119  } else if( (PValVec[i] > 0.5 && PValVec[i] > StatisticalSignificanceUp) ) {
120  MACH3LOG_INFO("Sample {} indicates disagreement between the model predictions and the data", SampleNameVec[i]);
121  MACH3LOG_INFO("Bonferroni-corrected statistical significance level: {:.2f} p-value: {:.2f}", StatisticalSignificanceUp, PValVec[i]);
122  Counter++;
123  }
124  }
125  if(Counter == 0) {
126  MACH3LOG_INFO("Every sample passed Bonferroni-corrected statistical significance level test");
127  } else {
128  MACH3LOG_WARN("{} samples didn't pass Bonferroni-corrected statistical significance level test", Counter);
129  }
130  MACH3LOG_INFO("");
131 }
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
Custom exception class used throughout MaCh3.

◆ ComputeKLDivergence() [1/2]

double ComputeKLDivergence ( const std::vector< double > &  Data,
const std::vector< double > &  MC 
)

Compute the Kullback-Leibler divergence between two TH2Poly histograms.

Parameters
DataVector of data entries.
MCVector of MC entries.
Returns
The Kullback-Leibler divergence value. Returns 0 if the data or MC integral is zero.

Definition at line 469 of file StatisticalUtils.cpp.

470  {
471 // ********************
472  double klDivergence = 0.0;
473  double DataIntegral = std::accumulate(Data.begin(), Data.end(), 0.0);
474  double MCIntegral = std::accumulate(MC.begin(), MC.end(), 0.0);
475  for (size_t i = 0; i < Data.size(); ++i)
476  {
477  if (Data[i] > 0 && MC[i] > 0) {
478  klDivergence += Data[i] / DataIntegral *
479  std::log((Data[i] / DataIntegral) / ( MC[i] / MCIntegral));
480  }
481  }
482  return klDivergence;
483 }

◆ ComputeKLDivergence() [2/2]

double ComputeKLDivergence ( TH2Poly *  DataPoly,
TH2Poly *  PolyMC 
)

Compute the Kullback-Leibler divergence between two TH2Poly histograms.

Parameters
DataPolyPointer to the data histogram (TH2Poly).
PolyMCPointer to the Monte Carlo histogram (TH2Poly).
Returns
The Kullback-Leibler divergence value. Returns 0 if the data or MC integral is zero.

Definition at line 486 of file StatisticalUtils.cpp.

486  {
487 // *********************
488  int nBins = DataPoly->GetNumberOfBins();
489  std::vector<double> Data(nBins);
490  std::vector<double> MC(nBins);
491 
492  for (int i = 0; i < nBins; ++i) {
493  Data[i] = DataPoly->GetBinContent(i+1);
494  MC[i] = PolyMC->GetBinContent(i+1);
495  }
496 
497  return ComputeKLDivergence(Data, MC);
498 }
double ComputeKLDivergence(const std::vector< double > &Data, const std::vector< double > &MC)
Compute the Kullback-Leibler divergence between two TH2Poly histograms.

◆ FisherCombinedPValue()

double FisherCombinedPValue ( const std::vector< double > &  pvalues)

KS: Combine p-values using Fisher's method.

Parameters
pvaluesA vector of individual p-values to combine.
Returns
The combined p-value, representing the overall significance.

Definition at line 501 of file StatisticalUtils.cpp.

501  {
502 // ********************
503  double testStatistic = 0;
504  for(size_t i = 0; i < pvalues.size(); i++)
505  {
506  const double pval = std::max(0.00001, pvalues[i]);
507  testStatistic += -2.0 * std::log(pval);
508  }
509  // Degrees of freedom is twice the number of p-values
510  int degreesOfFreedom = int(2 * pvalues.size());
511  double pValue = TMath::Prob(testStatistic, degreesOfFreedom);
512 
513  return pValue;
514 }

◆ Get2DBayesianpValue()

void Get2DBayesianpValue ( TH2D *  Histogram)

Calculates the 2D Bayesian p-value and generates a visualization.

Parameters
HistogramA pointer to a TH2D histogram object. The function modifies the histogram's title to include the p-value information.
Warning
The canvas is saved to the current ROOT file using TempCanvas->Write().

Definition at line 629 of file StatisticalUtils.cpp.

629  {
630 // ****************
631  const double TotalIntegral = Histogram->Integral();
632  // Count how many fills are above y=x axis
633  // This is the 2D p-value
634  double Above = 0.0;
635  for (int i = 0; i < Histogram->GetXaxis()->GetNbins(); ++i) {
636  const double xvalue = Histogram->GetXaxis()->GetBinCenter(i+1);
637  for (int j = 0; j < Histogram->GetYaxis()->GetNbins(); ++j) {
638  const double yvalue = Histogram->GetYaxis()->GetBinCenter(j+1);
639  // We're only interested in being _ABOVE_ the y=x axis
640  if (xvalue >= yvalue) {
641  Above += Histogram->GetBinContent(i+1, j+1);
642  }
643  }
644  }
645  const double pvalue = Above/TotalIntegral;
646  std::stringstream ss;
647  ss << int(Above) << "/" << int(TotalIntegral) << "=" << pvalue;
648  Histogram->SetTitle((std::string(Histogram->GetTitle())+"_"+ss.str()).c_str());
649 
650  // Now add the TLine going diagonally
651  double minimum = Histogram->GetXaxis()->GetBinLowEdge(1);
652  if (Histogram->GetYaxis()->GetBinLowEdge(1) > minimum) {
653  minimum = Histogram->GetYaxis()->GetBinLowEdge(1);
654  }
655  double maximum = Histogram->GetXaxis()->GetBinLowEdge(Histogram->GetXaxis()->GetNbins());
656  if (Histogram->GetYaxis()->GetBinLowEdge(Histogram->GetYaxis()->GetNbins()) < maximum) {
657  maximum = Histogram->GetYaxis()->GetBinLowEdge(Histogram->GetYaxis()->GetNbins());
658  //KS: Extend by bin width to perfectly fit canvas
659  maximum += Histogram->GetYaxis()->GetBinWidth(Histogram->GetYaxis()->GetNbins());
660  }
661  else maximum += Histogram->GetXaxis()->GetBinWidth(Histogram->GetXaxis()->GetNbins());
662  auto TempLine = std::make_unique<TLine>(minimum, minimum, maximum, maximum);
663  TempLine->SetLineColor(kRed);
664  TempLine->SetLineWidth(2);
665 
666  std::string Title = Histogram->GetName();
667  Title += "_canv";
668  auto TempCanvas = std::make_unique<TCanvas>(Title.c_str(), Title.c_str(), 1024, 1024);
669  TempCanvas->SetGridx();
670  TempCanvas->SetGridy();
671  TempCanvas->cd();
672  gStyle->SetPalette(81);
673  Histogram->SetMinimum(-0.01);
674  Histogram->Draw("colz");
675  TempLine->Draw("same");
676 
677  TempCanvas->Write();
678 }

◆ GetAndersonDarlingTestStat()

double GetAndersonDarlingTestStat ( const double  CumulativeData,
const double  CumulativeMC,
const double  CumulativeJoint 
)
Parameters
CumulativeDataValue of CDF for data
CumulativeMCValue of CDF for MC
CumulativeJointValue of CDF for joint data and MC distribution

Definition at line 134 of file StatisticalUtils.cpp.

134  {
135 // ****************
136  double ADstat = std::fabs(CumulativeData - CumulativeMC)/ std::sqrt(CumulativeJoint*(1 - CumulativeJoint));
137 
138  if( std::isinf(ADstat) || std::isnan(ADstat)) return 0;
139  return ADstat;
140 }

◆ GetArithmetic()

void GetArithmetic ( TH1D *const  hist,
double &  Mean,
double &  Error 
)

CW: Get Arithmetic mean from posterior.

Parameters
histhistograms from which we extract arithmetic mean
MeanArithmetic Mean value
ErrorArithmetic Error value

Definition at line 207 of file StatisticalUtils.cpp.

207  {
208 // **************************
209  Mean = hist->GetMean();
210  Error = hist->GetRMS();
211 }
double ** Mean
Definition: RHat.cpp:63

◆ GetBetaParameter()

double GetBetaParameter ( const double  data,
const double  mc,
const double  w2,
TestStatistic  TestStat 
)

KS: Calculate Beta parameter which will be different based on specified test statistic.

Parameters
dataNumber of data events in a bin
mcNumber of MC events in a bin
w2Value of weight squared in a bin
TestStatTest statistic based on which we calculate beta

Definition at line 160 of file StatisticalUtils.cpp.

160  {
161 // ****************
162  double Beta = 0.0;
163 
164  if (TestStat == kDembinskiAbdelmotteleb) {
165  //the so-called effective count
166  const double k = mc*mc / w2;
167  //Calculate beta which is scaling factor between true and generated MC
168  Beta = (data + k) / (mc + k);
169  }
170  //KS: Below is technically only true for Cowan's BB, which will not be true for Poisson or IceCube, because why not...
171  else {
172  // CW: Barlow-Beeston uses fractional uncertainty on MC, so sqrt(sum[w^2])/mc
173  const double fractional = std::sqrt(w2)/mc;
174  // CW: -b/2a in quadratic equation
175  const double temp = mc*fractional*fractional-1;
176  // CW: b^2 - 4ac in quadratic equation
177  const double temp2 = temp*temp + 4*data*fractional*fractional;
178  if (temp2 < 0) {
179  MACH3LOG_ERROR("Negative square root in Barlow Beeston coefficient calculation!");
180  throw MaCh3Exception(__FILE__ , __LINE__ );
181  }
182  // CW: Solve for the positive beta
183  Beta = (-1*temp+std::sqrt(temp2))/2.;
184  }
185  return Beta;
186 }
@ kDembinskiAbdelmotteleb
Based on .

◆ GetBIC()

double GetBIC ( const double  llh,
const int  data,
const int  nPars 
)

Get the Bayesian Information Criterion (BIC) or Schwarz information criterion (also SIC, SBC, SBIC)

Definition at line 74 of file StatisticalUtils.cpp.

74  {
75 // ****************
76  if(nPars == 0)
77  {
78  MACH3LOG_ERROR("You haven't passed number of model parameters as it is still zero");
79  throw MaCh3Exception(__FILE__ , __LINE__ );
80  }
81  const double BIC = double(nPars * logl(data) + llh);
82 
83  return BIC;
84 }

◆ GetCredibleInterval()

void GetCredibleInterval ( const std::unique_ptr< TH1D > &  hist,
std::unique_ptr< TH1D > &  hpost_copy,
const double  coverage = 0.6827 
)

KS: Get 1D histogram within credible interval, hpost_copy has to have the same binning, I don't do Copy() as this will lead to problems if this is used under multithreading.

Parameters
histhistograms based on which we calculate credible interval
hpost_copyTo make code thread safe we use copy of histograms which user has to pass
coverageWhat is defined coverage, by default 0.6827 (1 sigma)

Loop over histogram bins with highest number of entries until covered 90 or 68.3%

Get bin of highest content and save the number of entries reached so far

Replace bin value by -1 so it is not looped over as being maximum bin again

Definition at line 329 of file StatisticalUtils.cpp.

329  {
330 // ***************
331  if(coverage > 1)
332  {
333  MACH3LOG_ERROR("Specified Credible Interval is greater that 1 and equal to {} Should be between 0 and 1", coverage);
334  throw MaCh3Exception(__FILE__ , __LINE__ );
335  }
336  //KS: Reset first copy of histogram
337  hpost_copy->Reset("");
338  hpost_copy->Fill(0.0, 0.0);
339 
340  //KS: Temporary structure to be thread save
341  std::vector<double> hist_copy(hist->GetXaxis()->GetNbins()+1);
342  std::vector<bool> hist_copy_fill(hist->GetXaxis()->GetNbins()+1);
343  for (int i = 0; i <= hist->GetXaxis()->GetNbins(); ++i)
344  {
345  hist_copy[i] = hist->GetBinContent(i);
346  hist_copy_fill[i] = false;
347  }
348 
350  const long double Integral = hist->Integral();
351  long double sum = 0;
352 
353  while ((sum / Integral) < coverage)
354  {
356  int max_entry_bin = 0;
357  double max_entries = 0.;
358  for (int i = 0; i <= hist->GetXaxis()->GetNbins(); ++i)
359  {
360  if (hist_copy[i] > max_entries)
361  {
362  max_entries = hist_copy[i];
363  max_entry_bin = i;
364  }
365  }
367  hist_copy[max_entry_bin] = -1.;
368  hist_copy_fill[max_entry_bin] = true;
369 
370  sum += max_entries;
371  }
372  //KS: Now fill our copy only for bins which got included in coverage region
373  for(int i = 0; i <= hist->GetXaxis()->GetNbins(); ++i)
374  {
375  if(hist_copy_fill[i]) hpost_copy->SetBinContent(i, hist->GetBinContent(i));
376  }
377 }

◆ GetCredibleIntervalSig()

void GetCredibleIntervalSig ( const std::unique_ptr< TH1D > &  hist,
std::unique_ptr< TH1D > &  hpost_copy,
const bool  CredibleInSigmas,
const double  coverage = 0.6827 
)

KS: Get 1D histogram within credible interval, hpost_copy has to have the same binning, I don't do Copy() as this will lead to problems if this is used under multithreading.

Parameters
histhistograms based on which we calculate credible interval
hpost_copyTo make code thread safe we use copy of histograms which user has to pass
CredibleInSigmasWhether interval is in sigmas or percentage
coverageWhat is defined coverage, by default 0.6827 (1 sigma)

Definition at line 380 of file StatisticalUtils.cpp.

380  {
381 // ***************
382  //KS: Slightly different approach depending if intervals are in percentage or sigmas
383  if(CredibleInSigmas) {
384  //KS: Convert sigmas into percentage
385  const double CredReg = GetSigmaValue(int(std::round(coverage)));
386  GetCredibleInterval(hist, hpost_copy, CredReg);
387  } else {
388  GetCredibleInterval(hist, hpost_copy, coverage);
389  }
390 }
double GetSigmaValue(const int sigma)
KS: Convert sigma from normal distribution into percentage.
void GetCredibleInterval(const std::unique_ptr< TH1D > &hist, std::unique_ptr< TH1D > &hpost_copy, const double coverage)
KS: Get 1D histogram within credible interval, hpost_copy has to have the same binning,...

◆ GetCredibleRegion()

void GetCredibleRegion ( std::unique_ptr< TH2D > &  hist2D,
const double  coverage = 0.6827 
)

KS: Set 2D contour within some coverage.

Parameters
hist2Dhistograms based on which we calculate credible regions
coverageWhat is defined coverage, by default 0.6827 (1 sigma)

Loop over histogram bins with highest number of entries until covered 90 or 68.3%

Get bin of highest content and save the number of entries reached so far

Replace bin value by -1 so it is not looped over as being maximum bin again

Definition at line 393 of file StatisticalUtils.cpp.

393  {
394 // ***************
395  if(coverage > 1)
396  {
397  MACH3LOG_ERROR("Specified Credible Region is greater than 1 and equal to {:.2f} Should be between 0 and 1", coverage);
398  throw MaCh3Exception(__FILE__ , __LINE__ );
399  }
400 
401  //KS: Temporary structure to be thread save
402  std::vector<std::vector<double>> hist_copy(hist2D->GetXaxis()->GetNbins()+1,
403  std::vector<double>(hist2D->GetYaxis()->GetNbins()+1));
404  for (int i = 0; i <= hist2D->GetXaxis()->GetNbins(); ++i) {
405  for (int j = 0; j <= hist2D->GetYaxis()->GetNbins(); ++j) {
406  hist_copy[i][j] = hist2D->GetBinContent(i, j);
407  }
408  }
409 
411  const long double Integral = hist2D->Integral();
412  long double sum = 0;
413 
414  //We need to as ROOT requires array to set to contour
415  double Contour[1];
416  while ((sum / Integral) < coverage)
417  {
419  int max_entry_bin_x = 0;
420  int max_entry_bin_y = 0;
421  double max_entries = 0.;
422  for (int i = 0; i <= hist2D->GetXaxis()->GetNbins(); ++i)
423  {
424  for (int j = 0; j <= hist2D->GetYaxis()->GetNbins(); ++j)
425  {
426  if (hist_copy[i][j] > max_entries)
427  {
428  max_entries = hist_copy[i][j];
429  max_entry_bin_x = i;
430  max_entry_bin_y = j;
431  }
432  }
433  }
435  hist_copy[max_entry_bin_x][max_entry_bin_y] = -1.;
436 
437  sum += max_entries;
438  Contour[0] = max_entries;
439  }
440  hist2D->SetContour(1, Contour);
441 }

◆ GetCredibleRegionSig()

void GetCredibleRegionSig ( std::unique_ptr< TH2D > &  hist2D,
const bool  CredibleInSigmas,
const double  coverage = 0.6827 
)

KS: Set 2D contour within some coverage.

Parameters
hist2Dhistograms based on which we calculate credible regions
CredibleInSigmasWhether interval is in sigmas or percentage
coverageWhat is defined coverage, by default 0.6827 (1 sigma)

Definition at line 444 of file StatisticalUtils.cpp.

444  {
445 // ***************
446  if(CredibleInSigmas) {
447  //KS: Convert sigmas into percentage
448  const double CredReg = GetSigmaValue(int(std::round(coverage)));
449  GetCredibleRegion(hist2D, CredReg);
450  } else {
451  GetCredibleRegion(hist2D, coverage);
452  }
453 }
void GetCredibleRegion(std::unique_ptr< TH2D > &hist2D, const double coverage)
KS: Set 2D contour within some coverage.

◆ GetDeltaChi2()

std::unique_ptr<TH1D> GetDeltaChi2 ( TH1D *  posterior_probability_hist)

Convert a posterior probability histogram into a \(\Delta\chi^2\) distribution. Using the likelihood-ratio definition:

\[ \Delta\chi^2 = -2 \ln\left(\frac{L}{L_{\max}}\right) \]

Parameters
posterior_probability_histPointer to a TH1D histogram containing posterior probabilities
Note
based on CompareMaCh3PThetaDeltaChi2.C

Definition at line 682 of file StatisticalUtils.cpp.

682  {
683 // ****************
684  auto delta_chi2 = M3::Clone(posterior_probability_hist);
685  delta_chi2->GetYaxis()->SetTitle("#Delta#chi^{2}");
686 
687  int max_bin = delta_chi2->GetMaximumBin();
688  double max_content = delta_chi2->GetBinContent(max_bin);
689  if (max_content == 0) {
690  MACH3LOG_ERROR("Histogram {}, has larges bin with 0", delta_chi2->GetTitle());
691  MACH3LOG_ERROR("This suggest you skewed binning for posterior probability or something else");
692  throw MaCh3Exception(__FILE__, __LINE__);
693  }
694 
695  double NewMaximum = M3::_BAD_DOUBLE_;
696  for(int iBin = 1; iBin < delta_chi2->GetNbinsX()+1; iBin++) {
697  double bin_content = delta_chi2->GetBinContent(iBin);
698  if(bin_content == 0) bin_content = 1.0 ;
699 
700  double chi2_likelihood = -2*std::log(bin_content/max_content);
701  delta_chi2->SetBinContent(iBin, chi2_likelihood);
702  NewMaximum = std::max(NewMaximum, chi2_likelihood);
703  }
704  delta_chi2->SetMaximum(NewMaximum*1.1);
705  return delta_chi2;
706 }
std::unique_ptr< ObjectType > Clone(const ObjectType *obj, const std::string &name="")
KS: Creates a copy of a ROOT-like object and wraps it in a smart pointer.
constexpr static const double _BAD_DOUBLE_
Default value used for double initialisation.
Definition: Core.h:53

◆ GetDunneKaboth()

std::string GetDunneKaboth ( const double  BayesFactor)

Convert a Bayes factor into an approximate particle-physics significance level using the Dunne–Kaboth scale.

This function maps a Bayes factor B(θ1,θ2) to an equivalent Gaussian significance ("n σ") assuming equal priors for the two hypotheses.

The thresholds are based on commonly used particle-physics probability levels and their corresponding Bayes factors:

Bayes factor (B) Approximate significance

B < 2.125 < 1 σ 2.125 ≤ B < 20.74 > 1 σ 20.74 ≤ B < 369.4 > 2 σ 369.4 ≤ B < 15800 > 3 σ 15800 ≤ B < 1745000 > 4 σ B ≥ 1745000 > 5 σ

For example, a Bayes factor of 369.4 corresponds approximately to a 3 σ effect in traditional particle-physics terminology.

Note
For T2K users see Table 1 in https://www.t2k.org/docs/technotes/435
Parameters
BayesFactorThe Bayes factor B(θ1,θ2).
Returns
A string describing the equivalent significance level.

Definition at line 26 of file StatisticalUtils.cpp.

26  {
27 // **************************
28  std::string DunneKaboth = "";
29  //KS: Get fancy DunneKaboth Scale as I am to lazy to look into table every time
30 
31  if(2.125 > BayesFactor) DunneKaboth = "< 1 #sigma";
32  else if( 20.74 > BayesFactor) DunneKaboth = "> 1 #sigma";
33  else if( 369.4 > BayesFactor) DunneKaboth = "> 2 #sigma";
34  else if( 15800 > BayesFactor) DunneKaboth = "> 3 #sigma";
35  else if( 1745000 > BayesFactor) DunneKaboth = "> 4 #sigma";
36  else DunneKaboth = "> 5 #sigma";
37 
38  return DunneKaboth;
39 }

◆ GetGaussian()

void GetGaussian ( TH1D *&  hist,
TF1 *  gauss,
double &  Mean,
double &  Error 
)

CW: Fit Gaussian to posterior.

Parameters
histhistograms to which we fit gaussian
gausstf1 with gaussian, we pass pointer to make things faster
MeanGaussian Mean value
ErrorGaussian Error value

Definition at line 214 of file StatisticalUtils.cpp.

214  {
215 // **************************
216  // Supress spammy ROOT messages
217  int originalErrorLevel = gErrorIgnoreLevel;
218  gErrorIgnoreLevel = kFatal;
219 
220  const double meanval = hist->GetMean();
221  const double err = hist->GetRMS();
222  const double peakval = hist->GetBinCenter(hist->GetMaximumBin());
223 
224  // Set the range for the Gaussian fit
225  gauss->SetRange(meanval - 1.5*err , meanval + 1.5*err);
226  // Set the starting parameters close to RMS and peaks of the histograms
227  gauss->SetParameters(hist->GetMaximum()*err*std::sqrt(2*3.14), peakval, err);
228 
229  // Perform the fit
230  hist->Fit(gauss->GetName(),"Rq");
231  hist->SetStats(0);
232 
233  Mean = gauss->GetParameter(1);
234  Error = gauss->GetParameter(2);
235 
236  // restore original warning setting
237  gErrorIgnoreLevel = originalErrorLevel;
238 }

◆ GetHPD()

void GetHPD ( TH1D *const  hist,
double &  Mean,
double &  Error,
double &  Error_p,
double &  Error_m,
const double  coverage = 0.6827 
)

Get Highest Posterior Density (HPD)

Parameters
histhistograms from which we HPD
MeanHPD Mean value
ErrorHPD Error value
Error_pHPD Negative (left hand side) Error value
Error_mHPD Positive (right hand side) Error value
coverageWhat is defined coverage, by default 0.6827 (1 sigma)

Definition at line 241 of file StatisticalUtils.cpp.

241  {
242 // ****************
243  // Get the bin which has the largest posterior density
244  const int MaxBin = hist->GetMaximumBin();
245  // And it's value
246  const double peakval = hist->GetBinCenter(MaxBin);
247 
248  // The total integral of the posterior
249  const long double Integral = hist->Integral();
250  //KS: and integral of left handed and right handed parts
251  const long double LowIntegral = hist->Integral(1, MaxBin-1) + hist->GetBinContent(MaxBin)/2.0;
252  const long double HighIntegral = hist->Integral(MaxBin+1, hist->GetNbinsX()) + hist->GetBinContent(MaxBin)/2.0;
253 
254  // Keep count of how much area we're covering
255  //KS: Take only half content of HPD bin as one half goes for right handed error and the other for left handed error
256  long double sum = hist->GetBinContent(MaxBin)/2.0;
257 
258  // Counter for current bin
259  int CurrBin = MaxBin;
260  while (sum/HighIntegral < coverage && CurrBin < hist->GetNbinsX()) {
261  CurrBin++;
262  sum += hist->GetBinContent(CurrBin);
263  }
264  const double sigma_p = std::fabs(hist->GetBinCenter(MaxBin)-hist->GetXaxis()->GetBinUpEdge(CurrBin));
265  // Reset the sum
266  //KS: Take only half content of HPD bin as one half goes for right handed error and the other for left handed error
267  sum = hist->GetBinContent(MaxBin)/2.0;
268 
269  // Reset the bin counter
270  CurrBin = MaxBin;
271  // Counter for current bin
272  while (sum/LowIntegral < coverage && CurrBin > 1) {
273  CurrBin--;
274  sum += hist->GetBinContent(CurrBin);
275  }
276  const double sigma_m = std::fabs(hist->GetBinCenter(CurrBin)-hist->GetBinLowEdge(MaxBin));
277 
278  // Now do the double sided HPD
279  //KS: Start sum from the HPD
280  sum = hist->GetBinContent(MaxBin);
281  int LowBin = MaxBin;
282  int HighBin = MaxBin;
283  long double LowCon = 0.0;
284  long double HighCon = 0.0;
285 
286  while (sum/Integral < coverage && (LowBin > 0 || HighBin < hist->GetNbinsX()+1))
287  {
288  LowCon = 0.0;
289  HighCon = 0.0;
290  //KS:: Move further only if you haven't reached histogram end
291  if(LowBin > 1)
292  {
293  LowBin--;
294  LowCon = hist->GetBinContent(LowBin);
295  }
296  if(HighBin < hist->GetNbinsX())
297  {
298  HighBin++;
299  HighCon = hist->GetBinContent(HighBin);
300  }
301 
302  // If we're on the last slice and the lower contour is larger than the upper
303  if ((sum+LowCon+HighCon)/Integral > coverage && LowCon > HighCon) {
304  sum += LowCon;
305  break;
306  // If we're on the last slice and the upper contour is larger than the lower
307  } else if ((sum+LowCon+HighCon)/Integral > coverage && HighCon >= LowCon) {
308  sum += HighCon;
309  break;
310  } else {
311  sum += LowCon + HighCon;
312  }
313  }
314 
315  double sigma_hpd = 0.0;
316  if (LowCon > HighCon) {
317  sigma_hpd = std::fabs(hist->GetBinLowEdge(LowBin)-hist->GetBinCenter(MaxBin));
318  } else {
319  sigma_hpd = std::fabs(hist->GetXaxis()->GetBinUpEdge(HighBin)-hist->GetBinCenter(MaxBin));
320  }
321 
322  Mean = peakval;
323  Error = sigma_hpd;
324  Error_p = sigma_p;
325  Error_m = sigma_m;
326 }

◆ GetIQR()

double GetIQR ( TH1D *  Hist)

Interquartile Range (IQR)

Parameters
Histhistograms from which we IQR

Definition at line 456 of file StatisticalUtils.cpp.

456  {
457 // *********************
458  if(Hist->Integral() == 0) return 0.0;
459 
460  constexpr double quartiles_x[3] = {0.25, 0.5, 0.75};
461  double quartiles[3];
462 
463  Hist->GetQuantiles(3, quartiles, quartiles_x);
464 
465  return quartiles[2] - quartiles[0];
466 }

◆ GetJeffreysScale()

_MaCh3_Safe_Include_Start_ _MaCh3_Safe_Include_End_ std::string GetJeffreysScale ( const double  BayesFactor)

KS: Following H. Jeffreys [22].

Parameters
BayesFactorObtained value of Bayes factor

Definition at line 11 of file StatisticalUtils.cpp.

11  {
12 // **************************
13  std::string JeffreysScale = "";
14  //KS: Get fancy Jeffreys Scale as I am to lazy to look into table every time
15  if(BayesFactor < 0) JeffreysScale = "Negative";
16  else if( 5 > BayesFactor) JeffreysScale = "Barely worth mentioning";
17  else if( 10 > BayesFactor) JeffreysScale = "Substantial";
18  else if( 15 > BayesFactor) JeffreysScale = "Strong";
19  else if( 20 > BayesFactor) JeffreysScale = "Very strong";
20  else JeffreysScale = "Decisive";
21 
22  return JeffreysScale;
23 }

◆ GetModeError()

double GetModeError ( TH1D *  hpost)

Get the mode error from a TH1D.

Parameters
hposthist from which we extract mode error

Definition at line 575 of file StatisticalUtils.cpp.

575  {
576 // ****************
577  // Get the bin which has the largest posterior density
578  int MaxBin = hpost->GetMaximumBin();
579 
580  // The total integral of the posterior
581  const double Integral = hpost->Integral();
582 
583  int LowBin = MaxBin;
584  int HighBin = MaxBin;
585  double sum = hpost->GetBinContent(MaxBin);;
586  double LowCon = 0.0;
587  double HighCon = 0.0;
588  while (sum/Integral < 0.6827 && (LowBin > 0 || HighBin < hpost->GetNbinsX()+1) )
589  {
590  LowCon = 0.0;
591  HighCon = 0.0;
592  //KS:: Move further only if you haven't reached histogram end
593  if(LowBin > 1)
594  {
595  LowBin--;
596  LowCon = hpost->GetBinContent(LowBin);
597  }
598  if(HighBin < hpost->GetNbinsX())
599  {
600  HighBin++;
601  HighCon = hpost->GetBinContent(HighBin);
602  }
603 
604  // If we're on the last slice and the lower contour is larger than the upper
605  if ((sum+LowCon+HighCon)/Integral > 0.6827 && LowCon > HighCon) {
606  sum += LowCon;
607  break;
608  // If we're on the last slice and the upper contour is larger than the lower
609  } else if ((sum+LowCon+HighCon)/Integral > 0.6827 && HighCon >= LowCon) {
610  sum += HighCon;
611  break;
612  } else {
613  sum += LowCon + HighCon;
614  }
615  }
616 
617  double Mode_Error = 0.0;
618  if (LowCon > HighCon) {
619  Mode_Error = std::fabs(hpost->GetBinCenter(LowBin)-hpost->GetBinCenter(MaxBin));
620  } else {
621  Mode_Error = std::fabs(hpost->GetBinCenter(HighBin)-hpost->GetBinCenter(MaxBin));
622  }
623 
624  return Mode_Error;
625 }

◆ GetNeffective()

double GetNeffective ( const int  N1,
const int  N2 
)

KS: See 14.3.10 in Numerical Recipes in C [30].

Definition at line 87 of file StatisticalUtils.cpp.

87  {
88 // ****************
89  const double Nominator = (N1+N2);
90  const double Denominator = (N1*N2);
91  const double N_e = Nominator/Denominator;
92  return N_e;
93 }

◆ GetNumberOfRuns()

int GetNumberOfRuns ( const std::vector< int > &  GroupClasifier)

KS: https://esjeevanand.uccollege.edu.in/wp-content/uploads/sites/114/2020/08/NON-PARAMTERIC-TEST-6.pdf.

Definition at line 143 of file StatisticalUtils.cpp.

143  {
144 // ****************
145  int NumberOfRuns = 0;
146  int PreviousGroup = -999;
147 
148  //KS: If group changed increment run
149  for (unsigned int i = 0; i < GroupClasifier.size(); i++)
150  {
151  if(GroupClasifier[i] != PreviousGroup)
152  NumberOfRuns++;
153  PreviousGroup = GroupClasifier[i];
154  }
155 
156  return NumberOfRuns;
157 }

◆ GetPValueFromZScore()

double GetPValueFromZScore ( const double  zScore)

Compute the P-value from a given Z-score.

The P-value represents the probability of observing a value as extreme as the given Z-score under the null hypothesis of a standard normal distribution.

Parameters
zScoreThe Z-score for which to compute the P-value.
Returns
The P-value corresponding to the given Z-score.

Definition at line 568 of file StatisticalUtils.cpp.

568  {
569 // ********************
570  return 0.5 * std::erfc(-zScore / std::sqrt(2));
571 }

◆ GetSigmaValue()

double GetSigmaValue ( const int  sigma)

KS: Convert sigma from normal distribution into percentage.

Definition at line 42 of file StatisticalUtils.cpp.

42  {
43 // *********************
44  double width = 0;
45  switch (std::abs(sigma))
46  {
47  case 1:
48  width = 0.682689492137;
49  break;
50  case 2:
51  width = 0.954499736104;
52  break;
53  case 3:
54  width = 0.997300203937;
55  break;
56  case 4:
57  width = 0.999936657516;
58  break;
59  case 5:
60  width = 0.999999426697;
61  break;
62  case 6:
63  width = 0.999999998027;
64  break;
65  default:
66  MACH3LOG_ERROR("{} is unsupported value of sigma", sigma);
67  throw MaCh3Exception(__FILE__ , __LINE__ );
68  break;
69  }
70  return width;
71 }

◆ GetSubOptimality()

double GetSubOptimality ( const std::vector< double > &  EigenValues,
const int  TotalTarameters 
)

Based on [31].

Parameters
EigenValuesEigen values of covariance matrix
TotalTarametersTotal number of parameters needed to correctly calculate suboptimality

Definition at line 190 of file StatisticalUtils.cpp.

190  {
191 // *********************
192  double sum_eigenvalues_squared_inv = 0.0;
193  double sum_eigenvalues_inv = 0.0;
194  for (unsigned int j = 0; j < EigenValues.size(); j++)
195  {
196  //KS: IF Eigen values are super small skip them
197  //if(EigenValues[j] < 0.0000001) continue;
198  sum_eigenvalues_squared_inv += std::pow(EigenValues[j], -2);
199  sum_eigenvalues_inv += 1.0 / EigenValues[j];
200  }
201  const double SubOptimality = TotalTarameters * sum_eigenvalues_squared_inv / std::pow(sum_eigenvalues_inv, 2);
202  return SubOptimality;
203 }

◆ GetZScore()

double GetZScore ( const double  value,
const double  mean,
const double  stddev 
)

Compute the Z-score for a given value.

The Z-score indicates how many standard deviations a value is from the mean. A positive Z-score means the value is above the mean, while a negative Z-score means it is below the mean.

Parameters
valueThe data point for which to compute the Z-score.
meanThe mean of the data set.
stddevThe standard deviation of the data set. Must be non-zero.
Returns
The Z-score of the given value.
Warning
Ensure that stddev is not zero to avoid division by zero.

Definition at line 562 of file StatisticalUtils.cpp.

562  {
563 // ********************
564  return (value - mean) / stddev;
565 }

◆ PassErrorToRatioPlot()

void PassErrorToRatioPlot ( TH1D *  RatioHist,
TH1D *  Hist1,
TH1D *  DataHist 
)

Propagate numerator uncertainties to a ratio histogram.

Definition at line 709 of file StatisticalUtils.cpp.

709  {
710 // ****************
711  for (int j = 0; j <= RatioHist->GetNbinsX(); ++j)
712  {
713  if(DataHist->GetBinContent(j) > 0)
714  {
715  double dx = Hist1->GetBinError(j) / DataHist->GetBinContent(j);
716  RatioHist->SetBinError(j, dx);
717  }
718  }
719 }

◆ PoissonGraph()

std::unique_ptr<TGraphAsymmErrors> PoissonGraph ( const TH1D *  h,
double  cl = 0.683 
)

Create a TGraphAsymmErrors from a histogram using exact Poisson confidence intervals instead of symmetric sqrt(N) uncertainties.

Author
Yashwanth S Prabhu

Definition at line 723 of file StatisticalUtils.cpp.

723  {
724 // ****************
725  auto graph = std::make_unique<TGraphAsymmErrors>();
726 
727  const double alpha = 1.0 - cl;
728  const double half = alpha / 2.0;
729 
730  for (int i = 1; i <= hist->GetNbinsX(); i++)
731  {
732  double N = hist->GetBinContent(i);
733  double x = hist->GetBinCenter(i);
734  double ex = hist->GetBinWidth(i) / 2.0;
735 
736  double low = 0.0;
737  double high = 0.0;
738 
739  if (N > 0) {
740  low = N - ROOT::Math::gamma_quantile(half, N, 1.0);
741  high = ROOT::Math::gamma_quantile_c(half, N + 1, 1.0) - N;
742  } else {
743  low = 0.0;
744  high = ROOT::Math::gamma_quantile_c(half, 1, 1.0);
745  }
746 
747  int p = graph->GetN();
748  graph->SetPoint(p, x, N);
749  graph->SetPointError(p, ex, ex, low, high);
750  }
751  return graph;
752 }

◆ PoissonGraphScaled()

std::unique_ptr<TGraphAsymmErrors> PoissonGraphScaled ( const TH1D *  h,
double  scale = 1.0,
double  cl = 0.683 
)

Create a TGraphAsymmErrors from a histogram using exact Poisson confidence intervals instead of symmetric sqrt(N) uncertainties. Assume bin width scaling.

Author
Yashwanth S Prabhu

Definition at line 756 of file StatisticalUtils.cpp.

756  {
757 // ***************************************************************************
758  auto graph = std::make_unique<TGraphAsymmErrors>();
759 
760  const double alpha = 1.0 - cl;
761  const double half = alpha / 2.0;
762 
763  for (int i = 1; i <= hist->GetNbinsX(); i++) {
764  double N = hist->GetBinContent(i); // Raw count
765  double x = hist->GetBinCenter(i);
766  double ex = hist->GetBinWidth(i) / 2.0;
767  double binWidth = hist->GetBinWidth(i);
768 
769  double low = 0.0;
770  double high = 0.0;
771 
772  if (N > 0) {
773  low = N - ROOT::Math::gamma_quantile(half, N, 1.0);
774  high = ROOT::Math::gamma_quantile_c(half, N + 1, 1.0) - N;
775  } else {
776  low = 0.0;
777  high = ROOT::Math::gamma_quantile_c(half, 1, 1.0);
778  }
779 
780  // Scale the y-value and y-errors by (scale / binWidth)
781  double scaleFactor = scale / binWidth;
782  double y = N * scaleFactor;
783  double low_scaled = low * scaleFactor;
784  double high_scaled = high * scaleFactor;
785 
786  int p = graph->GetN();
787  graph->SetPoint(p, x, y);
788  graph->SetPointError(p, ex, ex, low_scaled, high_scaled);
789  }
790  return graph;
791 }

◆ ThinningMCMC()

void ThinningMCMC ( const std::string &  FilePath,
const int  ThinningCut 
)

Thin MCMC Chain, to save space and maintain low autocorrelations.

Parameters
FilePathPath to MCMC chain you want to thin
ThinningCutevery which entry you want to thin [24]
Warning
Thinning is done over entry not steps, it may now work very well for merged chains

Definition at line 517 of file StatisticalUtils.cpp.

517  {
518 // ********************
519  std::string FilePathNowRoot = FilePath;
520  if (FilePath.size() >= 5 && FilePath.substr(FilePath.size() - 5) == ".root") {
521  FilePathNowRoot = FilePath.substr(0, FilePath.size() - 5);
522  }
523  std::string TempFilePath = FilePathNowRoot + "_thinned.root";
524  int ret = system(("cp " + FilePath + " " + TempFilePath).c_str());
525  if (ret != 0) {
526  MACH3LOG_WARN("System call to copy file failed with code {}", ret);
527  }
528 
529  TFile *inFile = M3::Open(TempFilePath, "RECREATE", __FILE__, __LINE__);
530  TTree *inTree = inFile->Get<TTree>("posteriors");
531  if (!inTree) {
532  MACH3LOG_ERROR("TTree 'posteriors' not found in file.");
533  inFile->ls();
534  inFile->Close();
535  throw MaCh3Exception(__FILE__, __LINE__);
536  }
537 
538  // Clone the structure without data
539  TTree *outTree = inTree->CloneTree(0);
540 
541  // Loop over entries and apply thinning
542  Long64_t nEntries = inTree->GetEntries();
543  double retainedPercentage = (double(nEntries) / ThinningCut) / double(nEntries) * 100;
544  MACH3LOG_INFO("Thinning will retain {:.2f}% of chains", retainedPercentage);
545  for (Long64_t i = 0; i < nEntries; i++) {
546  if (i % (nEntries/10) == 0) {
547  M3::Utils::PrintProgressBar(i, nEntries);
548  }
549  if (i % ThinningCut == 0) {
550  inTree->GetEntry(i);
551  outTree->Fill();
552  }
553  }
554  inFile->WriteTObject(outTree, "posteriors", "kOverwrite");
555  inFile->Close();
556  delete inFile;
557 
558  MACH3LOG_INFO("Thinned TTree saved and overwrote original in: {}", TempFilePath);
559 }
void PrintProgressBar(const Long64_t Done, const Long64_t All)
KS: Simply print progress bar.
Definition: Monitor.cpp:229
TFile * Open(const std::string &Name, const std::string &Type, const std::string &File, const int Line)
Opens a ROOT file with the given name and mode.