How to move statistics box when using Draw("sames"

Hello,

In a macro I do:

TFile *herwig = new TFile(“HadronicMultiplicity_eneEtaDep_J8_Herwig_120x.root”);
TFile *pythia = new TFile(“HadronicMultiplicity_eneEtaDep_J8_Pythia_120x.root”);

TH1D* trueChargeHerwigHist = (TH1D*)herwig->Get(“True Charged Energy”);
TH1D* trueChargePythiaHist = (TH1D*)pythia->Get(“True Charged Energy”);

TCanvas *can = new TCanvas();
can->SetLogy();

trueChargeHerwigHist->Draw();

Now to put the stats box where I want I do (and it works):

can->Update();
TPaveStats st2 = (TPaveStats)can->GetPrimitive(“stats”);
st2->SetX1NDC(0.6);
st2->SetX2NDC(0.9);
st2->SetY1NDC(0.6);
st2->SetY2NDC(0.9);

Now I draw the second histogram:

trueChargePythiaHist->SetLineColor(kRed);
trueChargePythiaHist->Draw(“sames”);

Now if I do:

TPaveStats st3 = (TPaveStats)can->GetPrimitive(“stats”);
st3->SetX1NDC(0.3);
st3->SetX2NDC(0.59);
st3->SetY1NDC(0.6);
st3->SetY2NDC(0.9);

the stats box for the second histogram does not move. How can I move it? Is the way I do it the best way or there is an easier way (not dragging with the mouse as I want to automate making lots of plots in my macro to save time when redoing them)?

I am using root 5.14/00b on a linux slc4 machine.

Thanks,

Mark

1 Like

Hello Mark,
I am using using PyROOT, so my situation could be a little different from yours…
Anyway, in my case the TPaveStats for the second histogram (the one
drawn with the “same” option) doesn’t even show up. If I try to access it via
TH1F::GetListOfFunctions().FindObject(“stats”) I get a null pointer.
So I guess that, as you are accessing it via TCanvas::GetPrimitive(“stats”),
you are still reading the one from the first histo that you drawn.
The workaround that I found is:

        histo1.Draw()
        c1.GetPad(index+1).Update()
        stats1 = histo1.GetListOfFunctions().FindObject("stats").Clone("stats1")
        stats1.SetY1NDC(.5)
        stats1.SetY2NDC(.7)
        histo2.Draw()
        c1.GetPad(index+1).Update()
        stats2 = histo2.GetListOfFunctions().FindObject("stats").Clone("stats2")
        # move stats2 where you want it to be
        histo1.Draw("same")
        stats1.Draw()
        stats2.Draw()

You should translate this from python into C, if you want.
I hope this helps. Cheers,

davide

Also, did you try a TPad::Update() ?

see an example below

Rene

void sames() {
   TH1F *HPtJ1 = new TH1F("HPtJ1","Pt of 1st jet",20,0,20);
   TH1F *HPtJ2 = new TH1F("HPtJ2","Pt of 2nd jet",20,0,20);

   HPtJ1->Fill(10,0.5); HPtJ1->Fill(12,0.6);
   HPtJ2->Fill(2,1);HPtJ2->Fill(4,2);HPtJ2->Fill(7,3);

   HPtJ1->SetLineColor(4);
   HPtJ2->SetLineColor(2);

   TCanvas *c1 = new TCanvas("c1");

   //HPtJ2->Draw();
   gStyle->SetOptFit();
   HPtJ2->Fit("pol1");
   
   c1->Modified(); c1->Update();
   TPaveStats *stats =(TPaveStats*)c1->GetPrimitive("stats");
   stats->SetName("h1stats");
   stats->SetY1NDC(.4);
   stats->SetY2NDC(.6);
   stats->SetTextColor(2);

   //HPtJ1->Draw("sames");
   HPtJ1->Fit("pol0","","sames");
   c1->Update();
   TPaveStats *stats2 = (TPaveStats*)c1->GetPrimitive("stats");
   stats2->SetName("h1stats2");
   stats2->SetY1NDC(.1);
   stats2->SetY2NDC(.3);
   stats2->SetTextColor(4);
}
1 Like

[quote=“brun”]see an example below

Rene

[code]
void sames() {
TH1F *HPtJ1 = new TH1F(“HPtJ1”,“Pt of 1st jet”,20,0,20);
TH1F *HPtJ2 = new TH1F(“HPtJ2”,“Pt of 2nd jet”,20,0,20);

HPtJ1->Fill(10,0.5); HPtJ1->Fill(12,0.6);
HPtJ2->Fill(2,1);HPtJ2->Fill(4,2);HPtJ2->Fill(7,3);

HPtJ1->SetLineColor(4);
HPtJ2->SetLineColor(2);

TCanvas *c1 = new TCanvas(“c1”);

//HPtJ2->Draw();
gStyle->SetOptFit();
HPtJ2->Fit(“pol1”);

c1->Modified(); c1->Update();
TPaveStats stats =(TPaveStats)c1->GetPrimitive(“stats”);
stats->SetName(“h1stats”);
stats->SetY1NDC(.4);
stats->SetY2NDC(.6);
stats->SetTextColor(2);

//HPtJ1->Draw(“sames”);
HPtJ1->Fit(“pol0”,"",“sames”);
c1->Update();
TPaveStats stats2 = (TPaveStats)c1->GetPrimitive(“stats”);
stats2->SetName(“h1stats2”);
stats2->SetY1NDC(.1);
stats2->SetY2NDC(.3);
stats2->SetTextColor(4);
}
[/code][/quote]

Hello,Rene,
I test your code and works fine. The only thing I could’t understand is if I don’t set the name as :
stats->SetName(“h1stats”);
stats2->SetName(“h1stats2”);
Then the moving and color doesn’t work.
In my opinion, SetName is just set a name for a object.
What’s the ranson for this?
Many thanks.

When you want to superimpose a second TPaveStats to the same picture, it is important to change the name of an already existing TPaveStats, otherwise it will be deleted. This is is fundamental when redrawing again and again the histogram with many stats boxes.

Rene

Hi,
I have the same problem with PaveStats when looping over histograms. Within the loop over a list of TH1F, I call this following function

void DrawStatBox(TH1Fh1, int i){
gPad->Update();
TPaveStats stat;
stat = (TPaveStats
)h1->GetListOfFunctions()->FindObject(“stats”);
stat->SetName(sampleName[i].c_str());
stat->SetY1NDC(stat->GetY1NDC()-i
0.2);
stat->SetY2NDC(stat->GetY2NDC()-i*0.2);
stat->SetTextColor(colors[i]);
//stat->SetOptStat(11);//??? not working???
stat->Draw();
}

I would like to draw all histograms superimposed with the corresponding stat boxes in different positions and colors. The result of the loop is that all the stat boxes are drawn correctly but contain the same information, i.e. only the info of the last stats is saved and plotted.
Can you help me?

Regards, francesca

// http://root.cern.ch/root/html/TStyle.html#TStyle:SetOptStat gStyle->SetOptStat(1111); // ... // http://root.cern.ch/root/html/TH1.html#TH1:SetStats // SomeHisto->SetStats(kTRUE); // ... // http://root.cern.ch/root/html/THistPainter.html SomeHisto->Draw("SAMES"); // you MUST draw "SomeHisto" first (e.g. with "SAMES") gPad->Modified(); gPad->Update(); // make sure it's really (re)drawn // http://root.cern.ch/root/html/TPaveStats.html #if 1 /* 0 or 1 */ TPaveStats *s = ((TPaveStats*)(SomeHisto->FindObject("stats"))); // if (s) s->SetName("SomeDistinctObjectName"); // you can rename "stats" if you want #else /* 0 or 1 */ TPaveStats *s = ((TPaveStats*)(gPad->GetPrimitive("stats"))); if (s) s->SetName("SomeDistinctObjectName"); // you MUST rename "stats" #endif /* 0 or 1 */ if (s) { s->SetTextColor(SomeColorIndex); s->SetX1NDC(LeftX); s->SetX2NDC(RightX); s->SetY1NDC(BottomY); s->SetY2NDC(TopY); } gPad->Modified(); gPad->Update(); // make sure it's really (re)drawn

1 Like

Hi,

all the suggestions you gave me are already implemented. Then I discovered that the problem comes from the fact that I draw the histograms normalized with TH1F::DrawNormalized(), which causes the stat box of the first histogram to be replaced by the following one (even changing the name of the PaveStats). When I use simple TH1F:Draw() everything works fine. Can you check if it’s true and give me a suggestion on how to overcome this limit? Thanks a lot, cheers
francesca

// http://root.cern.ch/root/html/TStyle.html#TStyle:SetOptStat gStyle->SetOptStat(1111); // ... TH1 *h; // a pointer to the drawn histogram TPaveStats *s; // a pointer to the statistics box // ... // ... for every "Histogram" that you want to process ... // ... // http://root.cern.ch/root/html/TH1.html#TH1:SetStats // Histogram->SetStats(kTRUE); // ... // you MUST draw your "Histogram" first (e.g. with "SAMES") ... // http://root.cern.ch/root/html/THistPainter.html #if 0 /* 0 or 1 */ // http://root.cern.ch/root/html/TH1.html#TH1:Draw Histogram->Draw("SAMES"); // TH1::Draw h = Histogram; #else /* 0 or 1 */ // http://root.cern.ch/root/html/TH1.html#TH1:DrawCopy // h = Histogram->DrawCopy("SAMES"); // TH1::DrawCopy // http://root.cern.ch/root/html/TH1.html#TH1:DrawNormalized h = Histogram->DrawNormalized("SAMES"); // TH1::DrawNormalized #endif /* 0 or 1 */ gPad->Modified(); gPad->Update(); // make sure it's really (re)drawn // ... and now you can retrieve and modify the "stats" TPaveStats // http://root.cern.ch/root/html/TPaveStats.html #if 1 /* 0 or 1 */ s = ((TPaveStats*)(h->FindObject("stats"))); // if (s) s->SetName("SomeDistinctObjectName"); // you can rename "stats" if you want #else /* 0 or 1 */ s = ((TPaveStats*)(gPad->GetPrimitive("stats"))); if (s) s->SetName("SomeDistinctObjectName"); // you MUST rename "stats" #endif /* 0 or 1 */ if (s) { s->SetTextColor(SomeColorIndex); s->SetX1NDC(LeftX); s->SetX2NDC(RightX); s->SetY1NDC(BottomY); s->SetY2NDC(TopY); } gPad->Modified(); gPad->Update(); // make sure it's really (re)drawn

Hi,
ok so the trick is that I have to Draw (or DrawNormalized) the first TH in the loop twice… First Draw() it and then Draw(“SAMES”). Is this a bug or I’ve missed something? Thanks again, cheers
francesca

1 Like

When I say that you must draw the histogram first, it means that you need to do it (either Draw or DrawCopy or DrawNormalized it) BEFORE you can get its “stats” TPaveStats (as shown in my example source code).
Usually, you need to draw the first histogram WITHOUT the “SAMES” option (in this case it will clear the current pad before drawing and then it will create and paint axes together with the histogram itself and its “stats”), while the remaining histograms will be drawn WITH the “SAMES” option (in the same pad).
You can also draw the first histogram twice. The first time just with the “AXIS” option (draws only axes), then again with “SAMES”.

Hi, this is actually what I was doing since the beginning, of course.
You said redraw so I interpret as drawing it twice. In any case this let me understand the problem.

In my code, I’m doing the update of the gPad (gPad->Modified(); gPad->Update(); ) after drawing the histogram, as explained in the documentation of the TPaveStats, and then get the TPaveStats and draw it. Differently your example includes a second update of the gPad after drawing the statbox. This second one is crucial, while with h1->FindObject("stats”) the first one is useless.
So the statbox needs to be included in the pad before drawing other statboxes, otherwise is overwritten.
(PS: with Draw and DrawNormalized there are different behaviors probably for a different memory allocation).

Thanks for your feedback, cheers
francesca

Note: in my example source codes, I never explicitly draw any statistics box myself. I simply make sure that the current pad is “redrawn” (“updated”) in relevant places. In particular, updating the current pad after drawing the histogram and before retrieving its “stats” TPaveStats is MANDATORY.

1 Like

I confirm that between the h->Draw() and retrieving the stats I had to call c->Update(). Thank you for pointing this out.