Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TGraphAsymmErrors.cxx
Go to the documentation of this file.
1 // @(#)root/hist:$Id$
2 // Author: Rene Brun 03/03/99
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include <string.h>
13 
14 #include "Riostream.h"
15 #include "TEfficiency.h"
16 #include "TROOT.h"
17 #include "TGraphAsymmErrors.h"
18 #include "TGraphErrors.h"
19 #include "TStyle.h"
20 #include "TMath.h"
21 #include "TArrow.h"
22 #include "TBox.h"
23 #include "TVirtualPad.h"
24 #include "TF1.h"
25 #include "TH1.h"
26 #include "TVector.h"
27 #include "TVectorD.h"
28 #include "TClass.h"
29 #include "TSystem.h"
30 #include "Math/QuantFuncMathCore.h"
31 
32 ClassImp(TGraphAsymmErrors);
33 
34 /** \class TGraphAsymmErrors
35  \ingroup Hist
36 TGraph with asymmetric error bars.
37 
38 The TGraphAsymmErrors painting is performed thanks to the TGraphPainter
39 class. All details about the various painting options are given in this class.
40 
41 The picture below gives an example:
42 
43 Begin_Macro(source)
44 {
45  auto c1 = new TCanvas("c1","A Simple Graph with asymmetric error bars",200,10,700,500);
46  c1->SetFillColor(42);
47  c1->SetGrid();
48  c1->GetFrame()->SetFillColor(21);
49  c1->GetFrame()->SetBorderSize(12);
50  const Int_t n = 10;
51  Double_t x[n] = {-0.22, 0.05, 0.25, 0.35, 0.5, 0.61,0.7,0.85,0.89,0.95};
52  Double_t y[n] = {1,2.9,5.6,7.4,9,9.6,8.7,6.3,4.5,1};
53  Double_t exl[n] = {.05,.1,.07,.07,.04,.05,.06,.07,.08,.05};
54  Double_t eyl[n] = {.8,.7,.6,.5,.4,.4,.5,.6,.7,.8};
55  Double_t exh[n] = {.02,.08,.05,.05,.03,.03,.04,.05,.06,.03};
56  Double_t eyh[n] = {.6,.5,.4,.3,.2,.2,.3,.4,.5,.6};
57  auto gr = new TGraphAsymmErrors(n,x,y,exl,exh,eyl,eyh);
58  gr->SetTitle("TGraphAsymmErrors Example");
59  gr->SetMarkerColor(4);
60  gr->SetMarkerStyle(21);
61  gr->Draw("ALP");
62 }
63 End_Macro
64 */
65 
66 
67 ////////////////////////////////////////////////////////////////////////////////
68 /// TGraphAsymmErrors default constructor.
69 
70 TGraphAsymmErrors::TGraphAsymmErrors(): TGraph()
71 {
72  fEXlow = 0;
73  fEYlow = 0;
74  fEXhigh = 0;
75  fEYhigh = 0;
76 }
77 
78 
79 ////////////////////////////////////////////////////////////////////////////////
80 /// TGraphAsymmErrors copy constructor
81 
82 TGraphAsymmErrors::TGraphAsymmErrors(const TGraphAsymmErrors &gr)
83  : TGraph(gr)
84 {
85  if (!CtorAllocate()) return;
86  Int_t n = fNpoints*sizeof(Double_t);
87  memcpy(fEXlow, gr.fEXlow, n);
88  memcpy(fEYlow, gr.fEYlow, n);
89  memcpy(fEXhigh, gr.fEXhigh, n);
90  memcpy(fEYhigh, gr.fEYhigh, n);
91 }
92 
93 
94 ////////////////////////////////////////////////////////////////////////////////
95 /// TGraphAsymmErrors assignment operator
96 
97 TGraphAsymmErrors& TGraphAsymmErrors::operator=(const TGraphAsymmErrors &gr)
98 {
99  if(this!=&gr) {
100  TGraph::operator=(gr);
101  // delete arrays
102  if (fEXlow) delete [] fEXlow;
103  if (fEYlow) delete [] fEYlow;
104  if (fEXhigh) delete [] fEXhigh;
105  if (fEYhigh) delete [] fEYhigh;
106 
107  if (!CtorAllocate()) return *this;
108  Int_t n = fNpoints*sizeof(Double_t);
109  memcpy(fEXlow, gr.fEXlow, n);
110  memcpy(fEYlow, gr.fEYlow, n);
111  memcpy(fEXhigh, gr.fEXhigh, n);
112  memcpy(fEYhigh, gr.fEYhigh, n);
113  }
114  return *this;
115 }
116 
117 
118 ////////////////////////////////////////////////////////////////////////////////
119 /// TGraphAsymmErrors normal constructor.
120 ///
121 /// the arrays are preset to zero
122 
123 TGraphAsymmErrors::TGraphAsymmErrors(Int_t n)
124  : TGraph(n)
125 {
126  if (!CtorAllocate()) return;
127  FillZero(0, fNpoints);
128 }
129 
130 
131 ////////////////////////////////////////////////////////////////////////////////
132 /// TGraphAsymmErrors normal constructor.
133 ///
134 /// if exl,h or eyl,h are null, the corresponding arrays are preset to zero
135 
136 TGraphAsymmErrors::TGraphAsymmErrors(Int_t n, const Float_t *x, const Float_t *y, const Float_t *exl, const Float_t *exh, const Float_t *eyl, const Float_t *eyh)
137  : TGraph(n,x,y)
138 {
139  if (!CtorAllocate()) return;
140 
141  for (Int_t i=0;i<n;i++) {
142  if (exl) fEXlow[i] = exl[i];
143  else fEXlow[i] = 0;
144  if (exh) fEXhigh[i] = exh[i];
145  else fEXhigh[i] = 0;
146  if (eyl) fEYlow[i] = eyl[i];
147  else fEYlow[i] = 0;
148  if (eyh) fEYhigh[i] = eyh[i];
149  else fEYhigh[i] = 0;
150  }
151 }
152 
153 
154 ////////////////////////////////////////////////////////////////////////////////
155 /// TGraphAsymmErrors normal constructor.
156 ///
157 /// if exl,h or eyl,h are null, the corresponding arrays are preset to zero
158 
159 TGraphAsymmErrors::TGraphAsymmErrors(Int_t n, const Double_t *x, const Double_t *y, const Double_t *exl, const Double_t *exh, const Double_t *eyl, const Double_t *eyh)
160  : TGraph(n,x,y)
161 {
162  if (!CtorAllocate()) return;
163 
164  n = fNpoints*sizeof(Double_t);
165  if(exl) { memcpy(fEXlow, exl, n);
166  } else { memset(fEXlow, 0, n); }
167  if(exh) { memcpy(fEXhigh, exh, n);
168  } else { memset(fEXhigh, 0, n); }
169  if(eyl) { memcpy(fEYlow, eyl, n);
170  } else { memset(fEYlow, 0, n); }
171  if(eyh) { memcpy(fEYhigh, eyh, n);
172  } else { memset(fEYhigh, 0, n); }
173 }
174 
175 
176 ////////////////////////////////////////////////////////////////////////////////
177 /// Constructor with six vectors of floats in input
178 /// A grapherrors is built with the X coordinates taken from vx and Y coord from vy
179 /// and the errors from vectors vexl/h and veyl/h.
180 /// The number of points in the graph is the minimum of number of points
181 /// in vx and vy.
182 
183 TGraphAsymmErrors::TGraphAsymmErrors(const TVectorF &vx, const TVectorF &vy, const TVectorF &vexl, const TVectorF &vexh, const TVectorF &veyl, const TVectorF &veyh)
184  :TGraph()
185 {
186  fNpoints = TMath::Min(vx.GetNrows(), vy.GetNrows());
187  if (!TGraph::CtorAllocate()) return;
188  if (!CtorAllocate()) return;
189  Int_t ivxlow = vx.GetLwb();
190  Int_t ivylow = vy.GetLwb();
191  Int_t ivexllow = vexl.GetLwb();
192  Int_t ivexhlow = vexh.GetLwb();
193  Int_t iveyllow = veyl.GetLwb();
194  Int_t iveyhlow = veyh.GetLwb();
195  for (Int_t i=0;i<fNpoints;i++) {
196  fX[i] = vx(i+ivxlow);
197  fY[i] = vy(i+ivylow);
198  fEXlow[i] = vexl(i+ivexllow);
199  fEYlow[i] = veyl(i+iveyllow);
200  fEXhigh[i] = vexh(i+ivexhlow);
201  fEYhigh[i] = veyh(i+iveyhlow);
202  }
203 }
204 
205 
206 ////////////////////////////////////////////////////////////////////////////////
207 /// Constructor with six vectors of doubles in input
208 /// A grapherrors is built with the X coordinates taken from vx and Y coord from vy
209 /// and the errors from vectors vexl/h and veyl/h.
210 /// The number of points in the graph is the minimum of number of points
211 /// in vx and vy.
212 
213 TGraphAsymmErrors::TGraphAsymmErrors(const TVectorD &vx, const TVectorD &vy, const TVectorD &vexl, const TVectorD &vexh, const TVectorD &veyl, const TVectorD &veyh)
214  :TGraph()
215 {
216  fNpoints = TMath::Min(vx.GetNrows(), vy.GetNrows());
217  if (!TGraph::CtorAllocate()) return;
218  if (!CtorAllocate()) return;
219  Int_t ivxlow = vx.GetLwb();
220  Int_t ivylow = vy.GetLwb();
221  Int_t ivexllow = vexl.GetLwb();
222  Int_t ivexhlow = vexh.GetLwb();
223  Int_t iveyllow = veyl.GetLwb();
224  Int_t iveyhlow = veyh.GetLwb();
225  for (Int_t i=0;i<fNpoints;i++) {
226  fX[i] = vx(i+ivxlow);
227  fY[i] = vy(i+ivylow);
228  fEXlow[i] = vexl(i+ivexllow);
229  fEYlow[i] = veyl(i+iveyllow);
230  fEXhigh[i] = vexh(i+ivexhlow);
231  fEYhigh[i] = veyh(i+iveyhlow);
232  }
233 }
234 
235 
236 ////////////////////////////////////////////////////////////////////////////////
237 /// TGraphAsymmErrors constructor importing its parameters from the TH1 object passed as argument
238 /// the low and high errors are set to the bin error of the histogram.
239 
240 TGraphAsymmErrors::TGraphAsymmErrors(const TH1 *h)
241  : TGraph(h)
242 {
243  if (!CtorAllocate()) return;
244 
245  for (Int_t i=0;i<fNpoints;i++) {
246  fEXlow[i] = h->GetBinWidth(i+1)*gStyle->GetErrorX();
247  fEXhigh[i] = fEXlow[i];
248  fEYlow[i] = h->GetBinErrorLow(i+1);
249  fEYhigh[i] = h->GetBinErrorUp(i+1);;
250  }
251 }
252 
253 
254 ////////////////////////////////////////////////////////////////////////////////
255 /// Creates a TGraphAsymmErrors by dividing two input TH1 histograms:
256 /// pass/total. (see TGraphAsymmErrors::Divide)
257 
258 TGraphAsymmErrors::TGraphAsymmErrors(const TH1* pass, const TH1* total, Option_t *option)
259  : TGraph((pass)?pass->GetNbinsX():0)
260 {
261  if (!pass || !total) {
262  Error("TGraphAsymmErrors","Invalid histogram pointers");
263  return;
264  }
265  if (!CtorAllocate()) return;
266 
267  std::string sname = "divide_" + std::string(pass->GetName()) + "_by_" +
268  std::string(total->GetName());
269  SetName(sname.c_str());
270  SetTitle(pass->GetTitle());
271 
272  //copy style from pass
273  pass->TAttLine::Copy(*this);
274  pass->TAttFill::Copy(*this);
275  pass->TAttMarker::Copy(*this);
276 
277  Divide(pass, total, option);
278 }
279 
280 
281 ////////////////////////////////////////////////////////////////////////////////
282 /// TGraphAsymmErrors constructor reading input from filename
283 /// filename is assumed to contain at least 2 columns of numbers
284 ///
285 /// convention for format (default=`"%lg %lg %lg %lg %lg %lg"`)
286 /// - format = `"%lg %lg"` read only 2 first columns into X, Y
287 /// - format = `"%lg %lg %lg %lg"` read only 4 first columns into X, Y, ELY, EHY
288 /// - format = `"%lg %lg %lg %lg %lg %lg"` read only 6 first columns into X, Y, EXL, EYH, EYL, EHY
289 ///
290 /// For files separated by a specific delimiter different from `' '` and `'\t'` (e.g. `';'` in csv files)
291 /// you can avoid using `%*s` to bypass this delimiter by explicitly specify the `"option" argument,
292 /// e.g. `option=" \t,;"` for columns of figures separated by any of these characters `(' ', '\t', ',', ';')`
293 /// used once `(e.g. "1;1")` or in a combined way `(" 1;,;; 1")`.
294 /// Note in that case, the instantiation is about 2 times slower.
295 /// In case a delimiter is specified, the format `"%lg %lg %lg"` will read X,Y,EX.
296 
297 TGraphAsymmErrors::TGraphAsymmErrors(const char *filename, const char *format, Option_t *option)
298  : TGraph(100)
299 {
300  if (!CtorAllocate()) return;
301  Double_t x, y, exl, exh, eyl, eyh;
302  TString fname = filename;
303  gSystem->ExpandPathName(fname);
304  std::ifstream infile(fname.Data());
305  if (!infile.good()) {
306  MakeZombie();
307  Error("TGraphErrors", "Cannot open file: %s, TGraphErrors is Zombie", filename);
308  fNpoints = 0;
309  return;
310  }
311  std::string line;
312  Int_t np = 0;
313 
314  if (strcmp(option, "") == 0) { // No delimiters specified (standard constructor).
315 
316  Int_t ncol = TGraphErrors::CalculateScanfFields(format); //count number of columns in format
317  Int_t res;
318  while (std::getline(infile, line, '\n')) {
319  exl = exh = eyl = eyh = 0;
320  if (ncol < 3) {
321  res = sscanf(line.c_str(), format, &x, &y);
322  } else if (ncol < 5) {
323  res = sscanf(line.c_str(), format, &x, &y, &eyl, &eyh);
324  } else {
325  res = sscanf(line.c_str(), format, &x, &y, &exl, &exh, &eyl, &eyh);
326  }
327  if (res < 2) {
328  continue; //skip empty and ill-formed lines
329  }
330  SetPoint(np, x, y);
331  SetPointError(np, exl, exh, eyl, eyh);
332  np++;
333  }
334  Set(np);
335 
336  } else { // A delimiter has been specified in "option"
337 
338  // Checking format and creating its boolean equivalent
339  TString format_ = TString(format) ;
340  format_.ReplaceAll(" ", "") ;
341  format_.ReplaceAll("\t", "") ;
342  format_.ReplaceAll("lg", "") ;
343  format_.ReplaceAll("s", "") ;
344  format_.ReplaceAll("%*", "0") ;
345  format_.ReplaceAll("%", "1") ;
346  if (!format_.IsDigit()) {
347  Error("TGraphAsymmErrors", "Incorrect input format! Allowed format tags are {\"%%lg\",\"%%*lg\" or \"%%*s\"}");
348  return ;
349  }
350  Int_t ntokens = format_.Length() ;
351  if (ntokens < 2) {
352  Error("TGraphAsymmErrors", "Incorrect input format! Only %d tag(s) in format whereas at least 2 \"%%lg\" tags are expected!", ntokens);
353  return ;
354  }
355  Int_t ntokensToBeSaved = 0 ;
356  Bool_t * isTokenToBeSaved = new Bool_t [ntokens] ;
357  for (Int_t idx = 0; idx < ntokens; idx++) {
358  isTokenToBeSaved[idx] = TString::Format("%c", format_[idx]).Atoi() ; //atoi(&format_[idx]) does not work for some reason...
359  if (isTokenToBeSaved[idx] == 1) {
360  ntokensToBeSaved++ ;
361  }
362  }
363  if (ntokens >= 2 && (ntokensToBeSaved < 2 || ntokensToBeSaved > 4)) { //first condition not to repeat the previous error message
364  Error("TGraphAsymmErrors", "Incorrect input format! There are %d \"%%lg\" tag(s) in format whereas 2,3 or 4 are expected!", ntokensToBeSaved);
365  delete [] isTokenToBeSaved ;
366  return ;
367  }
368 
369  // Initializing loop variables
370  Bool_t isLineToBeSkipped = kFALSE ; //empty and ill-formed lines
371  char * token = NULL ;
372  TString token_str = "" ;
373  Int_t token_idx = 0 ;
374  Double_t * value = new Double_t [6] ; //x,y,exl, exh, eyl, eyh buffers
375  for (Int_t k = 0; k < 6; k++) {
376  value[k] = 0. ;
377  }
378  Int_t value_idx = 0 ;
379 
380  // Looping
381  char *rest;
382  while (std::getline(infile, line, '\n')) {
383  if (line != "") {
384  if (line[line.size() - 1] == char(13)) { // removing DOS CR character
385  line.erase(line.end() - 1, line.end()) ;
386  }
387  token = R__STRTOK_R(const_cast<char*>(line.c_str()), option, &rest) ;
388  while (token != NULL && value_idx < ntokensToBeSaved) {
389  if (isTokenToBeSaved[token_idx]) {
390  token_str = TString(token) ;
391  token_str.ReplaceAll("\t", "") ;
392  if (!token_str.IsFloat()) {
393  isLineToBeSkipped = kTRUE ;
394  break ;
395  } else {
396  value[value_idx] = token_str.Atof() ;
397  value_idx++ ;
398  }
399  }
400  token = R__STRTOK_R(NULL, option, &rest); // next token
401  token_idx++ ;
402  }
403  if (!isLineToBeSkipped && value_idx > 1) { //i.e. 2,3 or 4
404  x = value[0] ;
405  y = value[1] ;
406  exl = value[2] ;
407  exh = value[3] ;
408  eyl = value[4] ;
409  eyh = value[5] ;
410  SetPoint(np, x, y) ;
411  SetPointError(np, exl, exh, eyl, eyh);
412  np++ ;
413  }
414  }
415  isLineToBeSkipped = kFALSE ;
416  token = NULL ;
417  token_idx = 0 ;
418  value_idx = 0 ;
419  }
420  Set(np) ;
421 
422  // Cleaning
423  delete [] isTokenToBeSaved ;
424  delete [] value ;
425  delete token ;
426  }
427  infile.close();
428 }
429 
430 ////////////////////////////////////////////////////////////////////////////////
431 /// TGraphAsymmErrors default destructor.
432 
433 TGraphAsymmErrors::~TGraphAsymmErrors()
434 {
435  if(fEXlow) delete [] fEXlow;
436  if(fEXhigh) delete [] fEXhigh;
437  if(fEYlow) delete [] fEYlow;
438  if(fEYhigh) delete [] fEYhigh;
439 }
440 
441 ////////////////////////////////////////////////////////////////////////////////
442 /// Allocate internal data structures for `size` points.
443 
444 Double_t** TGraphAsymmErrors::Allocate(Int_t size) {
445  return AllocateArrays(6, size);
446 }
447 
448 ////////////////////////////////////////////////////////////////////////////////
449 /// Apply a function to all data points `y = f(x,y)`
450 ///
451 /// Errors are calculated as `eyh = f(x,y+eyh)-f(x,y)` and
452 /// `eyl = f(x,y)-f(x,y-eyl)`
453 ///
454 /// Special treatment has to be applied for the functions where the
455 /// role of "up" and "down" is reversed.
456 /// function suggested/implemented by Miroslav Helbich <helbich@mail.desy.de>
457 
458 void TGraphAsymmErrors::Apply(TF1 *f)
459 {
460  Double_t x,y,exl,exh,eyl,eyh,eyl_new,eyh_new,fxy;
461 
462  if (fHistogram) {
463  delete fHistogram;
464  fHistogram = 0;
465  }
466  for (Int_t i=0;i<GetN();i++) {
467  GetPoint(i,x,y);
468  exl=GetErrorXlow(i);
469  exh=GetErrorXhigh(i);
470  eyl=GetErrorYlow(i);
471  eyh=GetErrorYhigh(i);
472 
473  fxy = f->Eval(x,y);
474  SetPoint(i,x,fxy);
475 
476  // in the case of the functions like y-> -1*y the roles of the
477  // upper and lower error bars is reversed
478  if (f->Eval(x,y-eyl)<f->Eval(x,y+eyh)) {
479  eyl_new = TMath::Abs(fxy - f->Eval(x,y-eyl));
480  eyh_new = TMath::Abs(f->Eval(x,y+eyh) - fxy);
481  }
482  else {
483  eyh_new = TMath::Abs(fxy - f->Eval(x,y-eyl));
484  eyl_new = TMath::Abs(f->Eval(x,y+eyh) - fxy);
485  }
486 
487  //error on x doesn't change
488  SetPointError(i,exl,exh,eyl_new,eyh_new);
489  }
490  if (gPad) gPad->Modified();
491 }
492 
493 ////////////////////////////////////////////////////////////////////////////////
494 ///This function is only kept for backward compatibility.
495 ///You should rather use the Divide method.
496 ///It calls `Divide(pass,total,"cl=0.683 b(1,1) mode")` which is equivalent to the
497 ///former BayesDivide method.
498 
499 void TGraphAsymmErrors::BayesDivide(const TH1* pass, const TH1* total, Option_t *)
500 {
501  Divide(pass,total,"cl=0.683 b(1,1) mode");
502 }
503 
504 ////////////////////////////////////////////////////////////////////////////////
505 /// Fill this TGraphAsymmErrors by dividing two 1-dimensional histograms pass/total
506 ///
507 /// This method serves two purposes:
508 ///
509 /// ### 1) calculating efficiencies:
510 ///
511 /// The assumption is that the entries in "pass" are a subset of those in
512 /// "total". That is, we create an "efficiency" graph, where each entry is
513 /// between 0 and 1, inclusive.
514 ///
515 /// If the histograms are not filled with unit weights, the number of effective
516 /// entries is used to normalise the bin contents which might lead to wrong results.
517 /// \f[
518 /// \text{effective entries} = \frac{(\sum w_{i})^{2}}{\sum w_{i}^{2}}
519 /// \f]
520 /// The points are assigned a x value at the center of each histogram bin.
521 /// The y values are \f$\text{eff} = \frac{\text{pass}}{\text{total}}\f$
522 /// for all options except for the
523 /// bayesian methods where the result depends on the chosen option.
524 ///
525 /// If the denominator becomes 0 or pass > total, the corresponding bin is
526 /// skipped.
527 ///
528 /// ### 2) calculating ratios of two Poisson means (option 'pois'):
529 ///
530 /// The two histograms are interpreted as independent Poisson processes and the ratio
531 /// \f[
532 /// \tau = \frac{n_{1}}{n_{2}} = \frac{\varepsilon}{1 - \varepsilon}
533 /// \f]
534 /// with \f$\varepsilon = \frac{n_{1}}{n_{1} + n_{2}}\f$.
535 /// The histogram 'pass' is interpreted as \f$n_{1}\f$ and the total histogram
536 /// is used for \f$n_{2}\f$.
537 ///
538 /// The (asymmetric) uncertainties of the Poisson ratio are linked to the uncertainties
539 /// of efficiency by a parameter transformation:
540 /// \f[
541 /// \Delta \tau_{low/up} = \frac{1}{(1 - \varepsilon)^{2}} \Delta \varepsilon_{low/up}
542 /// \f]
543 ///
544 /// The x errors span each histogram bin (lowedge ... lowedge+width)
545 /// The y errors depend on the chosen statistic methode which can be determined
546 /// by the options given below. For a detailed description of the used statistic
547 /// calculations please have a look at the corresponding functions!
548 ///
549 /// Options:
550 /// - v : verbose mode: prints information about the number of used bins
551 /// and calculated efficiencies with their errors
552 /// - cl=x : determine the used confidence level (0<x<1) (default is 0.683)
553 /// - cp : Clopper-Pearson interval (see TEfficiency::ClopperPearson)
554 /// - w : Wilson interval (see TEfficiency::Wilson)
555 /// - n : normal approximation propagation (see TEfficiency::Normal)
556 /// - ac : Agresti-Coull interval (see TEfficiency::AgrestiCoull)
557 /// - fc : Feldman-Cousins interval (see TEfficiency::FeldmanCousinsInterval)
558 /// - midp : Lancaster mid-P interval (see TEfficiency::MidPInterval)
559 /// - b(a,b): bayesian interval using a prior probability ~Beta(a,b); a,b > 0
560 /// (see TEfficiency::Bayesian)
561 /// - mode : use mode of posterior for Bayesian interval (default is mean)
562 /// - shortest: use shortest interval (done by default if mode is set)
563 /// - central: use central interval (done by default if mode is NOT set)
564 /// - pois: interpret histograms as poisson ratio instead of efficiency
565 /// - e0 : plot efficiency and interval for bins where total=0
566 /// (default is to skip them)
567 ///
568 /// Note:
569 /// Unfortunately there is no straightforward approach for determining a confidence
570 /// interval for a given confidence level. The actual coverage probability of the
571 /// confidence interval oscillates significantly according to the total number of
572 /// events and the true efficiency. In order to decrease the impact of this
573 /// oscillation on the actual coverage probability a couple of approximations and
574 /// methodes has been developed. For a detailed discussion, please have a look at
575 /// this statistical paper:
576 /// http://www-stat.wharton.upenn.edu/~tcai/paper/Binomial-StatSci.pdf
577 
578 
579 void TGraphAsymmErrors::Divide(const TH1* pass, const TH1* total, Option_t *opt)
580 {
581  //check pointers
582  if(!pass || !total) {
583  Error("Divide","one of the passed pointers is zero");
584  return;
585  }
586 
587  //check dimension of histograms; only 1-dimensional ones are accepted
588  if((pass->GetDimension() > 1) || (total->GetDimension() > 1)) {
589  Error("Divide","passed histograms are not one-dimensional");
590  return;
591  }
592 
593  //check whether histograms are filled with weights -> use number of effective
594  //entries
595  Bool_t bEffective = false;
596  //compare sum of weights with sum of squares of weights
597  // re-compute here to be sure to get the right values
598  Double_t psumw = 0;
599  Double_t psumw2 = 0;
600  if (pass->GetSumw2()->fN > 0) {
601  for (int i = 0; i < pass->GetNcells(); ++i) {
602  psumw += pass->GetBinContent(i);
603  psumw2 += pass->GetSumw2()->At(i);
604  }
605  }
606  else {
607  psumw = pass->GetSumOfWeights();
608  psumw2 = psumw;
609  }
610  if (TMath::Abs(psumw - psumw2) > 1e-6)
611  bEffective = true;
612 
613  Double_t tsumw = 0;
614  Double_t tsumw2 = 0;
615  if (total->GetSumw2()->fN > 0) {
616  for (int i = 0; i < total->GetNcells(); ++i) {
617  tsumw += total->GetBinContent(i);
618  tsumw2 += total->GetSumw2()->At(i);
619  }
620  }
621  else {
622  tsumw = total->GetSumOfWeights();
623  tsumw2 = tsumw;
624  }
625  if (TMath::Abs(tsumw - tsumw2) > 1e-6)
626  bEffective = true;
627 
628 
629 
630  // we do not want to ignore the weights
631  // if (bEffective && (pass->GetSumw2()->fN == 0 || total->GetSumw2()->fN == 0) ) {
632  // Warning("Divide","histogram have been computed with weights but the sum of weight squares are not stored in the histogram. Error calculation is performed ignoring the weights");
633  // bEffective = false;
634  // }
635 
636  //parse option
637  TString option = opt;
638  option.ToLower();
639 
640  Bool_t bVerbose = false;
641  //pointer to function returning the boundaries of the confidence interval
642  //(is only used in the frequentist cases.)
643  Double_t (*pBound)(Double_t,Double_t,Double_t,Bool_t) = &TEfficiency::ClopperPearson; // default method
644  //confidence level
645  Double_t conf = 0.682689492137;
646  //values for bayesian statistics
647  Bool_t bIsBayesian = false;
648  Double_t alpha = 1;
649  Double_t beta = 1;
650 
651  //verbose mode
652  if(option.Contains("v")) {
653  option.ReplaceAll("v","");
654  bVerbose = true;
655  if (bEffective)
656  Info("Divide","weight will be considered in the Histogram Ratio");
657  }
658 
659 
660  //confidence level
661  if(option.Contains("cl=")) {
662  Double_t level = -1;
663  // coverity [secure_coding : FALSE]
664  sscanf(strstr(option.Data(),"cl="),"cl=%lf",&level);
665  if((level > 0) && (level < 1))
666  conf = level;
667  else
668  Warning("Divide","given confidence level %.3lf is invalid",level);
669  option.ReplaceAll("cl=","");
670  }
671 
672  // look for statistic options
673  // check first Bayesian case
674  // bayesian with prior
675  Bool_t usePosteriorMode = false;
676  Bool_t useShortestInterval = false;
677  if (option.Contains("b(")) {
678  Double_t a = 0;
679  Double_t b = 0;
680  sscanf(strstr(option.Data(), "b("), "b(%lf,%lf)", &a, &b);
681  if (a > 0)
682  alpha = a;
683  else
684  Warning("Divide", "given shape parameter for alpha %.2lf is invalid", a);
685  if (b > 0)
686  beta = b;
687  else
688  Warning("Divide", "given shape parameter for beta %.2lf is invalid", b);
689  option.ReplaceAll("b(", "");
690  bIsBayesian = true;
691 
692  // look for specific bayesian options
693 
694  // use posterior mode
695 
696  if (option.Contains("mode")) {
697  usePosteriorMode = true;
698  option.ReplaceAll("mode", "");
699  }
700  if (option.Contains("sh") || (usePosteriorMode && !option.Contains("cen"))) {
701  useShortestInterval = true;
702  }
703  }
704  // normal approximation
705  else if (option.Contains("n")) {
706  option.ReplaceAll("n", "");
707  pBound = &TEfficiency::Normal;
708  }
709  // clopper pearson interval
710  else if (option.Contains("cp")) {
711  option.ReplaceAll("cp", "");
712  pBound = &TEfficiency::ClopperPearson;
713  }
714  // wilson interval
715  else if (option.Contains("w")) {
716  option.ReplaceAll("w", "");
717  pBound = &TEfficiency::Wilson;
718  }
719  // agresti coull interval
720  else if (option.Contains("ac")) {
721  option.ReplaceAll("ac", "");
722  pBound = &TEfficiency::AgrestiCoull;
723  }
724  // Feldman-Cousins interval
725  else if (option.Contains("fc")) {
726  option.ReplaceAll("fc", "");
727  pBound = &TEfficiency::FeldmanCousins;
728  }
729  // mid-P Lancaster interval
730  else if (option.Contains("midp")) {
731  option.ReplaceAll("midp", "");
732  pBound = &TEfficiency::MidPInterval;
733  }
734 
735  // interpret as Poisson ratio
736  Bool_t bPoissonRatio = false;
737  if (option.Contains("pois")) {
738  bPoissonRatio = true;
739  option.ReplaceAll("pois", "");
740  }
741  Bool_t plot0Bins = false;
742  if (option.Contains("e0")) {
743  plot0Bins = true;
744  option.ReplaceAll("e0", "");
745  }
746 
747  // weights works only in case of Normal approximation or Bayesian for binomial interval
748  // in case of Poisson ratio we can use weights by rescaling the obtained results using the effective entries
749  if ((bEffective && !bPoissonRatio) && !bIsBayesian && pBound != &TEfficiency::Normal) {
750  Warning("Divide", "Histograms have weights: only Normal or Bayesian error calculation is supported");
751  Info("Divide", "Using now the Normal approximation for weighted histograms");
752  }
753 
754  if (bPoissonRatio) {
755  if (pass->GetDimension() != total->GetDimension()) {
756  Error("Divide", "passed histograms are not of the same dimension");
757  return;
758  }
759 
760  if (!TEfficiency::CheckBinning(*pass, *total)) {
761  Error("Divide", "passed histograms are not consistent");
762  return;
763  }
764  } else {
765  // check consistency of histograms, allowing weights
766  if (!TEfficiency::CheckConsistency(*pass, *total, "w")) {
767  Error("Divide", "passed histograms are not consistent");
768  return;
769  }
770  }
771 
772  // Set the graph to have a number of points equal to the number of histogram
773  // bins
774  Int_t nbins = pass->GetNbinsX();
775  Set(nbins);
776 
777  // Ok, now set the points for each bin
778  // (Note: the TH1 bin content is shifted to the right by one:
779  // bin=0 is underflow, bin=nbins+1 is overflow.)
780 
781  //efficiency with lower and upper boundary of confidence interval
782  double eff, low, upper;
783  //this keeps track of the number of points added to the graph
784  int npoint=0;
785  //number of total and passed events
786  Double_t t = 0 , p = 0;
787  Double_t tw = 0, tw2 = 0, pw = 0, pw2 = 0, wratio = 1; // for the case of weights
788  //loop over all bins and fill the graph
789  for (Int_t b=1; b<=nbins; ++b) {
790 
791  // default value when total =0;
792  eff = 0;
793  low = 0;
794  upper = 0;
795 
796  // special case in case of weights we have to consider the sum of weights and the sum of weight squares
797  if (bEffective) {
798  tw = total->GetBinContent(b);
799  tw2 = (total->GetSumw2()->fN > 0) ? total->GetSumw2()->At(b) : tw;
800  pw = pass->GetBinContent(b);
801  pw2 = (pass->GetSumw2()->fN > 0) ? pass->GetSumw2()->At(b) : pw;
802 
803  if (bPoissonRatio) {
804  // tw += pw;
805  // tw2 += pw2;
806  // compute ratio on the effective entries ( p and t)
807  // special case is when (pw=0, pw2=0) in this case we cannot get the bin weight.
808  // we use then the overall weight of the full histogram
809  if (pw == 0 && pw2 == 0)
810  p = 0;
811  else
812  p = (pw * pw) / pw2;
813 
814  if (tw == 0 && tw2 == 0)
815  t = 0;
816  else
817  t = (tw * tw) / tw2;
818 
819  if (pw > 0 && tw > 0)
820  // this is the ratio of the two bin weights ( pw/p / t/tw )
821  wratio = (pw * t) / (p * tw);
822  else if (pw == 0 && tw > 0)
823  // case p histogram has zero compute the weights from all the histogram
824  // weight of histogram - sumw2/sumw
825  wratio = (psumw2 * t) / (psumw * tw);
826  else if (tw == 0 && pw > 0)
827  // case t histogram has zero compute the weights from all the histogram
828  // weight of histogram - sumw2/sumw
829  wratio = (pw * tsumw) / (p * tsumw2);
830  else if (p > 0)
831  wratio = pw / p; // not sure if needed
832  else {
833  // case both pw and tw are zero - we skip these bins
834  if (!plot0Bins) continue; // skip bins with total <= 0
835  }
836 
837  t += p;
838  // std::cout << p << " " << t << " " << wratio << std::endl;
839  } else if (tw <= 0 && !plot0Bins)
840  continue; // skip bins with total <= 0
841 
842  // in the case of weights have the formula only for
843  // the normal and bayesian statistics (see below)
844 
845  }
846 
847  // use bin contents
848  else {
849  t = std::round(total->GetBinContent(b));
850  p = std::round(pass->GetBinContent(b));
851 
852  if (bPoissonRatio)
853  t += p;
854 
855  if (t == 0.0 && !plot0Bins)
856  continue; // skip bins with total = 0
857  }
858 
859  //using bayesian statistics
860  if(bIsBayesian) {
861  double aa,bb;
862 
863  if ((bEffective && !bPoissonRatio) && tw2 <= 0) {
864  // case of bins with zero errors
865  eff = pw/tw;
866  low = eff; upper = eff;
867  }
868  else {
869 
870  if (bEffective && !bPoissonRatio) {
871  // tw/tw2 re-normalize the weights
872  double norm = tw/tw2; // case of tw2 = 0 is treated above
873  aa = pw * norm + alpha;
874  bb = (tw - pw) * norm + beta;
875  }
876  else {
877  aa = double(p) + alpha;
878  bb = double(t-p) + beta;
879  }
880  if (usePosteriorMode)
881  eff = TEfficiency::BetaMode(aa,bb);
882  else
883  eff = TEfficiency::BetaMean(aa,bb);
884 
885  if (useShortestInterval) {
886  TEfficiency::BetaShortestInterval(conf,aa,bb,low,upper);
887  }
888  else {
889  low = TEfficiency::BetaCentralInterval(conf,aa,bb,false);
890  upper = TEfficiency::BetaCentralInterval(conf,aa,bb,true);
891  }
892  }
893  }
894  // case of non-bayesian statistics
895  else {
896  if (bEffective && !bPoissonRatio) {
897 
898  if (tw > 0) {
899 
900  eff = pw/tw;
901 
902  // use normal error calculation using variance of MLE with weights (F.James 8.5.2)
903  // this is the same formula used in ROOT for TH1::Divide("B")
904 
905  double variance = ( pw2 * (1. - 2 * eff) + tw2 * eff *eff ) / ( tw * tw) ;
906  double sigma = sqrt(variance);
907 
908  double prob = 0.5 * (1.-conf);
909  double delta = ROOT::Math::normal_quantile_c(prob, sigma);
910  low = eff - delta;
911  upper = eff + delta;
912  if (low < 0) low = 0;
913  if (upper > 1) upper = 1.;
914  }
915  }
916  else {
917  // when not using weights (all cases) or in case of Poisson ratio with weights
918  if(t != 0.0)
919  eff = ((Double_t)p)/t;
920 
921  low = pBound(t,p,conf,false);
922  upper = pBound(t,p,conf,true);
923  }
924  }
925  // treat as Poisson ratio
926  if(bPoissonRatio)
927  {
928  Double_t ratio = eff/(1 - eff);
929  // take the intervals in eff as intervals in the Poisson ratio
930  low = low/(1. - low);
931  upper = upper/(1.-upper);
932  eff = ratio;
933  if (bEffective) {
934  //scale result by the ratio of the weight
935  eff *= wratio;
936  low *= wratio;
937  upper *= wratio;
938  }
939  }
940  //Set the point center and its errors
941  if (TMath::Finite(eff)) {
942  SetPoint(npoint,pass->GetBinCenter(b),eff);
943  SetPointError(npoint,
944  pass->GetBinCenter(b)-pass->GetBinLowEdge(b),
945  pass->GetBinLowEdge(b)-pass->GetBinCenter(b)+pass->GetBinWidth(b),
946  eff-low,upper-eff);
947  npoint++;//we have added a point to the graph
948  }
949  }
950 
951  Set(npoint);//tell the graph how many points we've really added
952  if (npoint < nbins)
953  Warning("Divide","Number of graph points is different than histogram bins - %d points have been skipped",nbins-npoint);
954 
955 
956  if (bVerbose) {
957  Info("Divide","made a graph with %d points from %d bins",npoint,nbins);
958  Info("Divide","used confidence level: %.2lf\n",conf);
959  if(bIsBayesian)
960  Info("Divide","used prior probability ~ beta(%.2lf,%.2lf)",alpha,beta);
961  Print();
962  }
963 }
964 
965 ////////////////////////////////////////////////////////////////////////////////
966 /// Compute Range
967 
968 void TGraphAsymmErrors::ComputeRange(Double_t &xmin, Double_t &ymin, Double_t &xmax, Double_t &ymax) const
969 {
970  TGraph::ComputeRange(xmin,ymin,xmax,ymax);
971 
972  for (Int_t i=0;i<fNpoints;i++) {
973  if (fX[i] -fEXlow[i] < xmin) {
974  if (gPad && gPad->GetLogx()) {
975  if (fEXlow[i] < fX[i]) xmin = fX[i]-fEXlow[i];
976  else xmin = TMath::Min(xmin,fX[i]/3);
977  } else {
978  xmin = fX[i]-fEXlow[i];
979  }
980  }
981  if (fX[i] +fEXhigh[i] > xmax) xmax = fX[i]+fEXhigh[i];
982  if (fY[i] -fEYlow[i] < ymin) {
983  if (gPad && gPad->GetLogy()) {
984  if (fEYlow[i] < fY[i]) ymin = fY[i]-fEYlow[i];
985  else ymin = TMath::Min(ymin,fY[i]/3);
986  } else {
987  ymin = fY[i]-fEYlow[i];
988  }
989  }
990  if (fY[i] +fEYhigh[i] > ymax) ymax = fY[i]+fEYhigh[i];
991  }
992 }
993 
994 
995 ////////////////////////////////////////////////////////////////////////////////
996 /// Copy and release.
997 
998 void TGraphAsymmErrors::CopyAndRelease(Double_t **newarrays,
999  Int_t ibegin, Int_t iend, Int_t obegin)
1000 {
1001  CopyPoints(newarrays, ibegin, iend, obegin);
1002  if (newarrays) {
1003  delete[] fEXlow;
1004  fEXlow = newarrays[0];
1005  delete[] fEXhigh;
1006  fEXhigh = newarrays[1];
1007  delete[] fEYlow;
1008  fEYlow = newarrays[2];
1009  delete[] fEYhigh;
1010  fEYhigh = newarrays[3];
1011  delete[] fX;
1012  fX = newarrays[4];
1013  delete[] fY;
1014  fY = newarrays[5];
1015  delete[] newarrays;
1016  }
1017 }
1018 
1019 
1020 ////////////////////////////////////////////////////////////////////////////////
1021 /// Copy errors from fE*** to arrays[***]
1022 /// or to f*** Copy points.
1023 
1024 Bool_t TGraphAsymmErrors::CopyPoints(Double_t **arrays,
1025  Int_t ibegin, Int_t iend, Int_t obegin)
1026 {
1027  if (TGraph::CopyPoints(arrays ? arrays+4 : 0, ibegin, iend, obegin)) {
1028  Int_t n = (iend - ibegin)*sizeof(Double_t);
1029  if (arrays) {
1030  memmove(&arrays[0][obegin], &fEXlow[ibegin], n);
1031  memmove(&arrays[1][obegin], &fEXhigh[ibegin], n);
1032  memmove(&arrays[2][obegin], &fEYlow[ibegin], n);
1033  memmove(&arrays[3][obegin], &fEYhigh[ibegin], n);
1034  } else {
1035  memmove(&fEXlow[obegin], &fEXlow[ibegin], n);
1036  memmove(&fEXhigh[obegin], &fEXhigh[ibegin], n);
1037  memmove(&fEYlow[obegin], &fEYlow[ibegin], n);
1038  memmove(&fEYhigh[obegin], &fEYhigh[ibegin], n);
1039  }
1040  return kTRUE;
1041  } else {
1042  return kFALSE;
1043  }
1044 }
1045 
1046 
1047 ////////////////////////////////////////////////////////////////////////////////
1048 /// Should be called from ctors after fNpoints has been set
1049 /// Note: This function should be called only from the constructor
1050 /// since it does not delete previously existing arrays
1051 
1052 Bool_t TGraphAsymmErrors::CtorAllocate(void)
1053 {
1054  if (!fNpoints) {
1055  fEXlow = fEYlow = fEXhigh = fEYhigh = 0;
1056  return kFALSE;
1057  }
1058  fEXlow = new Double_t[fMaxSize];
1059  fEYlow = new Double_t[fMaxSize];
1060  fEXhigh = new Double_t[fMaxSize];
1061  fEYhigh = new Double_t[fMaxSize];
1062  return kTRUE;
1063 }
1064 
1065 ////////////////////////////////////////////////////////////////////////////////
1066 /// protected function to perform the merge operation of a graph with asymmetric errors
1067 
1068 Bool_t TGraphAsymmErrors::DoMerge(const TGraph *g)
1069 {
1070  if (g->GetN() == 0) return kFALSE;
1071 
1072  Double_t * exl = g->GetEXlow();
1073  Double_t * exh = g->GetEXhigh();
1074  Double_t * eyl = g->GetEYlow();
1075  Double_t * eyh = g->GetEYhigh();
1076  if (exl == 0 || exh == 0 || eyl == 0 || eyh == 0) {
1077  if (g->IsA() != TGraph::Class() )
1078  Warning("DoMerge","Merging a %s is not compatible with a TGraphAsymmErrors - errors will be ignored",g->IsA()->GetName());
1079  return TGraph::DoMerge(g);
1080  }
1081  for (Int_t i = 0 ; i < g->GetN(); i++) {
1082  Int_t ipoint = GetN();
1083  Double_t x = g->GetX()[i];
1084  Double_t y = g->GetY()[i];
1085  SetPoint(ipoint, x, y);
1086  SetPointError(ipoint, exl[i], exh[i], eyl[i], eyh[i] );
1087  }
1088 
1089  return kTRUE;
1090 }
1091 
1092 ////////////////////////////////////////////////////////////////////////////////
1093 /// Set zero values for point arrays in the range [begin, end)
1094 
1095 void TGraphAsymmErrors::FillZero(Int_t begin, Int_t end,
1096  Bool_t from_ctor)
1097 {
1098  if (!from_ctor) {
1099  TGraph::FillZero(begin, end, from_ctor);
1100  }
1101  Int_t n = (end - begin)*sizeof(Double_t);
1102  memset(fEXlow + begin, 0, n);
1103  memset(fEXhigh + begin, 0, n);
1104  memset(fEYlow + begin, 0, n);
1105  memset(fEYhigh + begin, 0, n);
1106 }
1107 
1108 
1109 ////////////////////////////////////////////////////////////////////////////////
1110 /// This function is called by GraphFitChisquare.
1111 /// It returns the error along X at point i.
1112 
1113 Double_t TGraphAsymmErrors::GetErrorX(Int_t i) const
1114 {
1115  if (i < 0 || i >= fNpoints) return -1;
1116  if (!fEXlow && !fEXhigh) return -1;
1117  Double_t elow=0, ehigh=0;
1118  if (fEXlow) elow = fEXlow[i];
1119  if (fEXhigh) ehigh = fEXhigh[i];
1120  return TMath::Sqrt(0.5*(elow*elow + ehigh*ehigh));
1121 }
1122 
1123 
1124 ////////////////////////////////////////////////////////////////////////////////
1125 /// This function is called by GraphFitChisquare.
1126 /// It returns the error along Y at point i.
1127 
1128 Double_t TGraphAsymmErrors::GetErrorY(Int_t i) const
1129 {
1130  if (i < 0 || i >= fNpoints) return -1;
1131  if (!fEYlow && !fEYhigh) return -1;
1132  Double_t elow=0, ehigh=0;
1133  if (fEYlow) elow = fEYlow[i];
1134  if (fEYhigh) ehigh = fEYhigh[i];
1135  return TMath::Sqrt(0.5*(elow*elow + ehigh*ehigh));
1136 }
1137 
1138 
1139 ////////////////////////////////////////////////////////////////////////////////
1140 /// Get high error on X.
1141 
1142 Double_t TGraphAsymmErrors::GetErrorXhigh(Int_t i) const
1143 {
1144  if (i<0 || i>fNpoints) return -1;
1145  if (fEXhigh) return fEXhigh[i];
1146  return -1;
1147 }
1148 
1149 
1150 ////////////////////////////////////////////////////////////////////////////////
1151 /// Get low error on X.
1152 
1153 Double_t TGraphAsymmErrors::GetErrorXlow(Int_t i) const
1154 {
1155  if (i<0 || i>fNpoints) return -1;
1156  if (fEXlow) return fEXlow[i];
1157  return -1;
1158 }
1159 
1160 
1161 ////////////////////////////////////////////////////////////////////////////////
1162 /// Get high error on Y.
1163 
1164 Double_t TGraphAsymmErrors::GetErrorYhigh(Int_t i) const
1165 {
1166  if (i<0 || i>fNpoints) return -1;
1167  if (fEYhigh) return fEYhigh[i];
1168  return -1;
1169 }
1170 
1171 
1172 ////////////////////////////////////////////////////////////////////////////////
1173 /// Get low error on Y.
1174 
1175 Double_t TGraphAsymmErrors::GetErrorYlow(Int_t i) const
1176 {
1177  if (i<0 || i>fNpoints) return -1;
1178  if (fEYlow) return fEYlow[i];
1179  return -1;
1180 }
1181 
1182 
1183 ////////////////////////////////////////////////////////////////////////////////
1184 /// Adds all graphs with asymmetric errors from the collection to this graph.
1185 /// Returns the total number of poins in the result or -1 in case of an error.
1186 
1187 Int_t TGraphAsymmErrors::Merge(TCollection* li)
1188 {
1189  TIter next(li);
1190  while (TObject* o = next()) {
1191  TGraph *g = dynamic_cast<TGraph*>(o);
1192  if (!g) {
1193  Error("Merge",
1194  "Cannot merge - an object which doesn't inherit from TGraph found in the list");
1195  return -1;
1196  }
1197  int n0 = GetN();
1198  int n1 = n0+g->GetN();
1199  Set(n1);
1200  Double_t * x = g->GetX();
1201  Double_t * y = g->GetY();
1202  Double_t * exlow = g->GetEXlow();
1203  Double_t * exhigh = g->GetEXhigh();
1204  Double_t * eylow = g->GetEYlow();
1205  Double_t * eyhigh = g->GetEYhigh();
1206  for (Int_t i = 0 ; i < g->GetN(); i++) {
1207  SetPoint(n0+i, x[i], y[i]);
1208  if (exlow) fEXlow[n0+i] = exlow[i];
1209  if (exhigh) fEXhigh[n0+i] = exhigh[i];
1210  if (eylow) fEYlow[n0+i] = eylow[i];
1211  if (eyhigh) fEYhigh[n0+i] = eyhigh[i];
1212  }
1213  }
1214  return GetN();
1215 }
1216 
1217 ////////////////////////////////////////////////////////////////////////////////
1218 /// Print graph and errors values.
1219 
1220 void TGraphAsymmErrors::Print(Option_t *) const
1221 {
1222  for (Int_t i=0;i<fNpoints;i++) {
1223  printf("x[%d]=%g, y[%d]=%g, exl[%d]=%g, exh[%d]=%g, eyl[%d]=%g, eyh[%d]=%g\n"
1224  ,i,fX[i],i,fY[i],i,fEXlow[i],i,fEXhigh[i],i,fEYlow[i],i,fEYhigh[i]);
1225  }
1226 }
1227 
1228 
1229 ////////////////////////////////////////////////////////////////////////////////
1230 /// Save primitive as a C++ statement(s) on output stream out
1231 
1232 void TGraphAsymmErrors::SavePrimitive(std::ostream &out, Option_t *option /*= ""*/)
1233 {
1234  char quote = '"';
1235  out << " " << std::endl;
1236  static Int_t frameNumber = 3000;
1237  frameNumber++;
1238 
1239  Int_t i;
1240  TString fXName = TString(GetName()) + Form("_fx%d",frameNumber);
1241  TString fYName = TString(GetName()) + Form("_fy%d",frameNumber);
1242  TString fElXName = TString(GetName()) + Form("_felx%d",frameNumber);
1243  TString fElYName = TString(GetName()) + Form("_fely%d",frameNumber);
1244  TString fEhXName = TString(GetName()) + Form("_fehx%d",frameNumber);
1245  TString fEhYName = TString(GetName()) + Form("_fehy%d",frameNumber);
1246  out << " Double_t " << fXName << "[" << fNpoints << "] = {" << std::endl;
1247  for (i = 0; i < fNpoints-1; i++) out << " " << fX[i] << "," << std::endl;
1248  out << " " << fX[fNpoints-1] << "};" << std::endl;
1249  out << " Double_t " << fYName << "[" << fNpoints << "] = {" << std::endl;
1250  for (i = 0; i < fNpoints-1; i++) out << " " << fY[i] << "," << std::endl;
1251  out << " " << fY[fNpoints-1] << "};" << std::endl;
1252  out << " Double_t " << fElXName << "[" << fNpoints << "] = {" << std::endl;
1253  for (i = 0; i < fNpoints-1; i++) out << " " << fEXlow[i] << "," << std::endl;
1254  out << " " << fEXlow[fNpoints-1] << "};" << std::endl;
1255  out << " Double_t " << fElYName << "[" << fNpoints << "] = {" << std::endl;
1256  for (i = 0; i < fNpoints-1; i++) out << " " << fEYlow[i] << "," << std::endl;
1257  out << " " << fEYlow[fNpoints-1] << "};" << std::endl;
1258  out << " Double_t " << fEhXName << "[" << fNpoints << "] = {" << std::endl;
1259  for (i = 0; i < fNpoints-1; i++) out << " " << fEXhigh[i] << "," << std::endl;
1260  out << " " << fEXhigh[fNpoints-1] << "};" << std::endl;
1261  out << " Double_t " << fEhYName << "[" << fNpoints << "] = {" << std::endl;
1262  for (i = 0; i < fNpoints-1; i++) out << " " << fEYhigh[i] << "," << std::endl;
1263  out << " " << fEYhigh[fNpoints-1] << "};" << std::endl;
1264 
1265  if (gROOT->ClassSaved(TGraphAsymmErrors::Class())) out<<" ";
1266  else out << " TGraphAsymmErrors *";
1267  out << "grae = new TGraphAsymmErrors("<< fNpoints << ","
1268  << fXName << "," << fYName << ","
1269  << fElXName << "," << fEhXName << ","
1270  << fElYName << "," << fEhYName << ");"
1271  << std::endl;
1272 
1273  out << " grae->SetName(" << quote << GetName() << quote << ");" << std::endl;
1274  out << " grae->SetTitle(" << quote << GetTitle() << quote << ");" << std::endl;
1275 
1276  SaveFillAttributes(out, "grae", 0, 1001);
1277  SaveLineAttributes(out, "grae", 1, 1, 1);
1278  SaveMarkerAttributes(out, "grae", 1, 1, 1);
1279 
1280  if (fHistogram) {
1281  TString hname = fHistogram->GetName();
1282  hname += frameNumber;
1283  fHistogram->SetName(Form("Graph_%s",hname.Data()));
1284  fHistogram->SavePrimitive(out,"nodraw");
1285  out<<" grae->SetHistogram("<<fHistogram->GetName()<<");"<<std::endl;
1286  out<<" "<<std::endl;
1287  }
1288 
1289  // save list of functions
1290  TIter next(fFunctions);
1291  TObject *obj;
1292  while ((obj = next())) {
1293  obj->SavePrimitive(out, Form("nodraw #%d\n",++frameNumber));
1294  if (obj->InheritsFrom("TPaveStats")) {
1295  out << " grae->GetListOfFunctions()->Add(ptstats);" << std::endl;
1296  out << " ptstats->SetParent(grae->GetListOfFunctions());" << std::endl;
1297  } else {
1298  TString objname;
1299  objname.Form("%s%d",obj->GetName(),frameNumber);
1300  if (obj->InheritsFrom("TF1")) {
1301  out << " " << objname << "->SetParent(grae);\n";
1302  }
1303  out << " grae->GetListOfFunctions()->Add("
1304  << objname << ");" << std::endl;
1305  }
1306  }
1307 
1308  const char *l = strstr(option,"multigraph");
1309  if (l) {
1310  out<<" multigraph->Add(grae,"<<quote<<l+10<<quote<<");"<<std::endl;
1311  } else {
1312  out<<" grae->Draw("<<quote<<option<<quote<<");"<<std::endl;
1313  }
1314 }
1315 
1316 ////////////////////////////////////////////////////////////////////////////////
1317 /// Set ex and ey values for point pointed by the mouse.
1318 
1319 void TGraphAsymmErrors::SetPointError(Double_t exl, Double_t exh, Double_t eyl, Double_t eyh)
1320 {
1321  Int_t px = gPad->GetEventX();
1322  Int_t py = gPad->GetEventY();
1323 
1324  //localize point to be deleted
1325  Int_t ipoint = -2;
1326  Int_t i;
1327  // start with a small window (in case the mouse is very close to one point)
1328  for (i=0;i<fNpoints;i++) {
1329  Int_t dpx = px - gPad->XtoAbsPixel(gPad->XtoPad(fX[i]));
1330  Int_t dpy = py - gPad->YtoAbsPixel(gPad->YtoPad(fY[i]));
1331  if (dpx*dpx+dpy*dpy < 25) {ipoint = i; break;}
1332  }
1333  if (ipoint == -2) return;
1334 
1335  fEXlow[ipoint] = exl;
1336  fEYlow[ipoint] = eyl;
1337  fEXhigh[ipoint] = exh;
1338  fEYhigh[ipoint] = eyh;
1339  gPad->Modified();
1340 }
1341 
1342 
1343 ////////////////////////////////////////////////////////////////////////////////
1344 /// Set ex and ey values for point number i.
1345 
1346 void TGraphAsymmErrors::SetPointError(Int_t i, Double_t exl, Double_t exh, Double_t eyl, Double_t eyh)
1347 {
1348  if (i < 0) return;
1349  if (i >= fNpoints) {
1350  // re-allocate the object
1351  TGraphAsymmErrors::SetPoint(i,0,0);
1352  }
1353  fEXlow[i] = exl;
1354  fEYlow[i] = eyl;
1355  fEXhigh[i] = exh;
1356  fEYhigh[i] = eyh;
1357 }
1358 
1359 
1360 ////////////////////////////////////////////////////////////////////////////////
1361 /// Set EXlow for point i
1362 
1363 void TGraphAsymmErrors::SetPointEXlow(Int_t i, Double_t exl)
1364 {
1365  if (i < 0) return;
1366  if (i >= fNpoints) {
1367  // re-allocate the object
1368  TGraphAsymmErrors::SetPoint(i,0,0);
1369  }
1370  fEXlow[i] = exl;
1371 }
1372 
1373 
1374 ////////////////////////////////////////////////////////////////////////////////
1375 /// Set EXhigh for point i
1376 
1377 void TGraphAsymmErrors::SetPointEXhigh(Int_t i, Double_t exh)
1378 {
1379  if (i < 0) return;
1380  if (i >= fNpoints) {
1381  // re-allocate the object
1382  TGraphAsymmErrors::SetPoint(i,0,0);
1383  }
1384  fEXhigh[i] = exh;
1385 }
1386 
1387 
1388 ////////////////////////////////////////////////////////////////////////////////
1389 /// Set EYlow for point i
1390 
1391 void TGraphAsymmErrors::SetPointEYlow(Int_t i, Double_t eyl)
1392 {
1393  if (i < 0) return;
1394  if (i >= fNpoints) {
1395  // re-allocate the object
1396  TGraphAsymmErrors::SetPoint(i,0,0);
1397  }
1398  fEYlow[i] = eyl;
1399 }
1400 
1401 
1402 ////////////////////////////////////////////////////////////////////////////////
1403 /// Set EYhigh for point i
1404 
1405 void TGraphAsymmErrors::SetPointEYhigh(Int_t i, Double_t eyh)
1406 {
1407  if (i < 0) return;
1408  if (i >= fNpoints) {
1409  // re-allocate the object
1410  TGraphAsymmErrors::SetPoint(i,0,0);
1411  }
1412  fEYhigh[i] = eyh;
1413 }
1414 
1415 
1416 ////////////////////////////////////////////////////////////////////////////////
1417 /// Stream an object of class TGraphAsymmErrors.
1418 
1419 void TGraphAsymmErrors::Streamer(TBuffer &b)
1420 {
1421  if (b.IsReading()) {
1422  UInt_t R__s, R__c;
1423  Version_t R__v = b.ReadVersion(&R__s, &R__c);
1424  if (R__v > 2) {
1425  b.ReadClassBuffer(TGraphAsymmErrors::Class(), this, R__v, R__s, R__c);
1426  return;
1427  }
1428  //====process old versions before automatic schema evolution
1429  TGraph::Streamer(b);
1430  fEXlow = new Double_t[fNpoints];
1431  fEYlow = new Double_t[fNpoints];
1432  fEXhigh = new Double_t[fNpoints];
1433  fEYhigh = new Double_t[fNpoints];
1434  if (R__v < 2) {
1435  Float_t *exlow = new Float_t[fNpoints];
1436  Float_t *eylow = new Float_t[fNpoints];
1437  Float_t *exhigh = new Float_t[fNpoints];
1438  Float_t *eyhigh = new Float_t[fNpoints];
1439  b.ReadFastArray(exlow,fNpoints);
1440  b.ReadFastArray(eylow,fNpoints);
1441  b.ReadFastArray(exhigh,fNpoints);
1442  b.ReadFastArray(eyhigh,fNpoints);
1443  for (Int_t i=0;i<fNpoints;i++) {
1444  fEXlow[i] = exlow[i];
1445  fEYlow[i] = eylow[i];
1446  fEXhigh[i] = exhigh[i];
1447  fEYhigh[i] = eyhigh[i];
1448  }
1449  delete [] eylow;
1450  delete [] exlow;
1451  delete [] eyhigh;
1452  delete [] exhigh;
1453  } else {
1454  b.ReadFastArray(fEXlow,fNpoints);
1455  b.ReadFastArray(fEYlow,fNpoints);
1456  b.ReadFastArray(fEXhigh,fNpoints);
1457  b.ReadFastArray(fEYhigh,fNpoints);
1458  }
1459  b.CheckByteCount(R__s, R__c, TGraphAsymmErrors::IsA());
1460  //====end of old versions
1461 
1462  } else {
1463  b.WriteClassBuffer(TGraphAsymmErrors::Class(),this);
1464  }
1465 }
1466 
1467 
1468 ////////////////////////////////////////////////////////////////////////////////
1469 /// Swap points.
1470 
1471 void TGraphAsymmErrors::SwapPoints(Int_t pos1, Int_t pos2)
1472 {
1473  SwapValues(fEXlow, pos1, pos2);
1474  SwapValues(fEXhigh, pos1, pos2);
1475  SwapValues(fEYlow, pos1, pos2);
1476  SwapValues(fEYhigh, pos1, pos2);
1477  TGraph::SwapPoints(pos1, pos2);
1478 }