Not plotting zero bins in TH2 with negative entries

Hi,

I’m using the “COLZ” option to plot a TH2D that has both positive and negative entries. Many entries are also zero. I would like to plot only nonzero entries and have empty bins show up as white. This is the default behavior for histograms with only non-negative entries. I’m hoping there is an easy way to get this behavior also for histograms with some negative entries.

Messing around with color palettes and SetContour hasn’t gotten my anywhere because the histograms are such that I can’t assume much about the entries. In particular, they are not symmetric around zero. I also tried calling gPad->PaintBox() to manually paint empty regions white like I saw it done in THistPainter::PaintColorLevels(), but my calls to PaintBox don’t seem to do anything. My attempt at a function to “whiteout” empty bins for a histogram that was just plotted is attached. Perhaps someone can tell me what’s wrong with it.

I’m using ROOT 5.34. Thanks in advance!

//
Anders
whiteOut.cpp (648 Bytes)

Have you considered using Ttree? i think you just need an if statement like:

TH2D* Hh1;
.
.
.
{fChain->GetTree()->GetEntry(entry);
if(x=!0){
Hh1->Fill(x,y);}
Hh1->Draw();
return kTRUE;}

else way just try an if statement with
SetMarkerColor(kWhite);

Sincerely
Stylianos Nikas

Did you try COLZ0 ?

Stellos:
I’m afraid I don’t understand your solution. The marker color or fill method should have no impact as far as I’m aware.

couet:
Drawing with “COLZ0” yields the same result as drawing with “COLZ”. A minimal working example is shown below. I want bin 3 (the empty one) to appear white. Can this be done? Again, it happens automatically if I don’t put any begative entries in the histogram.

#include "TH2D.h"
#include "TCanvas.h"

void colorTest() {
	TH2D* hist = new TH2D("test", "test", 4, 0., 4., 1, 0., 1.);
	hist->Fill(0., 0., 2); //Bins 1-4 contain 2, 1, 0 and -1
	hist->Fill(1., 0., 1); //I want the empty bin to appear white
	hist->Fill(3., 0., -1);

	new TCanvas();
	hist->Draw("COLZ"); //Using COLZ0 doesn't change anything
}

The empty bins are not drawn only if zmin>=0. If zmin<0 an bin with content 0 might be not empty.

I understand the rationale for drawing bins with content == 0. For my particular application, however, I would like to not draw them. Is this possible?

The answer is “No”, regarding the way it is coded now in THistPainter.

Looking at THistPainter::PaintColorLevels(), it seems that the function iterates though each bin of the histogram and finds the color appropriate for the corresponding “pixel” of the pad. My idea with the whiteout function attached to the first post was to do more or less the same thing - Iterate though the histogram that was just drawn and re-paint the pixels corresponding to bins with content == 0 as white. However, for reasons that I do not understand, the function does nothing. Can you spot the error, or perhaps correct it so that it works as intended?

… not “pixels”… but bin

can I see it ?

Hi,

although I do not have a proper solution for your problem I can offer you a hack.
The attached macro creates a histogram that can “mask” out the zero boxes with
“almost” white. Example:

.L maskHist.C 
TH2D *foo = new TH2D("test","",5,0,1,5,0,1);
foo->Fill(0.,0.,-1.);
foo->Fill(0.5,0.5,1.);
foo->Draw("COL")
TH2D *bar = getZeroMask(foo);
bar->SetFillColor(TColor::GetColor(253,253,253));
bar->Draw("BOX same");

You may have to fiddle around a bit with the axis, as they seem to get partially
overdrawn by the draw-call of “bar”.

Cheers,
Jochen
maskHist.C (407 Bytes)

couet:
It is attached to the first post.

jkerdels:
Your hack did the trick! Simply redrawing the axes restores them. Here’s a working version of the “whiteout” function in case anyone else has this problem in the future.

void whiteout(TH2D* hist) {
	TH2D* copy = dynamic_cast<TH2D*>(hist->Clone());
	copy->Reset();
	copy->SetFillColor(TColor::GetColor(253,253,253));
	Int_t bin = 0;
	Double_t maxVal = hist->GetMaximum();
	for(Int_t i = 1; i <= hist->GetNbinsX(); ++i) {
		for(Int_t j = 1; j <= hist->GetNbinsY(); ++j) {
			bin = hist->GetBin(i, j);
			if(!hist->GetBinContent(bin)) copy->SetBinContent(bin, maxVal);
		}
	}
	copy->Draw("BOX SAME");
	hist->Draw("AXIS SAME");
}

//
Anders

Turns out that the histograms look fine when viewed in a TCanvas, but when saving the plots to pdf or eps, faint lines appear at the bin borders. I ended up hacking THistPainter::PaintColorLevels() to never paint empty bins at all by changing the line

if (z == 0 && (zmin >= 0 || Hoption.Logz)) continue;

to

if (z == 0) continue;

After recompiling ROOT (which took seconds since I only changed this one detail), it works the way I want. This is clearly not a general solution, though. An option to skip plotting empty bins would be a nice addition for future versions of ROOT.

//
Anders

This was fixed. Very likely you are using an old ROOT version.

Yes

Now implemented in the master:

Using the COL option with histograms having some negative bins; the empty bins
(containing 0) are drawn. In some cases one wants to not draw empty bins
(containing 0) of histograms having a negative minimum. The option 1, used with
the option COL, allows to do that.

Resurrecting this old topic.

While option “1” is great, is there another option for TH2D with negative and positive values that will differentiate bins filled with value of “0” than those that are not filled ?

I would like to show bins filled actually with value of “0” and hide unfilled bins (leave them blank).

#include <TCanvas.h>
#include <TH2D.h>
#include <TRandom.h>

void drawTH2D() {

    TCanvas *c1 = new TCanvas("c1", "TH2D with COLZ1", 800, 600);

    TH2D *h2 = new TH2D("h2", "Random Integer Fill;X-axis;Y-axis", 10, 0, 10, 10, 0, 10);

    TRandom randGen;

    for (int ix = 1; ix <= h2->GetNbinsX(); ++ix) {
        for (int iy = 1; iy <= h2->GetNbinsY(); ++iy) {
            // Skip even Y-bin indices
            if (iy % 2 == 0) continue;

            // Random integer in range {-3, -2, -1, 0, 1, 2, 3}
            int value = randGen.Integer(7) - 3;

            h2->SetBinContent(ix, iy, value);
        }
    }

    h2->Draw("COLZ1");

    c1->Update();
}

There is no way to determine this. When a histogram is received by the visualization part of ROOT, it is impossible to tell whether a bin with zero content was never filled or if the sum of the calls to TH1::Fill resulted in a zero content.

In principle, an “empty bin” is a bin with “null content” AND “null error”. So, if a bin has “non-null error”, it should mean it is NOT “empty”, even if it has “null content” .

Good point. With COL1 and COL we can imagine to draw empty bins if the error is not 0.

I made this PR: Differentiate empty bins and bins with 0 content by couet · Pull Request #17948 · root-project/root · GitHub. With this PR the following code:

void col1(){
  auto h = new TH2D ("h2","h2",10,0.5,10.5,10,0.5,10.5);
  h->Fill(1.,1.,1.);
  h->Fill(2.,2.,2.);
  h->Fill(3.,3.,3.);
  h->Fill(4.,4.,4.);
  h->Fill(5.,5.,5.);
  h->Fill(6.,6.,6.);    
  h->Fill(6.,6.,-6.);
  printf("Error for bin #%d = %g\n",6,h->GetBinError(6,6));
  h->Fill(7.,7.,-7.);
  printf("Error for bin #%d = %g\n",8,h->GetBinError(8,8));
  h->Draw("COL1 Z");
}

Gives:

Note that bin (6,6) is drawn.

Does that also work if the bin is filled with a zero? If I do this modification (filling 2,2 with a zero):

{
  auto h = new TH2D ("h2","h2",10,0.5,10.5,10,0.5,10.5);
  h->Fill(1.,1.,1.);
  h->Fill(2.,2.,0);
  printf("Error for bin #%d = %g\n",2,h->GetBinError(2,2));
  h->Fill(3.,3.,3.);
  h->Fill(4.,4.,4.);
  h->Fill(5.,5.,5.);
  h->Fill(6.,6.,6.);    
  h->Fill(6.,6.,-6.);
  printf("Error for bin #%d = %g\n",6,h->GetBinError(6,6));
  h->Fill(7.,7.,-7.);
  printf("Error for bin #%d = %g\n",8,h->GetBinError(8,8));
  h->Draw("COL1 Z");
}

I get (on version 6.32.10, not using your PR):

Error for bin #2 = 0
Error for bin #6 = 8.48528
Error for bin #8 = 0

So, if (looking at the PR code) we just check for bincontent = error = 0, and when filling with zero the error is also zero, those bins which were filled directly with zero still won’t be seen.