How can I show stats box, when using THStack?

Hello, ROOTers

I has three histograms already filling and they
are added THStack object via Add method of THStack.
After that, they are drawn.
But, stats box for each histogram are not appeared in canvas.

In this case, How can I show stats box for each histogram ?

I attached sample program that uses THStack.

I use ROOT v3.10.02 with gcc 3.0.4 on IRIX65.

Thank you for you helop.

[/b]
test_stack.C (294 Bytes)

I have modified your example to generate teh “stat” box for each histogram in the stack at 3 different positions

Rene

void test_stack() {
THStack *hs = new THStack(“hs”, “test”);
TH1F h[3];
for ( Int_t i = 0; i < 3; i++ ) {
h[i] = new TH1F(Form(“h%d”, i), Form(“test h%d”), 50, -5, 5);
h[i]->FillRandom(“gaus”);
h[i]->SetLineColor(2
i+2);
hs->Add(h[i],“sames”);
}
hs->Draw(“ehist nostack”);
//the following lines will force the stats for h[1] and h[2]
//to be drawn at a different position to avoid overlaps
c1->Update(); //to for the generation of the 'stat" boxes
TPaveStats st1 = (TPaveStats)h[1]->GetListOfFunctions()->FindObject(“stats”);
TPaveStats st2 = (TPaveStats)h[2]->GetListOfFunctions()->FindObject(“stats”);
st1->SetX1NDC(.5); st1->SetX2NDC(.7);
st2->SetX1NDC(.2); st2->SetX2NDC(.4);
c1->Modified();
}

But what if you want them stacked? When I do something similar, but without the ‘nostack’ option, then I cannot move the stat boxes around because they aren’t defined.

When drawing with the default stack option, new histograms are created with the cimulative sum of the previous histograms. In this case you need a slightly different logic. See below:

void test_stack() { THStack *hs = new THStack("hs", "test"); TH1F *h[3]; for ( Int_t i = 0; i < 3; i++ ) { h[i] = new TH1F(Form("h%d", i), Form("test h%d"), 50, -5, 5); h[i]->FillRandom("gaus"); h[i]->SetLineColor(2*i+2); hs->Add(h[i],"sames"); } hs->Draw("ehist"); //hs->Draw("ehist nostack"); //the following lines will force the stats for h[1] and h[2] //to be drawn at a different position to avoid overlaps c1->Update(); //to for the generation of the 'stat" boxes TPaveStats *st1 = (TPaveStats*)hs->GetStack()->FindObject("h1")->FindObject("stats"); TPaveStats *st2 = (TPaveStats*)hs->GetStack()->FindObject("h2")->FindObject("stats"); st1->SetX1NDC(.5); st1->SetX2NDC(.7); st2->SetX1NDC(.2); st2->SetX2NDC(.4); c1->Modified(); }

Rene

After translating your code to pyROOT, it did not initially work because the histograms are not named h1, h2 … They have the names of the previous histograms I added to the stack. To get around this and not have to worry about what they are named I did:

        c = TCanvas(name, title, 200, 10, canvasW, canvasH)
        hs.Draw()
        leg.Draw()
        c.Update()
        # arrange stats
        histListForStats = []
        if showStats:
            for j in range(numHists-1, -1, -1):
                histListForStats.append( hs.GetStack().At(j) )
                # end of for loop
            self.arrangeStats(histListForStats, statBoxW, statBoxH)
        self.saveCanvas(c, logy)

arrangeStats is a function I wrote that moves the stat boxes one below the other.

    def arrangeStats(self, hists,
            statBoxW=0.16, statBoxH=0.10):
        i=0
        for h in hists:
            statBox = h.GetListOfFunctions().FindObject("stats")
            statBox.SetName('statBox' + str(i))
            statBox.SetX1NDC(0.99 - statBoxW)
            statBox.SetY1NDC(0.99 - i*(statBoxH + 0.01) - statBoxH)
            statBox.SetX2NDC(0.99)
            statBox.SetY2NDC(0.99 - i*(statBoxH + 0.01))
            statBox.SetTextColor(h.GetFillColor())
            statBox.SetBorderSize(2)
            i+=1

The problem is that the stat boxes are obviously wrong. I have attached a plot. The red histogram has much fewer entries than the blue, even though the statbox doesn’t reflect that.

Secondly, the graphs axis is draw on top of th statboxes for some reason.

Please see attached.

It seems like this whole business of messing with stats is very awkward. Why doesn’t ROOT cater to what a typical user would want?

And just a general criticism:
I would recommend that ROOT get away from passing any options in a string that has to be parsed at runtime. I would think that all options for any ROOT class could be controlled by a series of bools or enums, which would be much less awkward. (I realize that runtime issues are not relevant for pyROOT, but I also write compiled C++ code using ROOT.)

Thanks very much for your help.
OS_TauTau_mass_stack.pdf (16.8 KB)

1 Like

I do not see what could be wrong in your code without having
a concrete script or a file with your hists to reproduce your problem.
I checked that my script posted above produces correct results.

[quote]It seems like this whole business of messing with stats is very awkward.
Why doesn’t ROOT cater to what a typical user would want?
[/quote]
We are open to suggestions here. Our experience is that "typical users"
are vastly different ::slight_smile:
Concerning the statsbox with THStack, I agree that a more symmetric behaviour between "stack, nostack"
should be provided.

[quote]I would recommend that ROOT get away from passing any options in a string that
has to be parsed at runtime. I would think that all options for any ROOT class
could be controlled by a series of bools or enums, which would be much less
awkward. [/quote]

I totally disagree with you on this point. Having enums would have of course
the advantage of compile time checks. However
-very often these options are defined interactively, including from a GUI
-the enum has to be in some include file, typically in a class include,
but which one. If you take the TH1 paint options, in which class would you
expect to see the enum definition (TH1.h, THistPainter.h,…?). If in the
class, the enum grows when adding new options, forcing a complete recompilation
of all classes referencing the include.
-In case the class in question is a base class or abstract interface, you
do not want to put a enum definition there, because, by definition, the base
or abstract base class should not know the details of the derived class.
And if you put the enum in the final class, this is becoming a nightmare for
users who are exposed the details of implementations.
The only thing that counts is the correct description of the options
in the function calls. Here we have to improve our documentation.

Rene