Home | News | Documentation | Download

Unexpected additional bins appear in histogram projection when using bin labels

Dear experts,

I came across a strange bug/feature in ROOT. If I create a 2D histogram and assign labels to bins on the x axis, when making a projection, additional “ghost” bins appear in the projection. If no labels are used, no additional bins appear. Here is a minimal code that reproduces the behavior

import ROOT as r


r.gROOT.SetBatch()

hist = r.TH2F("hist", "", 2, 0, 2, 2, 0, 2)
hist.GetXaxis().SetBinLabel(1, "A")
hist.GetXaxis().SetBinLabel(2, "B")

c = r.TCanvas("c", "", 1200,800)
c.cd()

hist_px = hist.ProjectionX().Draw("hist")
hist_px.Draw("hist")

c.SaveAs('histProjectionX.png')

and here is the output

Without bin labels, the output looks as expected

One can work around the problem by adding the following code before drawing the projection

hist_px.GetXaxis().SetRangeUser(0,2)


but I thought I would nevertheless report the issue.

Best regards,
Dinko


ROOT Version: 6.20/04 (CVMFS installation in /cvmfs/sft.cern.ch/lcg/app/releases/ROOT/6.20.04/x86_64-ubuntu18-gcc75-opt/)
Platform: Ubuntu 18.04
Compiler: GCC 7.5


LabelsDeflate.

Thanks for the reply. Your suggestion of course works on hist_px since it has two labelled and two unlabelled bins. The problem I am referring to is the fact that the projection results in extra bins (two in this specific case) which are not there in the original 2D histogram.

This works:

{
   auto hist = new TH2F("hist", "", 2, 0, 2, 2, 0, 2);
   hist->GetXaxis()->SetBinLabel(1, "A");
   hist->GetXaxis()->SetBinLabel(2, "B");

   TH1D* hist_px = hist->ProjectionX();

   hist_px->LabelsDeflate();
   hist_px->Draw("hist");
}

Yes, of course, this works, as I already said in my previous reply. However, this is just another workaround for the problem. The following code perhaps illustrates the problem more clearly

import ROOT as r

r.gROOT.SetBatch()

hist = r.TH2F("hist", "", 2, 0, 2, 2, 0, 2)

print( "2D x-axis N bins: %d" % hist.GetXaxis().GetNbins() )

hist_px = hist.ProjectionX()

print( "1D N bins: %d" % hist_px.GetXaxis().GetNbins() )

hist.GetXaxis().SetBinLabel(1, "A")
hist.GetXaxis().SetBinLabel(2, "B")

hist_px = hist.ProjectionX()

print( "1D N bins: %d" % hist_px.GetXaxis().GetNbins() )

which returns the following output

2D x-axis N bins: 2
1D N bins: 2
1D N bins: 4

As you can see, the number of bins in the 1D projection changes depending on whether bins are labelled or not. This looks like a bug to me.

I see, my understanding is that the TH1D created from the TH2D in ProjectionX picks the labels from the X TH2D axis and add them just like one would do for a new created labelled TH1D. And in that case the number of labels created is more or less to double of what is needed for performance reasons. In a such case a Deflate is needed when all the labels are in place. Now the question is: Should ProjectionX call Deflate() ? may be yes. I let @moneta answer. The C++ code is:

void dinko() {
   auto hist = new TH2F("hist", "", 2, 0, 2, 2, 0, 2);
   printf( "2D x-axis N bins: %d \n", hist->GetXaxis()->GetNbins());
   TH1D* hist_px = hist->ProjectionX();

   printf( "1D N bins: %d \n",hist_px->GetXaxis()->GetNbins());

   hist->GetXaxis()->SetBinLabel(1, "A");
   hist->GetXaxis()->SetBinLabel(2, "B");
   printf( "2D x-axis N bins: %d \n", hist->GetXaxis()->GetNbins());

   hist_px = hist->ProjectionX();

   printf( "1D N bins: %d \n", hist_px->GetXaxis()->GetNbins() );
   hist_px->LabelsDeflate();
   printf( "1D N bins: %d \n", hist_px->GetXaxis()->GetNbins() );
}

output:

root [0] 
Processing dinko.C...
2D x-axis N bins: 2 
1D N bins: 2 
2D x-axis N bins: 2 
1D N bins: 4 
1D N bins: 2 
root [1] 

Hi,
This is a bug, I have open an issue for this:

Thank you for reporting the problem
Lorenzo