MaCh3  2.6.0
Reference Guide
StatisticalUtils.cpp
Go to the documentation of this file.
1 //MaCh3 includes
3 #include <numeric>
4 
6 // ROOT includes
7 #include "Math/QuantFuncMathCore.h"
9 
10 // **************************
11 std::string GetJeffreysScale(const double BayesFactor){
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 }
24 
25 // **************************
26 std::string GetDunneKaboth(const double BayesFactor){
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 }
40 
41 // *********************
42 double GetSigmaValue(const int sigma) {
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 }
72 
73 // ****************
74 double GetBIC(const double llh, const int data, const int nPars){
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 }
85 
86 // ****************
87 double GetNeffective(const int N1, const int N2) {
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 }
94 
95 // ****************
96 void CheckBonferoniCorrectedpValue(const std::vector<std::string>& SampleNameVec,
97  const std::vector<double>& PValVec,
98  const double Threshold) {
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 }
132 
133 // ****************
134 double GetAndersonDarlingTestStat(const double CumulativeData, const double CumulativeMC, const double CumulativeJoint) {
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 }
141 
142 // ****************
143 int GetNumberOfRuns(const std::vector<int>& GroupClasifier) {
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 }
158 
159 // ****************
160 double GetBetaParameter(const double data, const double mc, const double w2, const TestStatistic TestStat) {
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 }
187 
188 
189 // *********************
190 double GetSubOptimality(const std::vector<double>& EigenValues, const int TotalTarameters) {
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 }
204 
205 
206 // **************************
207 void GetArithmetic(TH1D * const hist, double& Mean, double& Error) {
208 // **************************
209  Mean = hist->GetMean();
210  Error = hist->GetRMS();
211 }
212 
213 // **************************
214 void GetGaussian(TH1D*& hist, TF1* gauss, double& Mean, double& Error) {
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 }
239 
240 // ***************
241 void GetHPD(TH1D* const hist, double& Mean, double& Error, double& Error_p, double& Error_m, const double coverage) {
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 }
327 
328 // ***************
329 void GetCredibleInterval(const std::unique_ptr<TH1D>& hist, std::unique_ptr<TH1D>& hpost_copy, const double coverage) {
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 }
378 
379 // ***************
380 void GetCredibleIntervalSig(const std::unique_ptr<TH1D>& hist, std::unique_ptr<TH1D>& hpost_copy, const bool CredibleInSigmas, const double coverage) {
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 }
391 
392 // ***************
393 void GetCredibleRegion(std::unique_ptr<TH2D>& hist2D, const double coverage) {
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 }
442 
443 // ***************
444 void GetCredibleRegionSig(std::unique_ptr<TH2D>& hist2D, const bool CredibleInSigmas, const double coverage) {
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 }
454 
455 // *********************
456 double GetIQR(TH1D *Hist) {
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 }
467 
468 // ********************
469 double ComputeKLDivergence(const std::vector<double>& Data,
470  const std::vector<double>& MC) {
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 }
484 
485 // ********************
486 double ComputeKLDivergence(TH2Poly* DataPoly, TH2Poly* PolyMC) {
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 }
499 
500 // ********************
501 double FisherCombinedPValue(const std::vector<double>& pvalues) {
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 }
515 
516 // ********************
517 void ThinningMCMC(const std::string& FilePath, const int ThinningCut) {
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 }
560 
561 // ********************
562 double GetZScore(const double value, const double mean, const double stddev) {
563 // ********************
564  return (value - mean) / stddev;
565 }
566 
567 // ********************
568 double GetPValueFromZScore(const double zScore) {
569 // ********************
570  return 0.5 * std::erfc(-zScore / std::sqrt(2));
571 }
572 
573 // ****************
574 // Get the mode error from a TH1D
575 double GetModeError(TH1D* hpost) {
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 }
626 
627 // ****************
628 // Make the 2D cut distribution and give the 2D p-value
629 void Get2DBayesianpValue(TH2D *Histogram) {
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 }
679 
680 // ****************
681 // Converts posterior likelihood to dchi2
682 std::unique_ptr<TH1D> GetDeltaChi2(TH1D* posterior_probability_hist) {
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 }
707 
708 // ****************
709 void PassErrorToRatioPlot(TH1D* RatioHist, TH1D* Hist1, TH1D* DataHist) {
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 }
720 
721 
722 // ****************
723 std::unique_ptr<TGraphAsymmErrors> PoissonGraph(const TH1D* hist, double cl) {
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 }
753 
754 
755 // ***************************************************************************
756 std::unique_ptr<TGraphAsymmErrors> PoissonGraphScaled(const TH1D* hist, double scale, double cl) {
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 }
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
Definition: Core.h:126
#define _MaCh3_Safe_Include_End_
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
double ** Mean
Definition: RHat.cpp:63
TestStatistic
Make an enum of the test statistic that we're using.
@ kDembinskiAbdelmotteleb
Based on .
double GetSigmaValue(const int sigma)
KS: Convert sigma from normal distribution into percentage.
std::unique_ptr< TGraphAsymmErrors > PoissonGraph(const TH1D *hist, double cl)
Create a TGraphAsymmErrors from a histogram using exact Poisson confidence intervals instead of symme...
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.
double ComputeKLDivergence(const std::vector< double > &Data, const std::vector< double > &MC)
Compute the Kullback-Leibler divergence between two TH2Poly histograms.
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 symme...
double GetSubOptimality(const std::vector< double > &EigenValues, const int TotalTarameters)
Based on .
double GetBIC(const double llh, const int data, const int nPars)
Get the Bayesian Information Criterion (BIC) or Schwarz information criterion (also SIC,...
double GetPValueFromZScore(const double zScore)
Compute the P-value from a given Z-score.
void Get2DBayesianpValue(TH2D *Histogram)
Calculates the 2D Bayesian p-value and generates a visualization.
double GetZScore(const double value, const double mean, const double stddev)
Compute the Z-score for a given value.
void GetGaussian(TH1D *&hist, TF1 *gauss, double &Mean, double &Error)
CW: Fit Gaussian to posterior.
void GetCredibleRegion(std::unique_ptr< TH2D > &hist2D, const double coverage)
KS: Set 2D contour within some coverage.
double GetModeError(TH1D *hpost)
Get the mode error from a TH1D.
double GetAndersonDarlingTestStat(const double CumulativeData, const double CumulativeMC, const double CumulativeJoint)
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,...
double FisherCombinedPValue(const std::vector< double > &pvalues)
KS: Combine p-values using Fisher's method.
void PassErrorToRatioPlot(TH1D *RatioHist, TH1D *Hist1, TH1D *DataHist)
Propagate numerator uncertainties to a ratio histogram.
double GetIQR(TH1D *Hist)
Interquartile Range (IQR)
void ThinningMCMC(const std::string &FilePath, const int ThinningCut)
Thin MCMC Chain, to save space and maintain low autocorrelations.
double GetNeffective(const int N1, const int N2)
KS: See 14.3.10 in Numerical Recipes in C .
void GetHPD(TH1D *const hist, double &Mean, double &Error, double &Error_p, double &Error_m, const double coverage)
Get Highest Posterior Density (HPD)
void GetCredibleRegionSig(std::unique_ptr< TH2D > &hist2D, const bool CredibleInSigmas, const double coverage)
KS: Set 2D contour within some coverage.
void GetArithmetic(TH1D *const hist, double &Mean, double &Error)
CW: Get Arithmetic mean from posterior.
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,...
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.
std::string GetDunneKaboth(const double BayesFactor)
Convert a Bayes factor into an approximate particle-physics significance level using the Dunne–Kaboth...
int GetNumberOfRuns(const std::vector< int > &GroupClasifier)
KS: https://esjeevanand.uccollege.edu.in/wp-content/uploads/sites/114/2020/08/NON-PARAMTERIC-TEST-6....
_MaCh3_Safe_Include_Start_ _MaCh3_Safe_Include_End_ std::string GetJeffreysScale(const double BayesFactor)
KS: Following H. Jeffreys .
std::unique_ptr< TH1D > GetDeltaChi2(TH1D *posterior_probability_hist)
Convert a posterior probability histogram into a distribution. Using the likelihood-ratio definition...
Utility functions for statistical interpretations in MaCh3.
Custom exception class used throughout MaCh3.
void PrintProgressBar(const Long64_t Done, const Long64_t All)
KS: Simply print progress bar.
Definition: Monitor.cpp:229
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
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.