Axis labels and ticks in logx scale

Dear ROOT friends,

Recently I had to make some plots in logx scale, normally in the range between 100 and 300, or between 100 and 600. The default behavior of ROOT (as far as I can tell) in such range and scale is to not draw any label (or just one), and a minimal number of ticks – two or three. I have copied below the code that I have written to overcome this logx problem, and I have a couple of questions:

  1. Is this issue/feature known? If so, can somebody point me to the right commands?

  2. If this issue isn’t known, or if it isn’t fixed yet, can somebody help me improving
    the code below? For example, the tick sizes are not automatic.

Finally, I hope you can see in this image how my plots looks like by using the code below.

Thanks and best regards,
Jonatan

Float_t mhmin = 100;
Float_t mhmax = 300;

Float_t uxmin = canvas->GetUxmin();
Float_t uxmax = canvas->GetUxmax();
Float_t uymin = canvas->GetUymin();

if (canvas->GetLogx()) {

ExpBand95->GetXaxis()->SetNdivisions(0);

TLine tick;

tick.SetLineWidth(1);
tick.SetLineColor(1);

for (Int_t i=100; i<=600; i+=10) {

  if (i < mhmin || i > mhmax) continue;

  Float_t xx = i;

  if (canvas->GetLogy())
tick.DrawLine(xx, pow(10,uymin), xx, pow(10,uymin) + (i%100 == 0 ? 0.025 : 0.01));
  else
tick.DrawLine(xx, uymin, xx, uymin + (i%100 == 0 ? 0.75 : 0.3));
}


// TLatex
//--------------------------------------------------------------------------
Float_t ylatex = (canvas->GetLogy()) ? pow(10,uymin) - 0.02 : uymin - 0.75;

Float_t xbins[6] = {100, 200, 300, 400, 500, 600};

while (mhmin > xbins[0]) xbins[0] += 10;

for (Int_t i=0; i<6; i++) {
  
  if (xbins[i] < mhmin || xbins[i] > mhmax) continue;

  TLatex* latex = new TLatex(xbins[i], ylatex, Form("%.0f", xbins[i]));
  
  latex->SetTextAlign(  22);
  latex->SetTextFont (  42);
  latex->SetTextSize (0.05);

  latex->Draw("same");
}

http://root.cern.ch/root/html/TAxis.html#TAxis:SetMoreLogLabels
http://root.cern.ch/root/html/TAttAxis.html#TAttAxis:SetNdivisions
http://root.cern.ch/root/html/TStyle.html#TStyle:SetNdivisions

Dear Pepe Le Pew,

Thank you for your help, but unless I am missing something, it does not work. Please try running the simple code below. In canvas c1 you can see what I get with my patch. In canvas c2 I have tried to reproduce it with the methods that you suggest, but I have failed.

Best,
Jonatan

void testLogx()
{
  // Prepare dummy histograms
  //----------------------------------------------------------------------------
  Double_t xlow   = 100;
  Double_t xup    = 400;
  Int_t    nbinsx = Int_t(xup - xlow);

  TF1* f1 = new TF1("f1", "gaus(0)", xlow, xup);

  f1->SetParameter(0, 1);
  f1->SetParameter(1, 0.50 * (xup + xlow));
  f1->SetParameter(2, 0.25 * (xup - xlow));

  TH1F* h1 = new TH1F("h1", "h1", nbinsx, xlow, xup);
  TH1F* h2 = new TH1F("h2", "h2", nbinsx, xlow, xup);

  h1->FillRandom("f1", 10000);
  h2->FillRandom("f1", 10000);


  // Draw the first canvas
  //----------------------------------------------------------------------------
  TCanvas* c1 = new TCanvas("c1", "c1", 15, 50, 700, 500);

  c1->SetLogx();
  c1->SetLogy();

  h1->Draw();

  c1->Update();

  Float_t uxmin = c1->GetUxmin();
  Float_t uxmax = c1->GetUxmax();
  Float_t uymin = c1->GetUymin();


  // x-axis ticks
  //----------------------------------------------------------------------------
  if (c1->GetLogx()) {

    h1->GetXaxis()->SetNdivisions(0);

    TLine tick;

    tick.SetLineWidth(1);
    tick.SetLineColor(1);

    Float_t length = (c1->GetLogy()) ? 0.2 : 2.0;

    for (Int_t i=xlow; i<=xup; i+=10) {
	
      Float_t xx = i;
      
      if (c1->GetLogy())
      	tick.DrawLine(xx, pow(10,uymin), xx, pow(10,uymin) + (i%100 == 0 ? 2*length : length));
      else
      	tick.DrawLine(xx, uymin, xx, uymin + (i%100 == 0 ? 2*length : length));
    }


    // x-axis labels
    //--------------------------------------------------------------------------
    Float_t ylatex = (c1->GetLogy()) ? pow(10,uymin) - 1.3*length : uymin - 2*length;
    
    for (Int_t i=xlow; i<=xup; i+=100) {
      
      Float_t xx = i;
    
      TLatex* latex = new TLatex(xx, ylatex, Form("%.0f", xx));
      
      latex->SetTextAlign(  22);
      latex->SetTextFont (  42);
      latex->SetTextSize (0.035);
    
      latex->Draw("same");
    }
  }


  // Draw the second canvas
  //----------------------------------------------------------------------------
  TCanvas* c2 = new TCanvas("c2", "c2", 730, 50, 700, 500);

  gStyle->SetNdivisions(999, "x");

  c2->SetLogx();
  c2->SetLogy();

  h2->GetXaxis()->SetMoreLogLabels();
  h2->GetXaxis()->SetNdivisions(999, kTRUE);
  h2->GetXaxis()->SetNoExponent();

  h2->Draw();
}

You can get the labels 100 and 400 doing:

Double_t xlow = 99.999;
Double_t xup = 400.01;

but there will be no way to get the intermediate ticks you added.

[quote=“couet”]You can get the labels 100 and 400 doing:

Double_t xlow = 99.999;
Double_t xup = 400.01;

but there will be no way to get the intermediate ticks you added.[/quote]

Dear couet,

Thanks a lot, that makes perfect sense. Is there any way to make the ticks
that I have added more automatic? I am referring to their position and size.

Best,
Jonatan

The additional ticks are not easy to get with the current version of TGaxis.
What you did right now makes sense.

[quote=“couet”]The additional ticks are not easy to get with the current version of TGaxis.
What you did right now makes sense.[/quote]

Thank you couet.

Last question: how about the tick size? With what I have done, I need to change
the tick size by hand, when I change the vertical axis range. Is there a way to give
them an absolute size?

Best,
Jonatan

[quote=“couet”]You can get the labels 100 and 400 doing:

Double_t xlow = 99.999;
Double_t xup = 400.01;

but there will be no way to get the intermediate ticks you added.[/quote]

I does not always work. For example, I cannot get the label 150 doing:

Double_t xup = 150.01;

I think, what Olivier tries to say is … with the “current implementation” … in “log scale” you can get ticks at 100, 200, 300, 400, 500, 600, 700(NO label), 800(NO label), 900(NO label), 1000 … but nothing in between.

I think some improvement need to be done fro your case. But I guess you need to Draw your axis NOW therefore the approach you took is the right one. I would event not use the trick I mentioned just use your “patch” .

How about this: [code]void testLogx()
{
// Prepare dummy histograms
//----------------------------------------------------------------------------
Double_t xlow = 99.999;
Double_t xup = 400.01; // try 400.01 or 150.01
Int_t nbinsx = Int_t(xup - xlow);

TF1* f1 = new TF1(“f1”, “gaus(0)”, xlow, xup);

f1->SetParameter(0, 1);
f1->SetParameter(1, 0.50 * (xup + xlow));
f1->SetParameter(2, 0.25 * (xup - xlow));

TH1F* h1 = new TH1F(“h1”, “h1”, nbinsx, xlow, xup);
TH1F* h2 = new TH1F(“h2”, “h2”, nbinsx, xlow, xup);

h1->FillRandom(“f1”, 10000);
h2->FillRandom(“f1”, 10000);

// Draw the first canvas
//----------------------------------------------------------------------------
TCanvas* c1 = new TCanvas(“c1”, “c1”, 15, 50, 700, 500);

c1->SetLogx();
c1->SetLogy();

h1->Draw();

c1->Update();

Float_t uxmin = c1->GetUxmin();
Float_t uxmax = c1->GetUxmax();
Float_t uymin = c1->GetUymin();

// x-axis ticks
//----------------------------------------------------------------------------
if (c1->GetLogx()) {

h1->GetXaxis()->SetNdivisions(0);

TLine tick;

tick.SetLineWidth(1);
tick.SetLineColor(1);

Float_t length = (c1->GetLogy()) ? 0.2 : 2.0;

for (Int_t i=xlow; i<=xup; i+=10) {

  Float_t xx = i;
 
  if (c1->GetLogy())
     tick.DrawLine(xx, pow(10,uymin), xx, pow(10,uymin) + (i%100 == 0 ? 2*length : length));
  else
     tick.DrawLine(xx, uymin, xx, uymin + (i%100 == 0 ? 2*length : length));
}


// x-axis labels
//--------------------------------------------------------------------------
Float_t ylatex = (c1->GetLogy()) ? pow(10,uymin) - 1.3*length : uymin - 2*length;

for (Int_t i=xlow; i<=xup; i+=100) {
 
  Float_t xx = i;

  TLatex* latex = new TLatex(xx, ylatex, Form("%.0f", xx));
 
  latex->SetTextAlign(  22);
  latex->SetTextFont (  42);
  latex->SetTextSize (0.035);

  latex->Draw("same");
}

}

// Draw the second canvas
//----------------------------------------------------------------------------
TCanvas* c2 = new TCanvas(“c2”, “c2”, 730, 50, 700, 500);

c2->SetLogx();
c2->SetLogy();

h2->GetXaxis()->SetMoreLogLabels();
h2->GetXaxis()->SetNoExponent();

h2->Draw();
gPad->Modified(); gPad->Update(); // make sure it’s drawn

// http://root.cern.ch/root/html/TF1.html#TF1:TF1@1
// http://root.cern.ch/root/html/TFormula.html#TFormula:Analyze
TF1 *f_h2_log10_x_axis = new TF1(“f_h2_log10_x_axis”, // name
"log10(x)", // formula
h2->GetXaxis()->GetXmin(), // xmin
h2->GetXaxis()->GetXmax()); // xmax

// http://root.cern.ch/root/html/TGaxis.html#TGaxis:TGaxis@3
// http://root.cern.ch/root/html/TGaxis.html#TGaxis:PaintAxis
TGaxis *a = new TGaxis(h2->GetXaxis()->GetXmin(), // xmin
h2->GetYaxis()->GetXmin(), // ymin
h2->GetXaxis()->GetXmax(), // xmax
h2->GetYaxis()->GetXmin(), // ymax
"f_h2_log10_x_axis", // funcname
100006, // ndiv (try 100006 or 506, don’t try 1006)
“BS”, // chopt (try “BS” or “UBS”)
0.0); // gridlength

// a->SetTickSize(h2->GetTickLength(“X”)); // use “the same” size
a->SetTickSize(1.5 * h2->GetTickLength(“X”)); // make it bigger
h2->SetTickLength(0.0, “X”); // get rid of “original” ticks

if (!(TString(a->GetOption())).Contains(“U”)) {
a->SetLabelFont(h2->GetLabelFont(“X”)); // use “the same” font
a->SetLabelSize(h2->GetLabelSize(“X”)); // use “the same” size
h2->SetLabelSize(0.0, “X”); // get rid of “original” labels
}

a->Draw();
gPad->Modified(); gPad->Update(); // make sure it’s redrawn
}[/code] Note: there is a bug (at least in ROOT 5.28, 5.32 and 5.34) somewhere in the procedure that manages the “number of 2nd divisions” for a TGaxis. One asks for 10 but one gets 9, so it’s better to avoid it (instead of 10 “2nd divisions”, one can use 10 “3rd divisions”, which work fine, but are “smaller” unfortunately).

Thank you Pepe Le Pew, it works!

Please find below a final comparison between my hand-made version and Pepe’s code. Finally I have managed to make the tick size automatic :slight_smile: and now I can choose:

  • the separation between small ticks with step_normal;
  • the separation between large ticks with factor_large;
  • the size of the small ticks as a fraction of the y-length;
  • the size of the large ticks as a fraction of the y-length;
  • it draws when necessary (not always) the label that corresponds to xup.

Best regards,
Jonatan

#include "TAxis.h"
#include "TCanvas.h"
#include "TF1.h"
#include "TGaxis.h"
#include "TH1.h"
#include "TLatex.h"
#include "TLine.h"
#include "TStyle.h"


void testLogx(Double_t xlow            = 100,
	      Double_t xup             = 109,
	      Int_t    step_normal     = 1,
	      Int_t    factor_large    = 2,
	      Float_t  fraction_normal = 0.015,
	      Float_t  fraction_large  = 0.035,
	      Bool_t   setLogx         = true,
	      Bool_t   setLogy         = true)
{
  gStyle->SetOptStat(0);
  

  // Prepare dummy histograms
  //----------------------------------------------------------------------------
  TF1* f1 = new TF1("f1", "gaus(0)", xlow, xup);

  f1->SetParameters(1, 0.5*(xup+xlow), 0.2*(xup-xlow));

  TH1F* h1 = new TH1F("h1", "h1", 50*Int_t(xup-xlow), xlow, xup);

  h1->FillRandom("f1", 100000);

  TH1F* h2 = (TH1F*)h1->Clone("h2");

  h2->SetNameTitle("h2", "h2");


  // Draw the first canvas
  //----------------------------------------------------------------------------
  TCanvas* c1 = new TCanvas("c1", "c1", 15, 50, 700, 500);

  c1->SetLogx(setLogx);
  c1->SetLogy(setLogy);

  h1->Draw();
  h1->SetTitle("");

  c1->Update();

  Float_t uymin = c1->GetUymin();
  Float_t uymax = c1->GetUymax();
  
  
  // x-axis ticks
  //----------------------------------------------------------------------------
  if (c1->GetLogx()) {

    Int_t ixlow      = Int_t(xlow);
    Int_t ixup       = Int_t(xup);
    Int_t step_large = factor_large * step_normal;

    h1->GetXaxis()->SetNdivisions(0);
    
    Float_t ymin = (c1->GetLogy()) ? pow(10, uymin) : uymin;
    Float_t ymax = (c1->GetLogy()) ? pow(10, uymax) : uymax;

    Float_t bottom_tick_normal = fraction_normal * (uymax - uymin);
    Float_t bottom_tick_large  = fraction_large  * (uymax - uymin);

    Float_t top_tick_normal = fraction_normal * (uymax - uymin);
    Float_t top_tick_large  = fraction_large  * (uymax - uymin);

    if (c1->GetLogy()) {

      bottom_tick_normal = pow(10, uymin + bottom_tick_normal) - pow(10, uymin);
      bottom_tick_large  = pow(10, uymin + bottom_tick_large)  - pow(10, uymin);

      top_tick_normal = pow(10, uymax) - pow(10, uymax - top_tick_normal);
      top_tick_large  = pow(10, uymax) - pow(10, uymax - top_tick_large);
    }

    TLine tick;

    for (Int_t i=ixlow; i<=ixup; i+=step_normal) {

      Float_t xx = i;

      tick.DrawLine(xx, ymin, xx, ymin + ((i-ixlow)%step_large == 0 ? bottom_tick_large : bottom_tick_normal));
      tick.DrawLine(xx, ymax, xx, ymax - ((i-ixlow)%step_large == 0 ? top_tick_large    : top_tick_normal));
    }


    // x-axis labels
    //--------------------------------------------------------------------------
    Float_t ylatex = ymin - bottom_tick_normal;

    if ((ixup-ixlow)%step_large >= step_normal) {

      TLatex* latex = new TLatex(xup, ylatex, Form("%.0f", xup));
      
      latex->SetTextAlign(   23);
      latex->SetTextFont (   42);
      latex->SetTextSize (0.035);
      latex->Draw("same");
    }

    for (Int_t i=ixlow; i<ixup; i+=step_large) {

      Float_t xx = i;

      TLatex* latex = new TLatex(xx, ylatex, Form("%.0f", xx));

      latex->SetTextAlign(   23);
      latex->SetTextFont (   42);
      latex->SetTextSize (0.035);
      latex->Draw("same");
    }
  }


  // Draw the second canvas
  //----------------------------------------------------------------------------
  TCanvas* c2 = new TCanvas("c2", "c2", 730, 50, 700, 500);

  c2->SetLogx();
  c2->SetLogy();

  h2->GetXaxis()->SetMoreLogLabels();
  h2->GetXaxis()->SetNoExponent();

  h2->Draw();
  h2->SetTitle("");

  gPad->Modified();
  gPad->Update(); // make sure it's drawn
  
  // http://root.cern.ch/root/html/TF1.html#TF1:TF1@1
  // http://root.cern.ch/root/html/TFormula.html#TFormula:Analyze
  TF1 *f_h2_log10_x_axis = new TF1("f_h2_log10_x_axis", // name
				   "log10(x)", // formula
				   h2->GetXaxis()->GetXmin(), // xmin
				   h2->GetXaxis()->GetXmax()); // xmax

  f_h2_log10_x_axis->SetLineColor(kBlue);

  // http://root.cern.ch/root/html/TGaxis.html#TGaxis:TGaxis@3
  // http://root.cern.ch/root/html/TGaxis.html#TGaxis:PaintAxis
  TGaxis *a = new TGaxis(h2->GetXaxis()->GetXmin(), // xmin
			 h2->GetYaxis()->GetXmin(), // ymin
			 h2->GetXaxis()->GetXmax(), // xmax
			 h2->GetYaxis()->GetXmin(), // ymax
			 "f_h2_log10_x_axis", // funcname
			 506, // ndiv (try 100006 or 506, don't try 1006)
			 "BS", // chopt (try "BS" or "UBS")
			 0.0); // gridlength

  // a->SetTickSize(h2->GetTickLength("X")); // use "the same" size
  a->SetTickSize(1.5 * h2->GetTickLength("X")); // make it bigger
  h2->SetTickLength(0.0, "X"); // get rid of "original" ticks

  if (!(TString(a->GetOption())).Contains("U")) {
    a->SetLabelFont(h2->GetLabelFont("X")); // use "the same" font
    a->SetLabelSize(h2->GetLabelSize("X")); // use "the same" size
    h2->SetLabelSize(0.0, "X"); // get rid of "original" labels
  }

  a->Draw();
  gPad->Modified(); gPad->Update(); // make sure it's redrawn
}