From 1a78b9cebf20130f317821a4be4be8f54e95e011 Mon Sep 17 00:00:00 2001 From: Lorenzo Moneta <Lorenzo.Moneta@cern.ch> Date: Wed, 7 May 2014 16:36:27 +0200 Subject: [PATCH] Implement correctly the projections from a TProfile2D to TProfile and from a TProfile3D to a TProfile2D. Add corresponding tests in stressHistogram. This fixes bug ROOT-6220 --- hist/hist/inc/TH3.h | 23 ++-- hist/hist/inc/TProfile2D.h | 4 + hist/hist/inc/TProfile3D.h | 7 +- hist/hist/src/TH2.cxx | 2 +- hist/hist/src/TProfile.cxx | 16 ++- hist/hist/src/TProfile2D.cxx | 152 ++++++++++++++++++++++---- hist/hist/src/TProfile3D.cxx | 190 +++++++++++++++++++++++++++++++-- test/stressHistogram.cxx | 200 ++++++++++++++++++++++++++++++++--- 8 files changed, 530 insertions(+), 64 deletions(-) diff --git a/hist/hist/inc/TH3.h b/hist/hist/inc/TH3.h index 9dfaa629e8c..266ad5a46c8 100644 --- a/hist/hist/inc/TH3.h +++ b/hist/hist/inc/TH3.h @@ -121,7 +121,7 @@ public: TH1D *ProjectionZ(const char *name="_pz", Int_t ixmin=0, Int_t ixmax=-1, Int_t iymin=0, Int_t iymax=-1, Option_t *option="") const; // *MENU* TH1 *Project3D(Option_t *option="x") const; // *MENU* - TProfile2D *Project3DProfile(Option_t *option="xy") const; // *MENU* + virtual TProfile2D *Project3DProfile(Option_t *option="xy") const; // *MENU* virtual void PutStats(Double_t *stats); virtual TH3 *RebinX(Int_t ngroup = 2, const char *newname = ""); virtual TH3 *RebinY(Int_t ngroup = 2, const char *newname = ""); @@ -134,15 +134,26 @@ public: virtual void SetShowProjection(const char *option="xy",Int_t nbins=1); // *MENU* protected: - TH1D *DoProject1D(const char* name, const char * title, TAxis* projX, + + virtual TH1D *DoProject1D(const char* name, const char * title, TAxis* projX, bool computeErrors, bool originalRange, bool useUF, bool useOF) const; - TH2D *DoProject2D(const char* name, const char * title, TAxis* projX, TAxis* projY, + virtual TH2D *DoProject2D(const char* name, const char * title, TAxis* projX, TAxis* projY, bool computeErrors, bool originalRange, bool useUF, bool useOF) const; - TProfile2D *DoProjectProfile2D(const char* name, const char * title, TAxis* projX, TAxis* projY, - bool originalRange, bool useUF, bool useOF) const; - + virtual TProfile2D *DoProjectProfile2D(const char* name, const char * title, TAxis* projX, TAxis* projY, + bool originalRange, bool useUF, bool useOF) const; + + // these functions are need to be used inside TProfile3D::DoProjectProfile2D + static TH1D *DoProject1D(const TH3 & h, const char* name, const char * title, TAxis* projX, + bool computeErrors, bool originalRange, bool useUF, bool useOF) { + return h.DoProject1D(name, title, projX, computeErrors, originalRange, useUF, useOF); + } + static TH2D *DoProject2D(const TH3 & h, const char* name, const char * title, TAxis* projX, TAxis* projY, + bool computeErrors, bool originalRange, bool useUF, bool useOF) { + return h.DoProject2D(name, title, projX,projY, computeErrors, originalRange, useUF, useOF); + } + ClassDef(TH3,5) //3-Dim histogram base class }; diff --git a/hist/hist/inc/TProfile2D.h b/hist/hist/inc/TProfile2D.h index d7aa86ade7a..8d680c11ceb 100644 --- a/hist/hist/inc/TProfile2D.h +++ b/hist/hist/inc/TProfile2D.h @@ -53,6 +53,8 @@ protected: nbins[1], range[2], range[3]); }; Int_t Fill(const Double_t* v) { return Fill(v[0], v[1], v[2], v[3]); }; + virtual TProfile *DoProfile(bool onX, const char *name, Int_t firstbin, Int_t lastbin, Option_t *option) const; + using TH2::Fill; Int_t Fill(Double_t, Double_t) {return TH2::Fill(0); } //MayNotUse @@ -127,6 +129,8 @@ public: virtual Bool_t Multiply(const TH1 *h1); virtual Bool_t Multiply(const TH1 *h1, const TH1 *h2, Double_t c1=1, Double_t c2=1, Option_t *option=""); // *MENU* TH2D *ProjectionXY(const char *name="_pxy", Option_t *option="e") const; + TProfile *ProfileX(const char *name="_pfx", Int_t firstybin=0, Int_t lastybin=-1, Option_t *option="") const; // *MENU* + TProfile *ProfileY(const char *name="_pfy", Int_t firstxbin=0, Int_t lastxbin=-1, Option_t *option="") const; // *MENU* virtual void PutStats(Double_t *stats); virtual void Reset(Option_t *option=""); virtual TProfile2D *Rebin2D(Int_t nxgroup=2, Int_t nygroup=2, const char *newname=""); diff --git a/hist/hist/inc/TProfile3D.h b/hist/hist/inc/TProfile3D.h index 926a5a5d231..86031f3e1d9 100644 --- a/hist/hist/inc/TProfile3D.h +++ b/hist/hist/inc/TProfile3D.h @@ -69,7 +69,9 @@ protected: //virtual void UpdateBinContent(Int_t bin, Double_t content); virtual Double_t GetBinErrorSqUnchecked(Int_t bin) const { Double_t err = GetBinError(bin); return err*err; } - + virtual TProfile2D *DoProjectProfile2D(const char* name, const char * title, TAxis* projX, TAxis* projY, + bool originalRange, bool useUF, bool useOF) const; + private: Double_t *GetB() {return &fBinEntries.fArray[0];} Double_t *GetB2() {return (fBinSumw2.fN ? &fBinSumw2.fArray[0] : 0 ); } @@ -127,7 +129,8 @@ public: virtual Bool_t Multiply(TF1 *h1, Double_t c1=1); virtual Bool_t Multiply(const TH1 *h1); virtual Bool_t Multiply(const TH1 *h1, const TH1 *h2, Double_t c1=1, Double_t c2=1, Option_t *option=""); // *MENU* - TH3D *ProjectionXYZ(const char *name="_pxyz", Option_t *option="e") const; + virtual TH3D *ProjectionXYZ(const char *name="_pxyz", Option_t *option="e") const; + virtual TProfile2D *Project3DProfile(Option_t *option="xy") const; // *MENU* virtual void PutStats(Double_t *stats); virtual void Reset(Option_t *option=""); virtual void SavePrimitive(std::ostream &out, Option_t *option = ""); diff --git a/hist/hist/src/TH2.cxx b/hist/hist/src/TH2.cxx index 721123c438b..8aa096b9673 100644 --- a/hist/hist/src/TH2.cxx +++ b/hist/hist/src/TH2.cxx @@ -2123,7 +2123,7 @@ TProfile *TH2::ProfileX(const char *name, Int_t firstybin, Int_t lastybin, Optio // // if option "d" is specified, the profile is drawn in the current pad. // - // if option "o" original axis range of the taget axes will be + // if option "o" original axis range of the target axes will be // kept, but only bins inside the selected range will be filled. // // The option can also be used to specify the projected profile error type. diff --git a/hist/hist/src/TProfile.cxx b/hist/hist/src/TProfile.cxx index c13761dc237..950dd8a84b0 100644 --- a/hist/hist/src/TProfile.cxx +++ b/hist/hist/src/TProfile.cxx @@ -1238,7 +1238,7 @@ TH1D *TProfile::ProjectionX(const char *name, Option_t *option) const if (opt.Contains("e")) computeErrors = kTRUE; if (opt.Contains("w")) binWeight = kTRUE; if (opt.Contains("c=e")) {cequalErrors = kTRUE; computeErrors=kFALSE;} - if (computeErrors || binWeight ) h1->Sumw2(); + if (computeErrors || binWeight || (binEntries && fBinSumw2.fN) ) h1->Sumw2(); // Fill the projected histogram Double_t cont; @@ -1255,15 +1255,11 @@ TH1D *TProfile::ProjectionX(const char *name, Option_t *option) const if (computeErrors ) h1->SetBinError(bin , GetBinError(bin) ); // in case of option W bin error is deduced from bin sum of z**2 values of profile // this is correct only if the profile is filled with weights =1 - if (binWeight) h1->SetBinError(bin , TMath::Sqrt(fSumw2.fArray[bin] ) ); - // in case of bin entries and h1 has sumw2 set, we need to set also the bin error - if (binEntries && h1->GetSumw2() ) { - Double_t err2; - if (fBinSumw2.fN) - err2 = fBinSumw2.fArray[bin]; - else - err2 = cont; // this is fBinEntries.fArray[bin] - h1->SetBinError(bin, TMath::Sqrt(err2 ) ); + if (binWeight) h1->GetSumw2()->fArray[bin] = fSumw2.fArray[bin]; + // in case of bin entries and profile is weighted, we need to set also the bin error + if (binEntries && fBinSumw2.fN ) { + R__ASSERT( h1->GetSumw2() ); + h1->GetSumw2()->fArray[bin] = fBinSumw2.fArray[bin]; } } diff --git a/hist/hist/src/TProfile2D.cxx b/hist/hist/src/TProfile2D.cxx index f26fe2c11e5..0d9b9569494 100644 --- a/hist/hist/src/TProfile2D.cxx +++ b/hist/hist/src/TProfile2D.cxx @@ -1192,21 +1192,21 @@ TH2D *TProfile2D::ProjectionXY(const char *name, Option_t *option) const // This option makes sense only for profile filled with all weights =1. // When the profile is weighted (filled with weights different than 1) the // bin error of the projected histogram (obtained using this option "W") cannot be -// correctly computed from the information stored in the profile. +// correctly computed from the information stored in the profile. In that case the +// obtained histogram contains as bin error square the weighted sum of the square of the +// profiled observable (TProfile2D::fSumw2[bin] ) TString opt = option; opt.ToLower(); + // Create the projection histogram + // name of projected histogram is by default name of orginal histogram + _pxy + TString pname(name); + if (pname.IsNull() || pname == "_pxy") + pname = TString(GetName() ) + TString("_pxy"); - // Create the projection histogram - char *pname = (char*)name; - if (strcmp(name,"_px") == 0) { - Int_t nch = strlen(GetName()) + 4; - pname = new char[nch]; - snprintf(pname,nch,"%s%s",GetName(),name); - } Int_t nx = fXaxis.GetNbins(); Int_t ny = fYaxis.GetNbins(); const TArrayD *xbins = fXaxis.GetXbins(); @@ -1229,8 +1229,7 @@ TH2D *TProfile2D::ProjectionXY(const char *name, Option_t *option) const if (opt.Contains("e")) computeErrors = kTRUE; if (opt.Contains("w")) binWeight = kTRUE; if (opt.Contains("c=e")) {cequalErrors = kTRUE; computeErrors=kFALSE;} - if (computeErrors || binWeight ) h1->Sumw2(); - if (pname != name) delete [] pname; + if (computeErrors || binWeight || (binEntries && fBinSumw2.fN) ) h1->Sumw2(); // Fill the projected histogram Int_t bin,binx, biny; @@ -1250,15 +1249,11 @@ TH2D *TProfile2D::ProjectionXY(const char *name, Option_t *option) const if (computeErrors ) h1->SetBinError(bin , GetBinError(bin) ); // in case of option W bin error is deduced from bin sum of z**2 values of profile // this is correct only if the profile is unweighted - if (binWeight) h1->SetBinError(bin , TMath::Sqrt(fSumw2.fArray[bin] ) ); - // in case of bin entries and h1 has sumw2 set set, we need to set the also bin error - if (binEntries && h1->GetSumw2() ) { - Double_t err2; - if (fBinSumw2.fN) - err2 = fBinSumw2.fArray[bin]; - else - err2 = cont; // this is fBinEntries.fArray[bin] - h1->SetBinError(bin, TMath::Sqrt(err2 ) ); + if (binWeight) h1->GetSumw2()->fArray[bin] = fSumw2.fArray[bin]; + // in case of bin entries and profile is weighted, we need to set also the bin error + if (binEntries && fBinSumw2.fN ) { + R__ASSERT( h1->GetSumw2() ); + h1->GetSumw2()->fArray[bin] = fBinSumw2.fArray[bin]; } } } @@ -1266,6 +1261,125 @@ TH2D *TProfile2D::ProjectionXY(const char *name, Option_t *option) const return h1; } +//______________________________________________________________________________ +TProfile *TProfile2D::ProfileX(const char *name, Int_t firstybin, Int_t lastybin, Option_t *option) const +{ + // *-*-*-*-*Project a 2-D histogram into a profile histogram along X*-*-*-*-*-* + // *-* ======================================================== + // + // The projection is made from the channels along the Y axis + // ranging from firstybin to lastybin included. + // The result is a 1D profile which contains the combination of all the considered bins along Y + // By default, bins 1 to ny are included + // When all bins are included, the number of entries in the projection + // is set to the number of entries of the 2-D histogram, otherwise + // the number of entries is incremented by 1 for all non empty cells. + // + // The option can also be used to specify the projected profile error type. + // Values which can be used are 's', 'i', or 'g'. See TProfile::BuildOptions for details + // + // + return DoProfile(true, name, firstybin, lastybin, option); +} + +//______________________________________________________________________________ +TProfile *TProfile2D::ProfileY(const char *name, Int_t firstxbin, Int_t lastxbin, Option_t *option) const +{ + // *-*-*-*-*Project a 2-D histogram into a profile histogram along X*-*-*-*-*-* + // *-* ======================================================== + // + // The projection is made from the channels along the X axis + // ranging from firstybin to lastybin included. + // The result is a 1D profile which contains the combination of all the considered bins along X + // By default, bins 1 to ny are included + // When all bins are included, the number of entries in the projection + // is set to the number of entries of the 2-D histogram, otherwise + // the number of entries is incremented by 1 for all non empty cells. + // + // The option can also be used to specify the projected profile error type. + // Values which can be used are 's', 'i', or 'g'. See TProfile::BuildOptions for details + // + // + // + return DoProfile(false, name, firstxbin, lastxbin, option); +} + +//______________________________________________________________________________ +TProfile * TProfile2D::DoProfile(bool onX, const char *name, Int_t firstbin, Int_t lastbin, Option_t *option) const { + // implementation of ProfileX or ProfileY for a TProfile2D + // Do correctly the combination of the bin averages when doing the projection + + TString opt = option; + opt.ToLower(); + bool originalRange = opt.Contains("o"); + + TString expectedName = ( onX ? "_pfx" : "_pfy" ); + + TString pname(name); + if (pname.IsNull() || name == expectedName) + pname = TString(GetName() ) + expectedName; + + const TAxis& outAxis = ( onX ? fXaxis : fYaxis ); + const TArrayD *bins = outAxis.GetXbins(); + Int_t firstOutBin = outAxis.GetFirst(); + Int_t lastOutBin = outAxis.GetLast(); + + TProfile * p1 = 0; + // case of fixed bins + if (bins->fN == 0) { + if (originalRange) + p1 = new TProfile(pname,GetTitle(), outAxis.GetNbins(), outAxis.GetXmin(), outAxis.GetXmax(), opt ); + else + p1 = new TProfile(pname,GetTitle(), lastOutBin-firstOutBin+1, + outAxis.GetBinLowEdge(firstOutBin),outAxis.GetBinUpEdge(lastOutBin), opt); + } else { + // case of variable bins + if (originalRange ) + p1 = new TProfile(pname,GetTitle(),outAxis.GetNbins(),bins->fArray,opt); + else + p1 = new TProfile(pname,GetTitle(),lastOutBin-firstOutBin+1,&bins->fArray[firstOutBin-1],opt); + + } + + if (fBinSumw2.fN) p1->Sumw2(); + + // make projection in a 2D first + TH2D * h2dW = ProjectionXY("h2temp-W","W"); + TH2D * h2dN = ProjectionXY("h2temp-N","B"); + + h2dW->SetDirectory(0); h2dN->SetDirectory(0); + + + TString opt1 = (originalRange) ? "o" : ""; + TH1D * h1W = (onX) ? h2dW->ProjectionX("h1temp-W",firstbin,lastbin,opt1) : h2dW->ProjectionY("h1temp-W",firstbin,lastbin,opt1); + TH1D * h1N = (onX) ? h2dN->ProjectionX("h1temp-N",firstbin,lastbin,opt1) : h2dN->ProjectionY("h1temp-N",firstbin,lastbin,opt1); + h1W->SetDirectory(0); h1N->SetDirectory(0); + + + // fill the bin content + R__ASSERT( h1W->fN == p1->fN ); + R__ASSERT( h1N->fN == p1->fN ); + R__ASSERT( h1W->GetSumw2()->fN != 0); // h1W should always be a weighted histogram since h2dW is + for (int i = 0; i < p1->fN ; ++i) { + p1->fArray[i] = h1W->GetBinContent(i); // array of profile is sum of all values + p1->GetSumw2()->fArray[i] = h1W->GetSumw2()->fArray[i]; // array of content square of profile is weight square of the W projected histogram + p1->SetBinEntries(i, h1N->GetBinContent(i) ); + if (fBinSumw2.fN) p1->GetBinSumw2()->fArray[i] = h1N->GetSumw2()->fArray[i]; // sum of weight squares are stored to compute errors in h1N histogram + } + // delete the created histograms + delete h2dW; + delete h2dN; + delete h1W; + delete h1N; + + // Also we need to set the entries since they have not been correctly calculated during the projection + // we can only set them to the effective entries + p1->SetEntries( p1->GetEffectiveEntries() ); + + return p1; +} + + //______________________________________________________________________________ void TProfile2D::PutStats(Double_t *stats) { diff --git a/hist/hist/src/TProfile3D.cxx b/hist/hist/src/TProfile3D.cxx index aee1a4fb901..4085feb4379 100644 --- a/hist/hist/src/TProfile3D.cxx +++ b/hist/hist/src/TProfile3D.cxx @@ -10,6 +10,7 @@ *************************************************************************/ #include "TProfile3D.h" +#include "TProfile2D.h" #include "THashList.h" #include "TMath.h" #include "THLimitsFinder.h" @@ -1064,47 +1065,214 @@ TH3D *TProfile3D::ProjectionXYZ(const char *name, Option_t *option) const // will be equal to the GetBinEntries(bin) of the profile, // if option "C=E" the bin contents of the projection are set to the // bin errors of the profile -// +// if option "E" is specified the errors of the projected histogram are computed and set +// to be equal to the errors of the profile. +// Option "E" is defined as the default one in the header file. +// if option "" is specified the histogram errors are simply the sqrt of its content +// if option "B" is specified, the content of bin of the returned histogram +// will be equal to the GetBinEntries(bin) of the profile, +// if option "C=E" the bin contents of the projection are set to the +// bin errors of the profile +// if option "W" is specified the bin content of the projected histogram is set to the +// product of the bin content of the profile and the entries. +// With this option the returned histogram will be equivalent to the one obtained by +// filling directly a TH2D using the 3-rd value as a weight. +// This option makes sense only for profile filled with all weights =1. +// When the profile is weighted (filled with weights different than 1) the +// bin error of the projected histogram (obtained using this option "W") cannot be +// correctly computed from the information stored in the profile. In that case the +// obtained histogram contains as bin error square the weighted sum of the square of the +// profiled observable (TProfile2D::fSumw2[bin] ) + TString opt = option; opt.ToLower(); Int_t nx = fXaxis.GetNbins(); Int_t ny = fYaxis.GetNbins(); Int_t nz = fZaxis.GetNbins(); + const TArrayD *xbins = fXaxis.GetXbins(); + const TArrayD *ybins = fYaxis.GetXbins(); + const TArrayD *zbins = fZaxis.GetXbins(); // Create the projection histogram TString pname = name; if (pname == "_px") { - pname = GetName(); pname.Append("_px"); + pname = GetName(); pname.Append("_pxyz"); + } + TH3D *h1 = 0 ; + if (xbins->fN == 0 && ybins->fN == 0 && zbins->fN == 0) + h1 = new TH3D(pname,GetTitle(),nx,fXaxis.GetXmin(),fXaxis.GetXmax(),ny,fYaxis.GetXmin(),fYaxis.GetXmax(),nz,fZaxis.GetXmin(),fZaxis.GetXmax()); + else if ( xbins->fN != 0 && ybins->fN != 0 && zbins->fN != 0) + h1 = new TH3D(pname,GetTitle(),nx,xbins->GetArray(),ny,ybins->GetArray(), nz,zbins->GetArray() ); + else { + Error("ProjectionXYZ","Histogram has an axis with variable bins and an axis with fixed bins. This case is not cupported - return a null pointer"); + return 0; } - TH3D *h1 = new TH3D(pname,GetTitle(),nx,fXaxis.GetXmin(),fXaxis.GetXmax(),ny,fYaxis.GetXmin(),fYaxis.GetXmax(),nz,fZaxis.GetXmin(),fZaxis.GetXmax()); + + Bool_t computeErrors = kFALSE; Bool_t cequalErrors = kFALSE; Bool_t binEntries = kFALSE; + Bool_t binWeight = kFALSE; + if (opt.Contains("b")) binEntries = kTRUE; if (opt.Contains("e")) computeErrors = kTRUE; + if (opt.Contains("w")) binWeight = kTRUE; if (opt.Contains("c=e")) {cequalErrors = kTRUE; computeErrors=kFALSE;} - if (computeErrors) h1->Sumw2(); + if (computeErrors || binWeight || (binEntries && fBinSumw2.fN) ) h1->Sumw2(); // Fill the projected histogram Int_t bin,binx,biny,binz; - Double_t cont,err; + Double_t cont; for (binx =0;binx<=nx+1;binx++) { for (biny =0;biny<=ny+1;biny++) { for (binz =0;binz<=nz+1;binz++) { bin = GetBin(binx,biny,binz); - if (binEntries) cont = GetBinEntries(bin); - else cont = GetBinContent(bin); - err = GetBinError(bin); - if (cequalErrors) h1->SetBinContent(binx,biny,binz, err); - else h1->SetBinContent(binx,biny,binz,cont); - if (computeErrors) h1->SetBinError(binx,biny,binz,err); + + if (binEntries) cont = GetBinEntries(bin); + else if (cequalErrors) cont = GetBinError(bin); + else if (binWeight) cont = GetBinContent(bin) * GetBinEntries(bin); + else cont = GetBinContent(bin); // default case + + h1->SetBinContent(bin ,cont); + + // if option E projected histogram errors are same as profile + if (computeErrors ) h1->SetBinError(bin , GetBinError(bin) ); + // in case of option W bin error is deduced from bin sum of z**2 values of profile + // this is correct only if the profile is unweighted + if (binWeight) h1->GetSumw2()->fArray[bin] = fSumw2.fArray[bin]; + // in case of bin entries and profile is weighted, we need to set also the bin error + if (binEntries && fBinSumw2.fN ) { + R__ASSERT( h1->GetSumw2() ); + h1->GetSumw2()->fArray[bin] = fBinSumw2.fArray[bin]; + } } } } h1->SetEntries(fEntries); return h1; } +//______________________________________________________________________________ +TProfile2D *TProfile3D::Project3DProfile(Option_t *option) const +{ + // *-*-*-*-*Project a 3-D profile into a 2D-profile histogram depending + // on the option parameter + // option may contain a combination of the characters x,y,z + // option = "xy" return the x versus y projection into a TProfile2D histogram + // option = "yx" return the y versus x projection into a TProfile2D histogram + // option = "xz" return the x versus z projection into a TProfile2D histogram + // option = "zx" return the z versus x projection into a TProfile2D histogram + // option = "yz" return the y versus z projection into a TProfile2D histogram + // option = "zy" return the z versus y projection into a TProfile2D histogram + // NB: the notation "a vs b" means "a" vertical and "b" horizontalalong X*-*-*-*-*-* + // + // The resulting profile contains the combination of all the considered bins along X + // By default, all bins are included considering also underflow/overflows + // + // The option can also be used to specify the projected profile error type. + // Values which can be used are 's', 'i', or 'g'. See TProfile::BuildOptions for details + // + // To select a bin range along an axis, use TAxis::SetRange, eg + // h3.GetYaxis()->SetRange(23,56); + // + // + + // can call TH3 method which will call the virtual method :DoProjectProfile2D reimplented below + // but need to add underflow/overflow + TString opt(option); + opt.Append(" UF OF"); + return TH3::Project3DProfile(opt); +} + +//______________________________________________________________________________ +TProfile2D *TProfile3D::DoProjectProfile2D(const char* name, const char * title, TAxis* projX, TAxis* projY, + bool originalRange, bool useUF, bool useOF) const +{ + // internal method to project to a 2D Profile + // called from TH3::Project3DProfile but re-implemented in case of the TPRofile3D since what is done is different + + // Get the ranges where we will work. + Int_t ixmin = projX->GetFirst(); + Int_t ixmax = projX->GetLast(); + Int_t iymin = projY->GetFirst(); + Int_t iymax = projY->GetLast(); + if (ixmin == 0 && ixmax == 0) { ixmin = 1; ixmax = projX->GetNbins(); } + if (iymin == 0 && iymax == 0) { iymin = 1; iymax = projY->GetNbins(); } + Int_t nx = ixmax-ixmin+1; + Int_t ny = iymax-iymin+1; + + // Create the projected profiles + TProfile2D *p2 = 0; + // Create always a new TProfile2D (not as in the case of TH3 projection) + + const TArrayD *xbins = projX->GetXbins(); + const TArrayD *ybins = projY->GetXbins(); + // assume all axis have variable bins or have fixed bins + if ( originalRange ) { + if (xbins->fN == 0 && ybins->fN == 0) { + p2 = new TProfile2D(name,title,projY->GetNbins(),projY->GetXmin(),projY->GetXmax() + ,projX->GetNbins(),projX->GetXmin(),projX->GetXmax()); + } else { + p2 = new TProfile2D(name,title,projY->GetNbins(),&ybins->fArray[iymin-1],projX->GetNbins(),&xbins->fArray[ixmin-1]); + } + } else { + if (xbins->fN == 0 && ybins->fN == 0) { + p2 = new TProfile2D(name,title,ny,projY->GetBinLowEdge(iymin),projY->GetBinUpEdge(iymax) + ,nx,projX->GetBinLowEdge(ixmin),projX->GetBinUpEdge(ixmax)); + } else { + p2 = new TProfile2D(name,title,ny,&ybins->fArray[iymin-1],nx,&xbins->fArray[ixmin-1]); + } + } + + // weights + bool useWeights = (fBinSumw2.fN != 0); + if (useWeights) p2->Sumw2(); + + // make projection in a 3D first + TH3D * h3dW = ProjectionXYZ("h3temp-W","W"); + TH3D * h3dN = ProjectionXYZ("h3temp-N","B"); + + h3dW->SetDirectory(0); h3dN->SetDirectory(0); + + // note that h3dW is always a weighted histogram - so we need to compute error in the projection + TAxis * projX_hW = h3dW->GetXaxis(); + TAxis * projX_hN = h3dN->GetXaxis(); + if (projX == GetYaxis() ) { projX_hW = h3dW->GetYaxis(); projX_hN = h3dN->GetYaxis(); } + if (projX == GetZaxis() ) { projX_hW = h3dW->GetZaxis(); projX_hN = h3dN->GetZaxis(); } + TAxis * projY_hW = h3dW->GetYaxis(); + TAxis * projY_hN = h3dN->GetYaxis(); + if (projY == GetXaxis() ) { projY_hW = h3dW->GetXaxis(); projY_hN = h3dN->GetXaxis(); } + if (projY == GetZaxis() ) { projY_hW = h3dW->GetZaxis(); projY_hN = h3dN->GetZaxis(); } + + TH2D * h2W = TH3::DoProject2D(*h3dW,"htemp-W","",projX_hW, projY_hW, true, originalRange, useUF, useOF); + TH2D * h2N = TH3::DoProject2D(*h3dN,"htemp-N","",projX_hN, projY_hN, useWeights, originalRange, useUF, useOF); + h2W->SetDirectory(0); h2N->SetDirectory(0); + + + // fill the bin content + R__ASSERT( h2W->fN == p2->fN ); + R__ASSERT( h2N->fN == p2->fN ); + R__ASSERT( h2W->GetSumw2()->fN != 0); // h2W should always be a weighted histogram since h3dW is weighted + for (int i = 0; i < p2->fN ; ++i) { + //std::cout << " proj bin " << i << " " << h2W->fArray[i] << " " << h2N->fArray[i] << std::endl; + p2->fArray[i] = h2W->fArray[i]; // array of profile is sum of all values + p2->GetSumw2()->fArray[i] = h2W->GetSumw2()->fArray[i]; // array of content square of profile is weight square of the W projected histogram + p2->SetBinEntries(i, h2N->fArray[i] ); + if (useWeights) p2->GetBinSumw2()->fArray[i] = h2N->GetSumw2()->fArray[i]; // sum of weight squares are stored to compute errors in h1N histogram + } + // delete the created histograms + delete h3dW; + delete h3dN; + delete h2W; + delete h2N; + + // Also we need to set the entries since they have not been correctly calculated during the projection + // we can only set them to the effective entries + p2->SetEntries( p2->GetEffectiveEntries() ); + + return p2; + +} //______________________________________________________________________________ void TProfile3D::PutStats(Double_t *stats) diff --git a/test/stressHistogram.cxx b/test/stressHistogram.cxx index b0d54baf14d..cd72fb34a82 100644 --- a/test/stressHistogram.cxx +++ b/test/stressHistogram.cxx @@ -8499,18 +8499,23 @@ bool testTH3toTH2() static const double centre_deviation = 0.3; -class ProjectionTester { +struct ProjectionTester { // This class implements the tests for all types of projections of // all the classes tested in this file. -private: +//public: static const unsigned int binsizeX = 8; static const unsigned int binsizeY = 10; static const unsigned int binsizeZ = 12; static const int lower_limit = 0; static const int upper_limit = 10; - + static const int lower_limitX = 0; + static const int upper_limitX = 10; + static const int lower_limitY = -5; + static const int upper_limitY = 10; + static const int lower_limitZ = -10; + static const int upper_limitZ = 10; TH3D* h3; TH2D* h2XY; @@ -8554,7 +8559,21 @@ private: TH1D* hw1YZ; TH1D* hw1ZX; TH1D* hw1ZY; - + + TProfile3D* p3; + + TProfile2D* p2XY; + TProfile2D* p2XZ; + TProfile2D* p2YX; + TProfile2D* p2YZ; + TProfile2D* p2ZX; + TProfile2D* p2ZY; + + TProfile* p1X; + TProfile* p1Y; + TProfile* p1Z; + + THnSparseD* s3; THnD* n3; @@ -8562,12 +8581,14 @@ private: public: - ProjectionTester() + + ProjectionTester(bool useWeights = false) { + buildWithWeights = useWeights; + CreateProfiles(); CreateHistograms(); - buildWithWeights = false; } - + void CreateHistograms() { h3 = new TH3D("h3","h3", binsizeX, lower_limit, upper_limit, @@ -8609,7 +8630,7 @@ public: pe2ZX = new TProfile2D("pe2ZX", "pe2ZX", binsizeZ, lower_limit, upper_limit, binsizeX, lower_limit, upper_limit); pe2ZY = new TProfile2D("pe2ZY", "pe2ZY", binsizeZ, lower_limit, upper_limit, - binsizeY, lower_limit, upper_limit); + binsizeY, lower_limit, upper_limit); h2wXY = new TH2D("h2wXY", "h2wXY", binsizeX, lower_limit, upper_limit, binsizeY, lower_limit, upper_limit); @@ -8659,6 +8680,32 @@ public: n3 = new THnD("n3","n3", 3, bsize, xmin, xmax); } + + void CreateProfiles() { + + // create Profile histograms + p3 = new TProfile3D("p3","p3", binsizeX, lower_limitX, upper_limitX, + binsizeY, lower_limitY, upper_limitY, + binsizeZ, lower_limitZ, upper_limitZ); + + p2XY = new TProfile2D("p2XY", "p2XY", binsizeX, lower_limitX, upper_limitX, + binsizeY, lower_limitY, upper_limitY); + p2XZ = new TProfile2D("p2XZ", "p2XZ", binsizeX, lower_limitX, upper_limitX, + binsizeZ, lower_limitZ, upper_limitZ); + p2YX = new TProfile2D("p2YX", "p2YX", binsizeY, lower_limitY, upper_limitY, + binsizeX, lower_limitX, upper_limitX); + p2YZ = new TProfile2D("p2YZ", "p2YZ", binsizeY, lower_limitY, upper_limitY, + binsizeZ, lower_limitZ, upper_limitZ); + p2ZX = new TProfile2D("p2ZX", "p2ZX", binsizeZ, lower_limitZ, upper_limitZ, + binsizeX, lower_limitX, upper_limitX); + p2ZY = new TProfile2D("p2ZY", "p2ZY", binsizeZ, lower_limitZ, upper_limitZ, + binsizeY, lower_limitY, upper_limitY); + + p1X = new TProfile("p1X", "pe1X", binsizeX, lower_limitX, upper_limitX); + p1Y = new TProfile("p1Y", "pe1Y", binsizeY, lower_limitY, upper_limitY); + p1Z = new TProfile("p1Z", "pe1Z", binsizeZ, lower_limitZ, upper_limitZ); + + } void DeleteHistograms() { @@ -8710,6 +8757,20 @@ public: delete s3; delete n3; + // profiles + delete p3; + + delete p2XY; + delete p2XZ; + delete p2YX; + delete p2YZ; + delete p2ZX; + delete p2ZY; + + delete p1X; + delete p1Y; + delete p1Z; + // delete all histogram in gROOT TList * l = gROOT->GetList(); TIter next(l); @@ -8727,6 +8788,7 @@ public: void buildHistograms() { + if (h3->GetSumw2N() ) {s3->Sumw2(); n3->Sumw2();} for ( int ix = 0; ix <= h3->GetXaxis()->GetNbins() + 1; ++ix ) { @@ -8807,6 +8869,7 @@ public: void buildHistogramsWithWeights() { + s3->Sumw2(); n3->Sumw2(); @@ -8994,6 +9057,7 @@ public: buildWithWeights = false; } + int compareHistograms() { int status = 0; @@ -9262,7 +9326,97 @@ public: return status; } - + + + void buildProfiles() { + + if (buildWithWeights) { + p3->Sumw2(); + p2XY->Sumw2(); p2YX->Sumw2(); p2YZ->Sumw2(); + p2XZ->Sumw2(); p2ZX->Sumw2(); p2ZY->Sumw2(); + p1X->Sumw2(); p1Y->Sumw2(); p1Z->Sumw2(); + } + + + // use a different way to fill the histogram + for (int i = 0; i < 100000; ++i) { + + // use in range in X but only overflow in Y and underflow/overflow in Z + double x = gRandom->Uniform(lower_limitX, upper_limitX ); + double y = gRandom->Uniform(lower_limitY, upper_limitY+2.); + double z = gRandom->Uniform(lower_limitZ-1, upper_limitZ+1); + double u = TMath::Gaus(x,0,3)*TMath::Gaus(y,3,5)*TMath::Gaus(z,-3,10); + + double w = 1; + if (buildWithWeights) w += x*x + (y-2)*(y-2) + (z+2)*(z+2); + + p3->Fill(x,y,z,u,w); + + p2XY->Fill(x,y,u,w); + p2YX->Fill(y,x,u,w); + p2XZ->Fill(x,z,u,w); + p2ZX->Fill(z,x,u,w); + p2YZ->Fill(y,z,u,w); + p2ZY->Fill(z,y,u,w); + + p1X->Fill(x,u,w); + p1Y->Fill(y,u,w); + p1Z->Fill(z,u,w); + + } + + // reset the statistics to get same statistics computed from bin centers + p1X->ResetStats(); + p1Y->ResetStats(); + p1Z->ResetStats(); + + p2XY->ResetStats(); + p2YX->ResetStats(); + p2XZ->ResetStats(); + p2ZX->ResetStats(); + p2YZ->ResetStats(); + p2ZY->ResetStats(); + } + + + // actual test of profile projections + int compareProfiles() + { + int status = 0; + int options = 0; + + // TProfile2d derived from TProfile3d + options = cmpOptStats; + //options = cmpOptPrint; + status += equals("TProfile3D -> XY", p2XY, p3->Project3DProfile("yx"), options); + status += equals("TProfile3D -> YX", p2YX, p3->Project3DProfile("xy"), options); + status += equals("TProfile3D -> XZ", p2XZ, p3->Project3DProfile("zx"), options); + status += equals("TProfile3D -> ZX", p2ZX, p3->Project3DProfile("xz"), options); + status += equals("TProfile3D -> YZ", p2YZ, p3->Project3DProfile("zy"), options); + status += equals("TProfile3D -> ZY", p2ZY, p3->Project3DProfile("yz"), options); + options = 0; + if ( defaultEqualOptions & cmpOptPrint ) + cout << "----------------------------------------------" << endl; + + // TProfile1 derived from TProfile2D from TProfile3D + options = cmpOptStats; + //options = cmpOptDebug; + TProfile2D* tmp1 = 0; + status += equals("TProfile2D -> X", p1X, p2XY->ProfileX(), options); + tmp1 = p3->Project3DProfile("xz"); + status += equals("TProfile3D -> X", p1X, tmp1->ProfileY(), options); + delete tmp1; tmp1 = 0; + status += equals("TProfile2D -> Y", p1Y, p2ZY->ProfileY(), options); + tmp1 = p3->Project3DProfile("xy"); + status += equals("TProfile3D -> X", p1Y, tmp1->ProfileX(), options); + delete tmp1; tmp1 = 0; + status += equals("TProfile2D -> Z", p1Z, p2ZX->ProfileX(), options); + tmp1 = p3->Project3DProfile("zy"); + status += equals("TProfile3D -> Z", p1Z, tmp1->ProfileY(), options); + delete tmp1; tmp1 = 0; + + return status; + } }; int stressHistogram() @@ -9310,10 +9464,18 @@ int stressHistogram() //Ht->buildHistograms(2,4,5,6,8,10); status = ht->compareHistograms(); GlobalStatus += status; - printResult("Testing Projections without weights..............................", status); delete ht; + printResult("Testing Histogram Projections without weights....................", status); + + ProjectionTester* htp = new ProjectionTester(); + htp->buildProfiles(); + status = htp->compareProfiles(); + GlobalStatus += status; + delete htp; + + printResult("Testing Profile Projections without weights......................", status); - // Test 2 + // Test 3-4 if ( defaultEqualOptions & cmpOptPrint ) std::cout << "**********************************\n" << " Test with weights \n" @@ -9324,8 +9486,16 @@ int stressHistogram() ht2->buildHistogramsWithWeights(); status = ht2->compareHistograms(); GlobalStatus += status; - printResult("Testing Projections with weights.................................", status); + printResult("Testing Histogram Projections with weights.......................", status); delete ht2; + + + ProjectionTester* htp2 = new ProjectionTester(true); + htp2->buildProfiles(); + status = htp2->compareProfiles(); + GlobalStatus += status; + printResult("Testing Profile Projections with weights.......................", status); + delete htp2; // Test 3 // Range Tests @@ -9925,17 +10095,17 @@ int equals(const char* msg, TH1D* h1, TH1D* h2, int options, double ERRORLIMIT) differents += (bool) equals(h1->GetXaxis()->GetNbins() , h2->GetXaxis()->GetNbins() ); if (debug) { - cout << "Nbins = " << h1->GetXaxis()->GetNbins() << " , " << h2->GetXaxis()->GetNbins() << " | " << differents << std::endl; + cout << "Nbins = " << h1->GetXaxis()->GetNbins() << " | " << h2->GetXaxis()->GetNbins() << " | " << differents << std::endl; } differents += (bool) equals(h1->GetXaxis()->GetXmin() , h2->GetXaxis()->GetXmin() ); if (debug) { - cout << "Xmin = " << h1->GetXaxis()->GetXmin() << " , " << h2->GetXaxis()->GetXmin() << " | " << differents << std::endl; + cout << "Xmin = " << h1->GetXaxis()->GetXmin() << " | " << h2->GetXaxis()->GetXmin() << " | " << differents << std::endl; } differents += (bool) equals(h1->GetXaxis()->GetXmax() , h2->GetXaxis()->GetXmax() ); if (debug) { - cout << "Xmax = " << h1->GetXaxis()->GetXmax() << " , " << h2->GetXaxis()->GetXmax() << endl; + cout << "Xmax = " << h1->GetXaxis()->GetXmax() << " | " << h2->GetXaxis()->GetXmax() << endl; } for ( int i = 0; i <= h1->GetNbinsX() + 1; ++i ) -- GitLab