Fuse TCanvas.Divide columns and share Y axis

Hi,
I have this kind of TCanvas:

I used:

c1.Divide(4, 1, 0.0001, 0.01)

I would like to reproduce this style:

I basically want the pads to share Y axis = reduce margins to 0 and remove some Y axis labels.

ROOT Version: 6.24/06 (I suppose PyROOT uses the same)
Platform: Windows Subsystem for Linux, Debian
Compiler: IDLE

{
   c1 = new TCanvas("c1","Divide Example");

   c1->Divide(4,1,0,0);
   for (int i = 1; i<=4; i++) {
      c1->cd(i);
      gPad->DrawFrame(0.,0.,1.,1);
   }
}

Axis ranges are modified.

mgGraphs[z].GetXaxis().SetRangeUser(minCoef, maxCoef) # -5, 5
mgGraphs[z].GetYaxis().SetRangeUser(0.6, 2)

It seems ignored.

Can titles be at the top of the canvas? Not inside.

For Y axis you should use SetMaximum and SetMinimum

And what about X and Y labels? And in the Y axis there a 2 at the top that is not fully shown.

I did

mgGraphs[z].GetXaxis().SetTitle("\\mathscr{O}")
mgGraphs[z].GetYaxis().SetTitle(r"\sigma/\sigma_{SM}")

Can titles be at the top of the canvas? Not inside.

What is the question about them ?

   c1->SetLeftMargin(0.3);
{
   c1 = new TCanvas("c1","Divide Example",800,400);
   c1->SetLeftMargin(0.3);
   auto g = new TGraph();
   g->AddPoint(1.,1.);
   g->AddPoint(2.,2.);

   c1->Divide(4,1,0,0);
   for (int i = 1; i<=4; i++) {
      c1->cd(i);
      g->Draw();
   }
   auto l = new TLegend();
   l->AddEntry(g,"Legend");
   l->SetTextSize(0.04);
   l->SetTextAlign(12);
   l->Draw();
   g->GetXaxis()->SetTitle("\\mathscr{O}");
   g->GetYaxis()->SetTitle("\\sigma/\\sigma_{SM}");
}

Mmm, I tried:

c1.SetLeftMargin(0.3)
c1.SetTopMargin(0.3) # since left margin didn't work, I tried this also
c1.Update()

But there is no change.

mgGraphs[z].GetXaxis().SetTitle("\\mathscr{O}")
mgGraphs[z].GetYaxis().SetTitle(r"\sigma/\sigma_{SM}")

But there are not titles.

The following works for me:

{
   c1 = new TCanvas("c1","Divide Example",800,400);
   c1->SetLeftMargin(0.3);
   c1->Divide(4,1,0,0);

   auto g1 = new TGraph();
   g1->AddPoint(1.,1.);
   g1->AddPoint(2.,2.);

   auto g2 = new TGraph();
   g2->AddPoint(3.,1.);
   g2->AddPoint(4.,2.);

   auto g3 = new TGraph();
   g3->AddPoint(5.,1.);
   g3->AddPoint(6.,2.);

   auto g4 = new TGraph();
   g4->AddPoint(7.,1.);
   g4->AddPoint(8.,2.);

   c1->cd(1); g1->Draw("AL");
   c1->cd(2); g2->Draw("AL");
   c1->cd(3); g3->Draw("AL");
   c1->cd(4); g4->Draw("AL");

   auto l = new TLegend(0.008,0.35,0.9,0.6);
   l->AddEntry(g1,"Legend 1");
   l->AddEntry(g2,"Legend 2");
   l->AddEntry(g3,"Legend 3");
   l->AddEntry(g4,"Legend 4");
   l->Draw();

   c1->cd(1)->Update();
   c1->cd(4)->Update();

   g1->GetYaxis()->SetTitle("\\sigma/\\sigma_{SM}");
   g4->GetXaxis()->SetTitle("\\mathscr{O}");
   c1->cd(1)->Modified();
   c1->cd(4)->Modified();
}

For me this:

ROOT.gStyle.SetTitleFontSize(0.1)
ROOT.gStyle.SetTitleXSize(0.04) ##########################
ROOT.gStyle.SetTitleYSize(0.04) ##########################
c1 = ROOT.TCanvas()
c1.SetLeftMargin(0.3) ##########################
c1.SetBottomMargin(0.1) ##########################
c1.Divide(4, 1, 0, 0)

graphs = {}
mgGraphs = {}
frames = {}

minCoef = -5
maxCoef = 5
N = 100
coefs = [minCoef + i*2*maxCoef/N for i in range(N)]
coefs.append(maxCoef)

colours = [ROOT.kRed, ROOT.kBlue, ROOT.kGreen, ROOT.kYellow]
coloursDict = {}
for i in range(len(keys)):
    coloursDict[keys[i]] = colours[i]

for z in range(4):
    c1.cd(z+1)
    frames[z] = ROOT.gPad.DrawFrame(0., 0., 1., 1)

    mgGraphs[z] = ROOT.TMultiGraph()

    for key in keys:
        if key == "SM": continue

        currKey = key + str(z)
        graphs[currKey] = ROOT.TGraph()
        graphs[currKey].SetLineColor(coloursDict[key])

        p0 = fitParameters[key][z][0]
        p1 = fitParameters[key][z][1]
        p2 = fitParameters[key][z][2]

        for i in range(N+1):
            coef = coefs[i]
            f = p0 + p1*coef + p2*coef**2
            #print(z, coef, f)
            graphs[currKey].AddPoint(coef, f)

        mgGraphs[z].Add(graphs[currKey], "L")
        
    mgGraphs[z].SetTitle(mll_cuts[z])
    
    if z == 0:
        mgGraphs[z].GetYaxis().SetTitle(r"\sigma/\sigma_{SM}") ##########################
    if z == 3:
        mgGraphs[z].GetXaxis().SetTitle(r"\mathscr{O}") ##########################
        
    c1.Update()

    # add (0, 1) as reference point (SM)
    graphs["SM" + str(z)] = ROOT.TGraph()
    graphs["SM" + str(z)].AddPoint(0, 1)
    graphs["SM" + str(z)].SetMarkerStyle(20)
    graphs["SM" + str(z)].SetMarkerSize(1)
    mgGraphs[z].Add(graphs["SM" + str(z)], "P")

    mgGraphs[z].GetXaxis().SetRangeUser(minCoef, maxCoef)
    mgGraphs[z].SetMinimum(0.6)
    mgGraphs[z].SetMaximum(2)
    mgGraphs[z].Draw("A")

c1.cd(1)
legend = ROOT.TLegend()
legend.SetTextSize(0.05)
legend.SetTextAlign(12)
for key in keys:
    legend.AddEntry(graphs[key + str(z)], key, "l")
legend.Draw()
        
c1.Update()
c1.Modified()

It looks horrible!

  1. No axes titles
  2. Multigraph 1 title overlapping
  3. Axis tick label sizes are different (and I set ROOT.gStyle.SetTitleXSize(0.04))
  4. The ā€œ2ā€ at the top of the y axis is cutted

(Ignore legend possition for now)

For 1 (axes titles), check out the note in the documentation for Divide():
https://root.cern/doc/master/classTPad.html#a2714ddd7ba72d5def84edc1fbaea8658
" Note3: in case xmargin <=0 and ymargin <= 0, there is no space between pads. The current pad margins are recomputed to optimize the layout."
So you can try putting your Set[ā€¦]Margin lines after doing Divide, but a probably better way is to, instead of doing this on the canvas, use Set[ā€¦]Margin on the Pad/s, since you need the space for titles inside the pads; in the case of the left margin, only on the first one (if z==0 in your code):

   c1->cd(1); gPad->SetLeftMargin(0.3); g1->Draw("AL");
   c1->cd(2); g2->Draw("AL");
   c1->cd(3); g3->Draw("AL");
   c1->cd(4); g4->Draw("AL");

And also use SetTopMargin accordingly (for all pads in this case), to solve point 4 (cutting the ā€œ2ā€).

Does the C++ macro I sent you work for you ? I think it does what you are asking for.

BTW: it seems this thread is about the same macro. May be we can have only one thread ?

@couet , your code works, but it does not give the result I would like.

Yes, it is the same macro, but I made different threads because the problems are different. That thread has been solved recently.

It does not work.

It does not work.


So I made a minimum macro in C++:

void TEST(){
	
	gStyle->SetTitleXSize(0.04);
	gStyle->SetTitleYSize(0.04);
	
	TCanvas* c1 = new TCanvas();
	
	c1->SetLeftMargin(0.15);
	c1->SetTopMargin(0.15);
	c1->Divide(4, 1, 0, 0);
	
	TGraph* gr;
	for(int i = 0; i < 4; ++i){
		
		c1->cd(i+1);
		
		gr = new TGraph();
		gr->AddPoint(1,1);
		gr->SetMinimum(0.6);
		gr->SetMaximum(2);
		gr->GetXaxis()->SetRangeUser(-5,5);
		gr->SetTitle("\\splitline{m_{ll} \\in [101.0, 117.96] GeV}{\\sigma_{SM}=(1876\\pm8)\\times10^{-6}pb}");
		
		if(i == 0){
			//gPad->SetLeftMargin(0.3);
			gr->GetYaxis()->SetTitle("\\sigma/\\sigma_{SM}");
		}
		if(i == 3){
			gr->GetXaxis()->SetTitle("\\mathscr{O}");
		}
		
		gr->Draw("AP");
		
		c1->Update();
	}
	
	c1->Print("test.pdf");
	
}

And I get

So can you modify it to get what I want? Iā€™m out of ideas. Problems:

  1. ā€œ2ā€ at the top-left Y axis is cutted, even with TopMargin
  2. Title of pad 1 is not centered
  3. X axis labels have different sizes.

If you do what I said it works (left and top margins).

Sorry, I was missplacing things.

void TEST(){
	
	gStyle->SetTitleXSize(0.04);
	gStyle->SetTitleYSize(0.04);
	
	TCanvas* c1 = new TCanvas();
	
	c1->Divide(4, 1, 0, 0);
	
	TGraph* gr;
	for(int i = 0; i < 4; ++i){
		
		c1->cd(i+1);
		
		if(i==0){
			gPad->SetLeftMargin(0.5);
		}
		
		gPad->SetTopMargin(0.008);
		
		gr = new TGraph();
		gr->AddPoint(1,1);
		gr->SetMinimum(0.6);
		gr->SetMaximum(2);
		gr->GetXaxis()->SetRangeUser(-5,5);
		gr->SetTitle("\\splitline{m_{ll} \\in [101.0, 117.96] GeV}{\\sigma_{SM}=(1876\\pm8)\\times10^{-6}pb}");
		
		if(i == 0){
			//gPad->SetLeftMargin(0.3);
			gr->GetYaxis()->SetTitle("\\sigma/\\sigma_{SM}");
		}
		if(i == 3){
			gr->GetXaxis()->SetTitle("\\mathscr{O}");
		}
		
		gr->Draw("AP");
		
		c1->Update();
	}
	
	c1->Print("test.pdf");
	
}

For the left side, if I did as you said, it does not work. I took a big left margin to see the problem with the title, if I take a smaller margir, the problem is still there, but more difficult to notice.

The title is centered on the Pad by default, not ton the Frame. It can be moved the following way.

void TEST(){
   gStyle->SetTitleXSize(0.04);
   gStyle->SetTitleYSize(0.04);

   TCanvas* c1 = new TCanvas();

   c1->Divide(4, 1, 0, 0);

   TGraph* gr;
   for(int i = 0; i < 4; ++i){

      c1->cd(i+1);

      if( i==0) gPad->SetLeftMargin(0.5);
      gPad->SetTopMargin(0.1);

      gr = new TGraph();
      gr->AddPoint(1,1);
      gr->SetMinimum(0.6);
      gr->SetMaximum(2);
      gr->GetXaxis()->SetRangeUser(-5,5);
      gr->SetTitle("\\splitline{m_{ll} \\in [101.0, 117.96] GeV}{\\sigma_{SM}=(1876\\pm8)\\times10^{-6}pb}");

      if(i == 0){
         //gPad->SetLeftMargin(0.3);
         gr->GetYaxis()->SetTitle("\\sigma/\\sigma_{SM}");
      }
      if(i == 3){
         gr->GetXaxis()->SetTitle("\\mathscr{O}");
      }

      gr->Draw("AP");

      if( i==0) {
         gPad->Update();
         TPaveText* title = (TPaveText*)gPad->FindObject("title");
         title->SetX1NDC(0.6);
         title->SetX2NDC(0.9);
         title->SetTextSize(0.03);
      }
   }
}

Yes, that works. I consider there are still two more problems:

  1. Pads have diferent sizes
  2. X axis tick labels have different size for pad 1 (this comes from 1))

Pad have the same size but the frames inside the pads have different sizes as you change the margin of the first one. There is a macro doing what you are looking for. I adapted to your case. It computes the frame to make sure le plots have all the same despite despite the fact the first and last pads have different sizes fro the other pads.

void CanvasPartition(TCanvas *C,const Int_t Nx = 2,const Int_t Ny = 2,
                     Float_t lMargin = 0.15, Float_t rMargin = 0.05,
                     Float_t bMargin = 0.15, Float_t tMargin = 0.05);

void test2()
{

   gStyle->SetOptStat(0);

   TCanvas *C = (TCanvas*) gROOT->FindObject("C");
   if (C) delete C;
   C = new TCanvas("C","canvas",1024,640);
   C->SetFillStyle(4000);

   // Number of PADS
   const Int_t Nx = 4;
   const Int_t Ny = 1;

   // Margins
   Float_t lMargin = 0.12;
   Float_t rMargin = 0.05;
   Float_t bMargin = 0.15;
   Float_t tMargin = 0.03;

   // Canvas setup
   CanvasPartition(C,Nx,Ny,lMargin,rMargin,bMargin,tMargin);

   TPad *pad[Nx][Ny];

   for (Int_t i=0;i<Nx;i++) {
      for (Int_t j=0;j<Ny;j++) {

         auto gr = new TGraph();
         gr->AddPoint(1,1);
         gr->SetMinimum(0.6);
         gr->SetMaximum(2);
         gr->SetTitle("\\splitline{m_{ll} \\in [101.0, 117.96] GeV}{\\sigma_{SM}=(1876\\pm8)\\times10^{-6}pb}");
         if (i == 0) gr->GetYaxis()->SetTitle("\\sigma/\\sigma_{SM}");
         if (i == 3) gr->GetXaxis()->SetTitle("\\mathscr{O}");


         C->cd(0);

         // Get the pads previously created.
         char pname[16];
         sprintf(pname,"pad_%i_%i",i,j);
         pad[i][j] = (TPad*) gROOT->FindObject(pname);
         pad[i][j]->Draw();
         pad[i][j]->cd();

         // Size factors
         Float_t xFactor = pad[0][0]->GetAbsWNDC()/pad[i][j]->GetAbsWNDC();
         Float_t yFactor = pad[0][0]->GetAbsHNDC()/pad[i][j]->GetAbsHNDC();

         char hname[16];
         sprintf(hname,"h_%i_%i",i,j);
         TH1F *hFrame = (TH1F*) gr->GetHistogram()->Clone(hname);
         hFrame->Reset();
         hFrame->Draw();

         // y axis range
         hFrame->GetYaxis()->SetRangeUser(0.0001,1.2*gr->GetHistogram()->GetMaximum());

         // Format for y axis
         hFrame->GetYaxis()->SetLabelFont(43);
         hFrame->GetYaxis()->SetLabelSize(16);
         hFrame->GetYaxis()->SetLabelOffset(0.02);
         hFrame->GetYaxis()->SetTitleFont(43);
         hFrame->GetYaxis()->SetTitleSize(16);
         hFrame->GetYaxis()->SetTitleOffset(2);

         hFrame->GetYaxis()->SetNdivisions(505);

         // TICKS Y Axis
         hFrame->GetYaxis()->SetTickLength(xFactor*0.04/yFactor);

         // Format for x axis
         hFrame->GetXaxis()->SetLabelFont(43);
         hFrame->GetXaxis()->SetLabelSize(16);
         hFrame->GetXaxis()->SetLabelOffset(0.02);
         hFrame->GetXaxis()->SetTitleFont(43);
         hFrame->GetXaxis()->SetTitleSize(16);
         hFrame->GetXaxis()->SetTitleOffset(1);
         hFrame->GetXaxis()->SetNdivisions(505);

         // TICKS X Axis
         hFrame->GetXaxis()->SetTickLength(yFactor*0.06/xFactor);

         gr->Draw("P");
         gPad->Update();
         TPaveText* title = (TPaveText*)gPad->FindObject("title");
         if (i==0) {
            title->SetX1NDC(0.4);
            title->SetY1NDC(0.8);
            title->SetX2NDC(0.89);
            title->SetY2NDC(0.99);
         } else if (i==Nx-1) {
            title->SetX1NDC(0.1);
            title->SetY1NDC(0.8);
            title->SetX2NDC(0.75);
            title->SetY2NDC(0.99);
         } else {
            title->SetX1NDC(0.1);
            title->SetY1NDC(0.8);
            title->SetX2NDC(0.89);
            title->SetY2NDC(0.99);
         }
      }
   }

   gPad->Modified();
   gPad->Update();
   C->cd();
}



void CanvasPartition(TCanvas *C,const Int_t Nx,const Int_t Ny,
                     Float_t lMargin, Float_t rMargin,
                     Float_t bMargin, Float_t tMargin)
{
   if (!C) return;

   // Setup Pad layout:
   Float_t vSpacing = 0.0;
   Float_t vStep  = (1.- bMargin - tMargin - (Ny-1) * vSpacing) / Ny;

   Float_t hSpacing = 0.0;
   Float_t hStep  = (1.- lMargin - rMargin - (Nx-1) * hSpacing) / Nx;

   Float_t vposd,vposu,vmard,vmaru,vfactor;
   Float_t hposl,hposr,hmarl,hmarr,hfactor;

   for (Int_t i=0;i<Nx;i++) {

      if (i==0) {
         hposl = 0.0;
         hposr = lMargin + hStep;
         hfactor = hposr-hposl;
         hmarl = lMargin / hfactor;
         hmarr = 0.0;
      } else if (i == Nx-1) {
         hposl = hposr + hSpacing;
         hposr = hposl + hStep + rMargin;
         hfactor = hposr-hposl;
         hmarl = 0.0;
         hmarr = rMargin / (hposr-hposl);
      } else {
         hposl = hposr + hSpacing;
         hposr = hposl + hStep;
         hfactor = hposr-hposl;
         hmarl = 0.0;
         hmarr = 0.0;
      }

      for (Int_t j=0;j<Ny;j++) {

         if (j==0) {
            vposd = 0.0;
            vposu = bMargin + vStep;
            vfactor = vposu-vposd;
            vmard = bMargin / vfactor;
            vmaru = 0.0;
         } else if (j == Ny-1) {
            vposd = vposu + vSpacing;
            vposu = vposd + vStep + tMargin;
            vfactor = vposu-vposd;
            vmard = 0.0;
            vmaru = tMargin / (vposu-vposd);
         } else {
            vposd = vposu + vSpacing;
            vposu = vposd + vStep;
            vfactor = vposu-vposd;
            vmard = 0.0;
            vmaru = 0.0;
         }

         C->cd(0);

         char name[16];
         sprintf(name,"pad_%i_%i",i,j);
         TPad *pad = (TPad*) gROOT->FindObject(name);
         if (pad) delete pad;
         pad = new TPad(name,"",hposl,vposd,hposr,vposu);
         pad->SetLeftMargin(hmarl);
         pad->SetRightMargin(hmarr);
         pad->SetBottomMargin(vmard);
         pad->SetTopMargin(vmaru);

         pad->SetFrameBorderMode(0);
         pad->SetBorderMode(0);
         pad->SetBorderSize(0);

         pad->Draw();
      }
   }
}

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.