Make another Y axis normalized with a 2nd dataset

Dear Experts,
I am fairly new to RooFit so I apologize in advance if this matter is trivial.

I am trying to plot on the same graph 2 datasets and their fit. However, each dataset does not contain the same number of events, meaning that when I plot the 2nd dataset, its normalization overwrites the previous one. Then I plot the fits and they get both normalized to the size of the 2nd dataset.
I would therefore like to know if it is possible to somehow create a second Y axis (that would display on the right side of the plot), and that the 2nd dataset normalizes this second Y axis.

Here is my plot so far:
Bdrho+0.05Kst.pdf (32.8 KB)

As you can see, the red fit goes too high in regard of the red data, but I made sure it does not have this shift when I plot it alone. It is due to the renormalization, as I plot the blue data after the red ones. However the shift is quite small because both dataset contains a close amount of events, but different nonetheless which causes this normalization issue.

Cheers,
Tristan

You need a 2nd pad like in this example.
Instead of making a TGaxis you can use X+ and Y+ options.
If you do not succeed post an example here and I will fix it.

Hello Couet,
Thanks for you prompt reply.
I’ve been trying to work it out using the tutorial you linked but I have not really been successful to say the least.
I made you a MWE, as you suggested:
MWE_2Yaxis.py (2.3 KB)

hopefully you’ll be able to settle it without too much trouble ! Thank you for your help and your time. In the meantime I’ll keep trying on my own.

Cheers,
Tristan

It seems your file has problems:

% python MWE_2Yaxis.py 
  File "MWE_2Yaxis.py", line 10
SyntaxError: Non-ASCII character '\xc3' in file MWE_2Yaxis.py on line 10, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

The code is relatively short so I’ll paste it here and that should solve the issue:

import ROOT
B_M = ROOT.RooRealVar("B_M","m[GeV/c^{2}]",2.5,6)
meanBdKst = ROOT.RooRealVar("meanBdKst", "mean of BdKst", 5.28, 5.1, 5.4)
sigmaBdKst = ROOT.RooRealVar("sigma1BdKst", "width of BdKst", 0.017, 0.01, 0.03)

meanBdrho = ROOT.RooRealVar("meanBdrho", "mean of Bdrho", 5.15, 5.1, 5.4)
sigma1Bdrho = ROOT.RooRealVar("sigma1Bdrho", "width of Bdrho", 0.01, 0.005, 0.02)

# Making gaussians :
fitBdKst = ROOT.RooGaussian("fitBdKst","Fit du M Substitué",B_M, meanBdKst, sigmaBdKst)
fitBdrho = ROOT.RooGaussian("fitBdrho", "Signal Bdrho", B_M, meanBdrho, sigma1Bdrho) 

# Generating data:
dataBdKst = fitBdKst.generate(ROOT.RooArgSet(B_M), 3000)
dataBdrho = fitBdrho.generate(ROOT.RooArgSet(B_M), 20000)

# Fitting gaussians:
fitBdKst.fitTo(dataBdKst)
fitBdrho.fitTo(dataBdrho)



# Plot :
framedata = B_M.frame(ROOT.RooFit.Title("B^{0} -> #rho #mu #mu + 5% B^{0} -> K #pi #mu #mu "), ROOT.RooFit.Range(5, 5.4))

dataBdKst.plotOn(framedata, ROOT.RooFit.MarkerColor(ROOT.kBlue), ROOT.RooFit.Name("dataBdKst"))
fitBdKst.plotOn(framedata, ROOT.RooFit.LineColor(ROOT.kBlack), ROOT.RooFit.LineWidth(1), ROOT.RooFit.MoveToBack())
fitBdKst.plotOn(framedata, ROOT.RooFit.LineColor(ROOT.kBlack), ROOT.RooFit.DrawOption("F"), ROOT.RooFit.FillColor(ROOT.kBlue-9), ROOT.RooFit.Name("fitBdKst"), ROOT.RooFit.MoveToBack())

dataBdrho.plotOn(framedata, ROOT.RooFit.MarkerColor(ROOT.kRed), ROOT.RooFit.Name("dataBdrho"))
fitBdrho.plotOn(framedata, ROOT.RooFit.LineColor(ROOT.kBlack), ROOT.RooFit.LineWidth(1), ROOT.RooFit.MoveToBack())
fitBdrho.plotOn(framedata, ROOT.RooFit.LineColor(ROOT.kBlack), ROOT.RooFit.DrawOption("F"), ROOT.RooFit.FillColor(ROOT.kRed-9), ROOT.RooFit.Name("fitBdrho"), ROOT.RooFit.MoveToBack())

DataBdrho = framedata.findObject("dataBdrho")
DataBdKst = framedata.findObject("dataBdKst")
FitBdrho = framedata.findObject("fitBdrho")
FitBdKst = framedata.findObject("fitBdKst")

Frame = ROOT.TCanvas("Bdrho+0.05Kst", "Bdrho+0.05Kst", 1200, 800)
framedata.SetAxisColor(color=ROOT.kRed, axis="Y")
framedata.SetLabelColor(color=ROOT.kRed, axis="Y")
framedata.Draw()
l = ROOT.TLegend(0.7, 0.7, 0.88, 0.88)
l.AddEntry(DataBdrho, "Data B^{0} -> #rho #mu #mu", "pe" )
l.AddEntry(DataBdKst, "Data B^{0} -> K^{*0} #mu #mu", "pe" )
l.AddEntry(FitBdrho, "Fit B^{0} -> #rho #mu #mu", "f" )
l.AddEntry(FitBdKst, "Fit B^{0} -> K^{*0} #mu #mu", "f" )
l.SetBorderSize(0)
l.Draw()

Sorry for the inconvenience and thanks again !

Python does not like the French é

That’s right, my bad. I thought I had removed them all but I missed that one. Surprisingly enough, it didn’t cause any crash on my side

Your script does not produce any visible graphics (no image output file, no window on screen)

I am quite puzzled. I have reduced the MWE to its sheer minimum, and I got ridden of the “é” so the file should be readable now:
MWE_2Yaxis.py (1.6 KB)

But just in case I’ll give directly the code here:

import ROOT
B_M = ROOT.RooRealVar("B_M","m[GeV/c^{2}]",2.5,6)
meanBdKst = ROOT.RooRealVar("meanBdKst", "mean of BdKst", 5.28, 5.1, 5.4)
sigmaBdKst = ROOT.RooRealVar("sigma1BdKst", "width of BdKst", 0.017, 0.01, 0.03)

meanBdrho = ROOT.RooRealVar("meanBdrho", "mean of Bdrho", 5.15, 5.1, 5.4)
sigma1Bdrho = ROOT.RooRealVar("sigma1Bdrho", "width of Bdrho", 0.01, 0.005, 0.02)

# Making gaussians :
fitBdKst = ROOT.RooGaussian("fitBdKst","Fit of B2Kst",B_M, meanBdKst, sigmaBdKst)
fitBdrho = ROOT.RooGaussian("fitBdrho", "Signal Bdrho", B_M, meanBdrho, sigma1Bdrho) 

# Generating data:
dataBdKst = fitBdKst.generate(ROOT.RooArgSet(B_M), 3000)
dataBdrho = fitBdrho.generate(ROOT.RooArgSet(B_M), 20000)

# Fitting gaussians:
fitBdKst.fitTo(dataBdKst)
fitBdrho.fitTo(dataBdrho)



# Plot :
framedata = B_M.frame(ROOT.RooFit.Title(" "), ROOT.RooFit.Range(5, 5.4))

dataBdKst.plotOn(framedata, ROOT.RooFit.MarkerColor(ROOT.kBlue))
fitBdKst.plotOn(framedata, ROOT.RooFit.LineColor(ROOT.kBlack), ROOT.RooFit.LineWidth(1), ROOT.RooFit.MoveToBack())
fitBdKst.plotOn(framedata, ROOT.RooFit.DrawOption("F"), ROOT.RooFit.FillColor(ROOT.kBlue-9), ROOT.RooFit.MoveToBack())

dataBdrho.plotOn(framedata, ROOT.RooFit.MarkerColor(ROOT.kRed))
fitBdrho.plotOn(framedata, ROOT.RooFit.LineColor(ROOT.kBlack), ROOT.RooFit.LineWidth(1), ROOT.RooFit.MoveToBack())
fitBdrho.plotOn(framedata, ROOT.RooFit.DrawOption("F"), ROOT.RooFit.FillColor(ROOT.kRed-9), ROOT.RooFit.MoveToBack())

Frame = ROOT.TCanvas("MWE_2Yaxis", "MWE_2Yaxis", 1200, 800)
framedata.SetAxisColor(color=ROOT.kRed, axis="Y")
framedata.SetLabelColor(color=ROOT.kRed, axis="Y")
framedata.Draw()
fitBdrho.Print('t')

Here is the graph I get:
MWE_2Yaxis.py (1.6 KB)

I put the code line by line in a terminal to see any alert messages so hopefully it should work ; otherwise I’d have no idea what would be the source of the trouble.

Ok I added:

Frame.Print("Frame.png")

in your script and I get:

I see only one axis. But in your script there is nothing like the code I pointed (transpad.C).

Indeed, but as I said I have not been able to achieve anything with it, because I don’t have (yet) any knowledge of C++ and I couldn’t figure out how to “translate” the example you pointed to python and to adapt it to my code. So I didn’t let any of my attempts because then the code I sent would not have worked at all.

here is a quick translation of what the example I pointed is doing:

import ROOT
B_M = ROOT.RooRealVar("B_M","m[GeV/c^{2}]",2.5,6)
meanBdKst = ROOT.RooRealVar("meanBdKst", "mean of BdKst", 5.28, 5.1, 5.4)
sigmaBdKst = ROOT.RooRealVar("sigma1BdKst", "width of BdKst", 0.017, 0.01, 0.03)

meanBdrho = ROOT.RooRealVar("meanBdrho", "mean of Bdrho", 5.15, 5.1, 5.4)
sigma1Bdrho = ROOT.RooRealVar("sigma1Bdrho", "width of Bdrho", 0.01, 0.005, 0.02)

# Making gaussians :
fitBdKst = ROOT.RooGaussian("fitBdKst","Fit of B2Kst",B_M, meanBdKst, sigmaBdKst)
fitBdrho = ROOT.RooGaussian("fitBdrho", "Signal Bdrho", B_M, meanBdrho, sigma1Bdrho)

# Generating data:
dataBdKst = fitBdKst.generate(ROOT.RooArgSet(B_M), 3000)
dataBdrho = fitBdrho.generate(ROOT.RooArgSet(B_M), 20000)

# Fitting gaussians:
fitBdKst.fitTo(dataBdKst)
fitBdrho.fitTo(dataBdrho)



# Plot :
framedata = B_M.frame(ROOT.RooFit.Title(" "), ROOT.RooFit.Range(5, 5.4))

dataBdKst.plotOn(framedata, ROOT.RooFit.MarkerColor(ROOT.kBlue))
fitBdKst.plotOn(framedata, ROOT.RooFit.LineColor(ROOT.kBlack), ROOT.RooFit.LineWidth(1), ROOT.RooFit.MoveToBack())
fitBdKst.plotOn(framedata, ROOT.RooFit.DrawOption("F"), ROOT.RooFit.FillColor(ROOT.kBlue-9), ROOT.RooFit.MoveToBack())

dataBdrho.plotOn(framedata, ROOT.RooFit.MarkerColor(ROOT.kRed))
fitBdrho.plotOn(framedata, ROOT.RooFit.LineColor(ROOT.kBlack), ROOT.RooFit.LineWidth(1), ROOT.RooFit.MoveToBack())
fitBdrho.plotOn(framedata, ROOT.RooFit.DrawOption("F"), ROOT.RooFit.FillColor(ROOT.kRed-9), ROOT.RooFit.MoveToBack())

Frame = ROOT.TCanvas("MWE_2Yaxis", "MWE_2Yaxis", 1200, 800)
framedata.SetAxisColor(color=ROOT.kRed, axis="Y")
framedata.SetLabelColor(color=ROOT.kRed, axis="Y")
framedata.Draw()

pad2 = ROOT.TPad("pad2","",0,0,1,1)
pad2.SetFillStyle(4000)
pad2.Draw()
pad2.cd()
axis = ROOT.TGaxis(0.9,0.1,0.9,.9,0.,1000.,50510,"+L")
axis.Draw()

Frame.Print("Frame.png")
fitBdrho.Print('t')

Now you have a 2nd pad (pad2) on top of the initial one with and axis in it.

Thank you very much. However even if this solution looks good, the scale on this new axis is arbitrarily set and the red dataset still overwrites the blue one that is plotted first. Would you now how to set the second Y axis so that it is scaled according to the number of events of the blue dataset ?

I guess I could try to do it by getting the maximum value of this blue dataset, but since its maximum is not the top of the frame (and I don’t know if it’s possible to know how “high”, relatively speaking, it is in the frame), i don’t think it would be a solution.

Simply Draw the axis with the appropriate limits. I guess you should be able to find them
from your data.

Actually I came to realize that we got a bit mixed up here: I indeed wanted a second Y axis, but the main purpose was to prevent the rescaling of the blue dataset (and its fit too). Which means that I need the blue gaussian to be of the same size than the red one. Maybe it is possible to overlay a transparent TCanvas (or a TPad?) over a first one, and each TCanvas gets normalized by each dataset ? And then display the Y axis on the right for one of the 2 TCanvas.

Yes that’s what the code I put in your macro is doing. It makes a 2nd transparent pad on top of the original one. But in your macro you two plots are coupled in a single Draw. You need to decouple them and do:

  1. Draw the red plot
  2. Draw the transparent plot on top
  3. Draw the blue plot in that transparent pad
  4. Draw the axis on right

Okay I’ve managed to put both fits to be approximately the same size. However I had to use SetFrameFillStyle(4000) and not SetFillStyle(4000).
Here is the MWE:

import ROOT
B_M = ROOT.RooRealVar("B_M","m[GeV/c^{2}]",2.5,6)
meanBdKst = ROOT.RooRealVar("meanBdKst", "mean of BdKst", 5.28, 5.1, 5.4)
sigmaBdKst = ROOT.RooRealVar("sigma1BdKst", "width of BdKst", 0.017, 0.01, 0.03)

meanBdrho = ROOT.RooRealVar("meanBdrho", "mean of Bdrho", 5.15, 5.1, 5.4)
sigma1Bdrho = ROOT.RooRealVar("sigma1Bdrho", "width of Bdrho", 0.01, 0.005, 0.02)

# Making gaussians :
fitBdKst = ROOT.RooGaussian("fitBdKst","Fit of B2Kst",B_M, meanBdKst, sigmaBdKst)
fitBdrho = ROOT.RooGaussian("fitBdrho", "Signal Bdrho", B_M, meanBdrho, sigma1Bdrho)

# Generating data:
dataBdKst = fitBdKst.generate(ROOT.RooArgSet(B_M), 3000)
dataBdrho = fitBdrho.generate(ROOT.RooArgSet(B_M), 20000)

# Fitting gaussians:
fitBdKst.fitTo(dataBdKst)
fitBdrho.fitTo(dataBdrho)

# Plot :
Frame = ROOT.TCanvas("MWE_2Yaxis", "MWE_2Yaxis", 600, 400)
frameKst = B_M.frame(ROOT.RooFit.Title(" "), ROOT.RooFit.Range(5, 5.4))
frameRho = B_M.frame(ROOT.RooFit.Title(" "), ROOT.RooFit.Range(5, 5.4))

dataBdKst.plotOn(frameKst, ROOT.RooFit.MarkerColor(ROOT.kBlue))
fitBdKst.plotOn(frameKst, ROOT.RooFit.LineColor(ROOT.kBlack), ROOT.RooFit.LineWidth(1))
fitBdKst.plotOn(frameKst, ROOT.RooFit.DrawOption("F"), ROOT.RooFit.FillColor(ROOT.kBlue-9))
frameKst.SetAxisColor(color=ROOT.kBlue, axis="Y")
frameKst.SetLabelColor(color=ROOT.kBlue, axis="Y")

dataBdrho.plotOn(frameRho, ROOT.RooFit.MarkerColor(ROOT.kRed))
fitBdrho.plotOn(frameRho, ROOT.RooFit.LineColor(ROOT.kBlack), ROOT.RooFit.LineWidth(1))
fitBdrho.plotOn(frameRho, ROOT.RooFit.DrawOption("F"), ROOT.RooFit.FillColor(ROOT.kRed-9))
frameRho.SetAxisColor(color=ROOT.kRed, axis="Y")
frameRho.SetLabelColor(color=ROOT.kRed, axis="Y")

frameKst.Draw()

pad = ROOT.TPad("pad","",0,0,1,1)
pad.SetFrameFillStyle(4000)
pad.Draw()
pad.cd()
frameRho.Draw("Y+")
Frame.Print("Frame.png")

And here is the graph:

The half bottom blue markers below the red fit look a bit odd but nevermind.
What puzzles me is that the red area really seems smaller than the blue one, and I thought they’d both have the same size. Is there any way to set the scales so that they have the same normalization ?

In addition to this, I tried to use the .Draw("Y+") command to put the blue Y axis on the right but this does not seem to have any effect. I guess I did not put it where it is supposed to in the MWE, could you correct me please ?

Rooplot has its own logic and not all options available for histogram Drawing are available.
May be @moneta can give you some hints.

Hi,
In RooFit RooPlot is just a frame and a container for plotting RooCurve and RooHist objects which are derived class of TGraph. Therefore all the options available for plotting TGraph’s should be available, if not directly one can always get an handle to these plotted objects, by calling RooPlot::getCurve or RooPlot::getHist.

Cheers

Lorenzo

Hi,
Thank you for your response. Could you be a little bit more specific though, please ? I am not familiar with these commands nor with the C++ notations of ROOT which make up most of the documentation for these commands.