ComPWA
Common Partial-Wave-Analysis Framework
HistTools.hpp
Go to the documentation of this file.
1 // Copyright (c) 2013, 2017 The ComPWA Team.
2 // This file is part of the ComPWA framework, check
3 // https://github.com/ComPWA/ComPWA/license.txt for details.
4 
10 
11 #ifndef TOOLS_H
12 #define TOOLS_H
13 
14 #include <TCanvas.h>
15 #include <TFile.h>
16 #include <TGraph.h>
17 #include <TH1D.h>
18 #include <TH2D.h>
19 #include <TH2Poly.h>
20 #include <TKDTreeBinning.h>
21 #include <TLatex.h>
22 #include <TMath.h>
23 #include <TObject.h>
24 #include <TPad.h>
25 #include <TPaveStats.h>
26 #include <TRandom3.h>
27 #include <TStyle.h>
28 
29 #include "Core/Logging.hpp"
30 inline TH1 *getPull(TH1 *Histogram1, TH1 *Histogram2, TString Name = "pull_") {
31  if (Histogram1->GetNbinsX() != Histogram2->GetNbinsX() ||
32  Histogram1->GetNbinsY() != Histogram2->GetNbinsY() ||
33  Histogram1->GetNbinsZ() != Histogram2->GetNbinsZ()) {
34  std::cout << "binning doesn't match" << std::endl;
35  return 0;
36  }
37  TH1 *PullHistogram = (TH1 *)Histogram1->Clone(Name + Histogram1->GetName());
38  PullHistogram->Reset();
39  double PullLimit = 0;
40  for (int i = 1; i <= Histogram1->GetNbinsX(); ++i) {
41  for (int j = 1; j <= Histogram1->GetNbinsY(); ++j) {
42  for (int k = 1; k <= Histogram1->GetNbinsZ(); ++k) {
43  unsigned int bin = Histogram1->GetBin(i, j, k);
44  double c1 = Histogram1->GetBinContent(bin);
45  double c2 = Histogram2->GetBinContent(bin);
46  double c1Err = Histogram1->GetBinError(bin);
47  double c2Err = Histogram2->GetBinError(bin);
48  double Pull;
49  if (c1 > 0 && c2 > 0) {
50  Pull = (c1 - c2) / sqrt(c1Err * c1Err + c2Err * c2Err);
51  PullHistogram->SetBinContent(bin, Pull);
52  if (std::fabs(Pull) > PullLimit)
53  PullLimit = std::abs(Pull);
54  } else
55  PullHistogram->SetBinContent(
56  bin,
57  -999); // empty bins are set to error value
58  PullHistogram->SetBinError(bin, .0001);
59  }
60  }
61  }
62  PullLimit = 4; // set fix limits
63  PullLimit += 1;
64  // X
65  PullHistogram->GetXaxis()->SetTitleSize(.14);
66  PullHistogram->GetXaxis()->SetTitleOffset(.93);
67  PullHistogram->GetXaxis()->SetLabelSize(.12);
68  if (PullHistogram->GetDimension() == 1) {
69  PullHistogram->GetYaxis()->SetRangeUser(-PullLimit, PullLimit);
70  PullHistogram->GetYaxis()->SetTitle("deviation [#sigma]");
71  PullHistogram->GetYaxis()->SetTitleOffset(0.36);
72  PullHistogram->GetYaxis()->SetTitleSize(0.12);
73  PullHistogram->GetYaxis()->SetLabelSize(0.12);
74  PullHistogram->GetYaxis()->SetNdivisions(504);
75  PullHistogram->GetYaxis()->CenterTitle();
76  }
77  if (PullHistogram->GetDimension() ==
78  2) { // symmetric range including all values
79  PullHistogram->GetZaxis()->SetRangeUser(-PullLimit, PullLimit);
80  PullHistogram->GetZaxis()->SetTitle("deviation [#sigma]");
81  PullHistogram->GetYaxis()->SetTitleSize(.14);
82  PullHistogram->GetYaxis()->SetTitleOffset(.93);
83  PullHistogram->GetYaxis()->SetLabelSize(.12);
84  }
85  PullHistogram->SetTitle("");
86  PullHistogram->SetStats(0);
87  return PullHistogram;
88 }
89 
90 inline TH1 *getResidual(TH1 *Histogram1, TH1 *Histogram2,
91  TString Name = "res_") {
92  if (Histogram1->GetNbinsX() != Histogram2->GetNbinsX() ||
93  Histogram1->GetNbinsY() != Histogram2->GetNbinsY() ||
94  Histogram1->GetNbinsZ() != Histogram2->GetNbinsZ()) {
95  std::cout << "binning doesn't match" << std::endl;
96  return 0;
97  }
98  TH1 *Residuals = (TH1 *)Histogram1->Clone(Name + Histogram1->GetName());
99  Residuals->Reset();
100 
101  Histogram2->Scale(Histogram1->Integral() / Histogram2->Integral());
102  double Limit = 0;
103  for (int i = 1; i <= Histogram1->GetNbinsX(); ++i) {
104  for (int j = 1; j <= Histogram1->GetNbinsY(); ++j) {
105  for (int k = 1; k <= Histogram1->GetNbinsZ(); ++k) {
106  unsigned int Bin = Histogram1->GetBin(i, j, k);
107  double BinContent1 = Histogram1->GetBinContent(Bin);
108  double BinContent2 = Histogram2->GetBinContent(Bin);
109  if (BinContent1 == 0 && BinContent2 == 0) {
110  double res = -9999;
111  Residuals->SetBinContent(Bin, res);
112  } else {
113  double res = BinContent1 - BinContent2;
114  Residuals->SetBinContent(Bin, res);
115  if (std::fabs(res) > Limit)
116  Limit = std::abs(res);
117  }
118  Residuals->SetBinError(Bin, 0);
119  }
120  }
121  }
122  Limit += 1;
123  if (Residuals->GetDimension() == 1)
124  Residuals->GetYaxis()->SetRangeUser(
125  -Limit, Limit); // symmetric range including all values
126  if (Residuals->GetDimension() == 2)
127  Residuals->GetZaxis()->SetRangeUser(
128  -Limit, Limit); // symmetric range including all values
129  return Residuals;
130 }
131 
132 inline TPad *drawHist(std::vector<TH1D *> Histograms,
133  std::vector<TString> DrawOption, double Min = 0,
134  double Max = 0) {
135  if (Histograms.size() != DrawOption.size())
136  throw std::runtime_error("drawPull() | Number of histograms and number "
137  "of draw options does not match!");
138 
139  gStyle->SetOptTitle(0);
140  gStyle->SetOptStat(0); // entries only
141 
142  TPad *Pad = new TPad();
143  if (!Histograms.size())
144  return 0;
145  for (unsigned int i = 0; i < Histograms.size(); i++)
146  Histograms.at(i)->Draw(DrawOption.at(i));
147  return Pad;
148 }
149 
150 inline TPad *drawPull(std::vector<TH1D *> Histogram,
151  std::vector<TString> DrawOptions, double Min = 0,
152  double Max = 0) {
153  if (Histogram.size() != DrawOptions.size())
154  throw std::runtime_error("drawPull() | Number of histograms and number "
155  "of draw options does not match!");
156 
157  if (!Histogram.size())
158  LOG(ERROR) << "drawPull() | No histograms given.";
159 
160  Int_t OldOptTitle = gStyle->GetOptTitle();
161  Int_t OldOptStat = gStyle->GetOptStat();
162  gStyle->SetOptTitle(0);
163  gStyle->SetOptStat(0); // entries only
164 
165  TPad *Pad = nullptr;
166  if (Histogram.size() == 1) {
167  Pad = new TPad();
168  Histogram.at(0)->Draw(DrawOptions.at(0));
169  } else {
170  Pad = new TPad("hist", "hist", 0.0, 0.3, 1, 1);
171  Pad->Draw();
172  Pad->SetMargin(0.1, 0.05, 0.0, 0.05);
173  auto PullPad = new TPad("pull", "pull", 0.0, 0.0, 1, 0.3);
174  PullPad->Draw();
175  PullPad->SetMargin(0.1, 0.05, 0.3, 0.0); // left-right-bottom-top
176 
177  if (Histogram.at(0)->GetDimension() != 1 ||
178  Histogram.at(1)->GetDimension() != 1) {
179  std::cout << "Dimension of histograms larger 1!" << std::endl;
180  return Pad;
181  }
182  if (Histogram.at(0)->GetNbinsX() != Histogram.at(1)->GetNbinsX()) {
183  std::cout << "binning doesn't match" << std::endl;
184  return Pad;
185  }
186 
187  Pad->cd();
188  Histogram.at(0)->GetYaxis()->SetRangeUser(
189  0.00000001, Histogram.at(0)->GetMaximum() * 1.3);
190  Histogram.at(0)->GetYaxis()->SetTitleOffset(1.0);
191  Histogram.at(0)->GetYaxis()->SetTitleSize(0.05);
192  Histogram.at(0)->GetYaxis()->SetLabelSize(0.05);
193 
194  Histogram.at(0)->Draw(DrawOptions.at(0));
195  Histogram.at(1)->Draw(DrawOptions.at(1));
196  for (unsigned int i = 2; i < Histogram.size(); ++i)
197  Histogram.at(i)->Draw(DrawOptions.at(i));
198 
199  Double_t chi2;
200  Double_t Chi2Results[Histogram.at(0)->GetNbinsX()];
201  Int_t NDF, IGood;
202  Histogram.at(0)->Chi2TestX(Histogram.at(1), chi2, NDF, IGood, "UW",
203  Chi2Results);
204 
205  char Chi2Char[60];
206  sprintf(Chi2Char, "#chi^{2}_{1D}/ndf = %.2f/%d", chi2, NDF);
207  // TLatex* ltx = new TLatex();
208  // ltx->DrawLatexNDC(0.2,0.85,chi2Char);//needs to be drawn inside a TPad
209  // delete ltx;
210 
211  TAxis *XAxis = ((TH1 *)Histogram.at(0))->GetXaxis();
212  Int_t NPoints = XAxis->GetNbins();
213  Double_t Points[NPoints];
214  for (Int_t i = 0; i < NPoints; i++)
215  Points[i] = XAxis->GetBinCenter(i + 1);
216 
217  TGraph *PullGraph = new TGraph(NPoints, Points, Chi2Results);
218  PullGraph->SetName("dataAdaptiveBinned");
219  PullPad->cd();
220 
221  // pad_pull->SetGridy();
222  PullGraph->Draw("AP"); // draw markers only
223  PullGraph->SetTitle("Normalized residuals");
224  PullGraph->GetXaxis()->SetTitle(XAxis->GetTitle());
225  PullGraph->GetXaxis()->SetTitleOffset(0.8);
226  PullGraph->GetXaxis()->SetTitleSize(0.15);
227  PullGraph->GetXaxis()->SetLabelSize(0.10);
228  PullGraph->GetXaxis()->SetLimits(XAxis->GetBinLowEdge(1),
229  XAxis->GetBinLowEdge(NPoints + 1));
230  PullGraph->GetYaxis()->SetTitle("Dev. [#sigma]");
231  PullGraph->GetYaxis()->SetTitleOffset(0.4);
232  PullGraph->GetYaxis()->SetNdivisions(504);
233  if (Min != Max)
234  PullGraph->GetYaxis()->SetRangeUser(Min, Max);
235  else
236  PullGraph->GetYaxis()->SetRangeUser(-5, 5.5);
237  PullGraph->GetYaxis()->SetTitleSize(0.10);
238  PullGraph->GetYaxis()->SetLabelSize(0.12);
239  PullGraph->GetYaxis()->CenterTitle(1);
240 
241  TLine *Line = new TLine(XAxis->GetBinLowEdge(1), 0.0,
242  XAxis->GetBinUpEdge(NPoints + 1), 0.0);
243  Line->SetLineStyle(2);
244  Line->Draw();
245  gPad->Update();
246 
247  gStyle->SetOptTitle(OldOptTitle);
248  gStyle->SetOptTitle(OldOptStat);
249  }
250  return Pad;
251 }
252 
253 inline TPad *drawPull(TH1D *Histogram1, TH1D *Histogram2, TString DrawOption1,
254  TString DrawOption2, double Min = 0, double Max = 0) {
255  std::vector<TH1D *> Histograms;
256  Histograms.push_back(Histogram1);
257  Histograms.push_back(Histogram2);
258  std::vector<TString> DrawOptions;
259  DrawOptions.push_back(DrawOption1);
260  DrawOptions.push_back(DrawOption2);
261  return drawPull(Histograms, DrawOptions, Min, Max);
262 }
263 
264 inline TPad *drawResidual(std::vector<TH1D *> Histograms,
265  std::vector<TString> DrawOptions, double Min = 0,
266  double Max = 0) {
267  if (Histograms.size() != DrawOptions.size())
268  throw std::runtime_error("drawResidual() | Number of histograms and number "
269  "of draw options does not match!");
270 
271  Int_t OldOptTitle = gStyle->GetOptTitle();
272  Int_t OldOptStat = gStyle->GetOptStat();
273  gStyle->SetOptTitle(0);
274  gStyle->SetOptStat(0); // entries only
275 
276  TPad *Pad = nullptr;
277  if (Histograms.size() == 1) {
278  Pad = new TPad();
279  Histograms.at(0)->Draw(DrawOptions.at(0));
280  Histograms.at(0)->GetYaxis()->SetTitleSize(0.06);
281  } else if (Histograms.size() == 2) {
282  Pad = new TPad("hist", "hist", 0.0, 0.3, 1, 1);
283  Pad->Draw();
284  Pad->SetMargin(0.1, 0.05, 0.0, 0.05);
285  TPad *PullPad = new TPad("pull", "pull", 0.0, 0.0, 1, 0.3);
286  PullPad->Draw();
287  PullPad->SetMargin(0.1, 0.05, 0.3, 0.0); // left-right-bottom-top
288 
289  if (Histograms.at(0)->GetDimension() != 1 ||
290  Histograms.at(1)->GetDimension() != 1) {
291  std::cout << "Dimension of histograms larger 1!" << std::endl;
292  return Pad;
293  }
294  if (Histograms.at(0)->GetNbinsX() != Histograms.at(1)->GetNbinsX()) {
295  std::cout << "binning doesn't match" << std::endl;
296  return Pad;
297  }
298 
299  Pad->cd();
300  Histograms.at(0)->GetYaxis()->SetRangeUser(
301  0.00000001, Histograms.at(0)->GetMaximum() * 1.1);
302  Histograms.at(0)->GetYaxis()->SetTitleOffset(0.83);
303 
304  Histograms.at(0)->Draw(DrawOptions.at(0));
305  Histograms.at(1)->Draw(DrawOptions.at(1));
306  Histograms.at(0)->GetYaxis()->SetTitleSize(0.06);
307  for (unsigned int i = 2; i < Histograms.size(); ++i) {
308  Histograms.at(i)->Draw(DrawOptions.at(i));
309  }
310 
311  Double_t Chi2;
312  Double_t Chi2Result[Histograms.at(0)->GetNbinsX()];
313  Int_t NDF, IGood;
314  Histograms.at(0)->Chi2TestX(Histograms.at(1), Chi2, NDF, IGood, "UW",
315  Chi2Result);
316 
317  char Chi2Char[60];
318  sprintf(Chi2Char, "#chi^{2}_{1D}/ndf = %.2f/%d", Chi2, NDF);
319 
320  TAxis *XAxis = ((TH1 *)Histograms.at(0))->GetXaxis();
321  Int_t NPoints = XAxis->GetNbins();
322 
323  TH1D *Residuals = (TH1D *)Histograms.at(0)->Clone("resHist");
324  Residuals->Add(Histograms.at(1), -1);
325  PullPad->cd();
326  Residuals->Draw("E"); // draw markers only
327  Residuals->SetTitle("Residuals");
328  Residuals->GetXaxis()->SetTitle(XAxis->GetTitle());
329  Residuals->GetXaxis()->SetTitleOffset(0.8);
330  Residuals->GetXaxis()->SetTitleSize(0.15);
331  Residuals->GetXaxis()->SetLabelSize(0.10);
332  Residuals->GetXaxis()->SetLimits(XAxis->GetBinLowEdge(1),
333  XAxis->GetBinLowEdge(NPoints + 1));
334  Residuals->GetYaxis()->SetTitle("Deviation");
335  Residuals->GetYaxis()->SetTitleOffset(0.4);
336  Residuals->GetYaxis()->SetNdivisions(504);
337  if (Min != Max)
338  Residuals->GetYaxis()->SetRangeUser(Min, Max);
339  Residuals->GetYaxis()->SetTitleSize(0.12);
340  Residuals->GetYaxis()->SetLabelSize(0.12);
341  Residuals->GetYaxis()->CenterTitle(1);
342  Residuals->SetMarkerStyle(20);
343 
344  TLine *Line = new TLine(XAxis->GetBinLowEdge(1), 0.0,
345  XAxis->GetBinUpEdge(NPoints + 1), 0.0);
346  Line->SetLineStyle(2);
347  Line->Draw();
348  gPad->Update();
349 
350  gStyle->SetOptTitle(OldOptTitle);
351  gStyle->SetOptTitle(OldOptStat);
352  }
353  return Pad;
354 }
355 
356 inline TPad *drawResidual(TH1D *Histogram1, TH1D *Histogram2,
357  TString DrawOption1, TString DrawOption2,
358  double Min = 0, double Max = 0) {
359  std::vector<TH1D *> Histograms;
360  Histograms.push_back(Histogram1);
361  Histograms.push_back(Histogram2);
362  std::vector<TString> DrawOptions;
363  DrawOptions.push_back(DrawOption1);
364  DrawOptions.push_back(DrawOption2);
365  return drawResidual(Histograms, DrawOptions, Min, Max);
366 }
367 
368 inline void getTH2PolyChi2(TH2Poly *Histogram1, TH2Poly *Histogram2,
369  double &Chi2, int &NDF, int &IGood) {
370  if (Histogram1->GetBins()->GetEntries() !=
371  Histogram2->GetBins()->GetEntries()) {
372  std::cout << "binning doesn't match" << std::endl;
373  return;
374  }
375  Chi2 = 0;
376  NDF = 0;
377  for (int Bin = 1; Bin <= Histogram1->GetBins()->GetEntries(); Bin++) {
378  double BinContent1 = Histogram1->GetBinContent(Bin);
379  double BinContent2 = Histogram2->GetBinContent(Bin);
380  double BinError1 = Histogram1->GetBinError(Bin);
381  double BinError2 = Histogram2->GetBinError(Bin);
382  if (BinContent1 > 0 || BinContent2 > 0) {
383  Chi2 += (BinContent1 - BinContent2) * (BinContent1 - BinContent2) /
384  (BinError1 * BinError1 + BinError2 * BinError2);
385  NDF++;
386  }
387  }
388  return;
389 }
390 
391 inline TH2Poly *getTH2PolyPull(TH2Poly *Histogram1, TH2Poly *Histogram2,
392  TString Name) {
393  if (Histogram1->GetBins()->GetEntries() !=
394  Histogram2->GetBins()->GetEntries()) {
395  std::cout << "binning doesn't match" << std::endl;
396  return 0;
397  }
398  TH2Poly *Residuals =
399  (TH2Poly *)Histogram1->Clone(Name + Histogram1->GetName());
400  Residuals->Reset("");
401 
402  Histogram2->Scale(Histogram1->Integral() / Histogram2->Integral());
403  double Limit = 0;
404  double Integral = 0;
405  int NBins = 0;
406  for (int bin = 1; bin <= Histogram1->GetBins()->GetEntries(); bin++) {
407  double BinContent1 = Histogram1->GetBinContent(bin);
408  double BinContent2 = Histogram2->GetBinContent(bin);
409  double BinError1 = Histogram1->GetBinError(bin);
410  double BinError2 = Histogram2->GetBinError(bin);
411  if (BinContent1 > 0 && BinContent2 > 0) {
412  double Pull = (BinContent1 - BinContent2) /
413  sqrt(BinError1 * BinError1 + BinError2 * BinError2);
414  Integral += Pull;
415  NBins++;
416  Residuals->SetBinContent(bin, Pull);
417  if (std::fabs(Pull) > Limit)
418  Limit = std::abs(Pull);
419  } else
420  Residuals->SetBinContent(bin, -999); // empty bins are set to error value
421  Residuals->SetBinError(bin, 0);
422  }
423  Limit = 4;
424  Limit += 1;
425  Residuals->GetZaxis()->SetRangeUser(
426  -Limit, Limit); // symmetric range including all values
427  Residuals->GetZaxis()->SetTitle("deviation [#sigma]");
428  Residuals->GetZaxis()->SetTitleOffset(0.5);
429  std::cout << "Creating pull histogram for TH2Poly! integral=" << Integral
430  << " nBins=" << NBins << std::endl;
431 
432  return Residuals;
433 }
434 
435 inline TH2Poly *adaptiveBinning(UInt_t DataSize, UInt_t DataDimension,
436  Double_t *Data, UInt_t NBins = 100) {
437  UInt_t Size =
438  UInt_t(DataSize / NBins) * NBins; // size should be a multiple of nBins
439  if (Size == 0) { // return empty histogram
440  TH2Poly *Histogram = new TH2Poly();
441  Histogram->AddBin(0, 1, 0, 1);
442  return Histogram;
443  }
444 
445  TKDTreeBinning KDBins(Size, DataDimension, Data, NBins);
446 
447  UInt_t NKDBins = KDBins.GetNBins();
448  UInt_t Dimension = KDBins.GetDim();
449 
450  const Double_t *binsMinEdges = KDBins.GetBinsMinEdges();
451  const Double_t *binsMaxEdges = KDBins.GetBinsMaxEdges();
452 
453  TH2Poly *Histogram = new TH2Poly("h2PolyBinTest", "", KDBins.GetDataMin(0),
454  KDBins.GetDataMax(0), KDBins.GetDataMin(1),
455  KDBins.GetDataMax(1));
456 
457  for (UInt_t i = 0; i < NKDBins; ++i) {
458  UInt_t EdgeDim = i * Dimension;
459  Histogram->AddBin(binsMinEdges[EdgeDim], binsMinEdges[EdgeDim + 1],
460  binsMaxEdges[EdgeDim], binsMaxEdges[EdgeDim + 1]);
461  }
462 
463  return Histogram;
464 }
465 #endif
TH2Poly * getTH2PolyPull(TH2Poly *Histogram1, TH2Poly *Histogram2, TString Name)
Definition: HistTools.hpp:391
void getTH2PolyChi2(TH2Poly *Histogram1, TH2Poly *Histogram2, double &Chi2, int &NDF, int &IGood)
Definition: HistTools.hpp:368
TPad * drawResidual(std::vector< TH1D *> Histograms, std::vector< TString > DrawOptions, double Min=0, double Max=0)
Definition: HistTools.hpp:264
TPad * drawHist(std::vector< TH1D *> Histograms, std::vector< TString > DrawOption, double Min=0, double Max=0)
Definition: HistTools.hpp:132
TPad * drawPull(std::vector< TH1D *> Histogram, std::vector< TString > DrawOptions, double Min=0, double Max=0)
Definition: HistTools.hpp:150
TH1 * getPull(TH1 *Histogram1, TH1 *Histogram2, TString Name="pull_")
Definition: HistTools.hpp:30
TH2Poly * adaptiveBinning(UInt_t DataSize, UInt_t DataDimension, Double_t *Data, UInt_t NBins=100)
Definition: HistTools.hpp:435
TH1 * getResidual(TH1 *Histogram1, TH1 *Histogram2, TString Name="res_")
Definition: HistTools.hpp:90