Trouble w/ StackHistograms

Hi,

I guess this issue is solved on my side. I just wanted to expand this old post:

If I do:

THStack* hs = blah;
hs->Add(…);

Then If I want to edit the y-axis and draw:

hs->SetMaximum(600);
hs->Draw();

As the previous post says the result is not 600, but slightly more based on ‘gStyle->GetHistTopMargin()’. But if one draws the stack in log mode, they must be weary of what is being done behind the scenes:

root.cern.ch/root/html/src/THStack.cxx.html#717
and
root.cern.ch/root/html/src/THStack.cxx.html#722

Thus to get my proper y-axis I need to do this:

if(!gPad->GetLogy()){
stack->SetMaximum(themax/(1+gStyle->GetHistTopMargin()));
stack->SetMinimum(themin);
}
else {//I know exactly what I want my hist range to be

stack->SetMaximum(themax/(1+0.2TMath::Log10(themax/themin)));
stack->SetMinimum(themin
(1+0.5*TMath::Log10(themax/themin)));
}

I understand the need to make sure that the user defined gStyle is adhered to, but why do I have to go through so much trouble just to set the min and max in log mode? I think that if I explicitly tell my stack to have a certain maximum, I expect it to be followed. I save my canvas as a root file. Open the canvas in a TBrowser and manually set the axis range through the GUI, I then want to put these values in my script so that the next time I run the script, I can just directly extract an eps or whatever. I spent a whole morning trying to figure out exactly what I needed to do to change the y-axis to be exactly what i wanted. I did the old trick of editing the canvas in the GUI, saving as ‘.C’ and inspecting. This is where I discoved that the axis ranges are defined by the THStack::fHistogram. However, I could not figure out how to edit this guy in my macro:

stack->Draw();//ensure that fHistogram exists
stack->GetHistogram()->SetAxisRange(…);
stack->GetYaxis()->SetRangeUser(…);

many more desperation attempts that all compile and run–just gives me nothing.

So unless I missed a much simpler method, could documentation be improved?:

root.cern.ch/root/html/THStack.h … SetMaximum

give a warning that the values you insert will be changed?

Or give a method that will actually just do what you ask? If the user gives a negative value for the min or max, then they should be punished… just my opinion.

Thanks,
Justin

1 Like

The margin added at the top of the histogram drawing is meant to make the plot look nicer. It has nothing to do with the real maximum or the maximum set. It is just an extra space. In case of log scale the extra margin cannot just be the log of the the HistTopMargin (it does not work). The following macro shows that to get the same visual effect in linear and logarithmic scale we need to define two different drawn maximum.
(comment and uncomment the line gPad->SetLogy(); to see the difference);

{
   
   THStack *hs = new THStack("hs","Stacked 1D histograms");

   TH1F *h1st = new TH1F("h1st","test hstack",100,-4,4);
   h1st->FillRandom("gaus",20000);
   h1st->SetFillColor(kRed);
   h1st->SetFillStyle(3002);
   hs->Add(h1st);
   TH1F *h2st = new TH1F("h2st","test hstack",100,-4,4);
   h2st->FillRandom("gaus",15000);
   h2st->SetFillColor(kBlue);
   h2st->SetFillStyle(3003);
   hs->Add(h2st);
   TH1F *h3st = new TH1F("h3st","test hstack",100,-4,4);
   h3st->FillRandom("gaus",10000);
   h3st->SetFillColor(kGreen);
   h3st->SetFillStyle(3004);
   hs->Add(h3st);
   
   TCanvas *cst = new TCanvas("cst","stacked hists",10,10,700,700);
 
   hs->Draw();

   gPad->SetLogy();
   
   gPad->Update();
 
   printf("The maximum is %g\n", hs->GetMaximum());
   
   Double_t x1,y1,x2,y2;
   gPad->GetRangeAxis(x1,y1,x2,y2);
   if (gPad->GetLogy()) printf("The maximum drawn is %g\n", TMath::Power(10,y2));
   else                 printf("The maximum drawn is %g\n", y2);
}

Hi,

I understand the motivation for adding the padding at the top of the stack in order to make the plot look nice in log mode. My only point is, that when I am ready to go ahead and make final plots, I take a look at the canvas in root and manually change the range. I then want to set this range in my script. So it took a little while for me to realize why this didn’t work as expected: Or better yet often times round numbers such as 1e-2 or 1e4 look nicer than:

root.cern.ch/root/html/src/THStack.cxx.html#737
fHistogram->SetMaximum(themax*(1+0.2*TMath::Log10(themax/themin)));

so

hstack->SetMaximum(1e4); //assuming min is 10

is not truly 1e4 but 1.6e4

and this does not seem to be advertsed on the html:

root.cern.ch/root/html/THStack.h … SetMaximum

So if I know what max I want, I would like to have an easy way to set that. In the end, I found that simply drawing a dumy histogram first was the best solution:

TH1* dummy = hstack->GetHists()->At(0);
dummy->SetAxisRange(…);
dummy->Draw();
hstack->Draw(“same”);

Cheers,
Justin

Yes I would do that too.

or gPad -> DrawFrame(…);

Hi,

I finally managed to get the correct Y-axis range with the dummy method. However, the axis ticks are only shown in the plot range which belongs to the histogram I added last to the stack. Any ideas how I make ticks visible for the whole stack ranges?

     c = ROOT.TCanvas("","",1600,1200)
     c.SetGrid(1)
     ROOT.gStyle.SetOptStat(0)
     ROOT.gPad.SetTickx()
     ROOT.gPad.SetTicky()
 
     hs = ROOT.THStack("hs",";LAr module x-y-width [cm];")
 
     scatter_ratio.SetLineColor(1)
     scatter_ratio.SetFillColor(2)
     scatter_ratio.SetFillStyle(3003)
     scatter_ratio.GetYaxis().SetRangeUser(0.,1.)
 
     cap_in_ratio.SetLineColor(1)
     cap_in_ratio.SetFillColor(3)
     cap_in_ratio.SetFillStyle(3004)
     cap_in_ratio.GetYaxis().SetRangeUser(0.,1.)
 
     cap_out_ratio.SetLineColor(1)
     cap_out_ratio.SetFillColor(4)
     cap_out_ratio.SetFillStyle(3005)
     cap_out_ratio.GetYaxis().SetRangeUser(0.,1.)
 
     hs.Add(cap_in_ratio)
     hs.Add(cap_out_ratio)
     hs.Add(scatter_ratio)
 
     hs.Draw();
     hs.GetYaxis().SetTitleOffset(1.4)
     hs.GetYaxis().SetRangeUser(0.,1.)
 
     dummy = hs.GetHists().At(0)
     dummy.SetAxisRange(0.,1.)
     dummy.Draw()
 
     hs.Draw("same")
     c.Update()

Cheers,
Patrick

The ticks seem to be erased by the hs.Draw("same") command.
Try gPad->RedrawAxis(); just after .

1 Like

Another problem I encountered with THStack is the z-axis. I’d like to set the title offset in the same way as I do for the x- and y-axis. However, ROOT says: AttributeError: ‘THStack’ object has no attribute ‘GetZaxis’. Even though I can set a z-axis title.

     c1 = ROOT.TCanvas("","",1600,1200)
     c1.SetGrid(1)
     #c1.SetLeftMargin(0.15)
     ROOT.gStyle.SetOptStat(0)
     ROOT.gPad.SetTickx()
     ROOT.gPad.SetTicky()
     ROOT.gPad.SetLogx(0)
     ROOT.gPad.SetLogy(0)
 
     hist_1.SetFillColor(2)
     hist_2.SetFillColor(4)
     hist_3.SetFillColor(5)
 
     hs = ROOT.THStack("hs","Spill Multiplicity of Recoiling Protons > %.0f MeV;TPC-X;TPC-Z;Average Hit Number" % (e_th_trk*1e3))
     hs.Add(hist_1)
     hs.Add(hist_2)
     hs.Add(hist_3)
     hs.Draw("0lego1")
     hs.GetXaxis().SetTitleOffset(1.5)
     hs.GetYaxis().SetTitleOffset(1.5)
     #hs.GetZaxis().SetTitleOffset(1.5)
     hs.GetXaxis().SetRangeUser(0.5,8.5)
     hs.GetYaxis().SetRangeUser(0.2,5.8)
 
     legend = ROOT.TLegend(0.55, 0.70, .95, .90);
     legend.AddEntry(hist_1, "Nu Vertex in Same TPC, #sum=%.2f " % int_1, "f");
     legend.AddEntry(hist_2, "Nu Vertex in Other TPC, #sum=%.2f " % int_2, "f");
     legend.AddEntry(hist_3, "Nu Vertex in Rock, #sum=%.2f " % int_3, "f");
     legend.Draw();

Cheers,
Patrick

Using

hs.GetHistogram().GetZaxis().SetTitleOffset(1.5)

instead fixes the problem.

1 Like

May be we need also the short cut for the Axis in THstack.

1 Like