Problem with dividing TCanvas for multiple RDataFrame Histograms in pyROOT


Please read tips for efficient and successful posting and posting code

ROOT Version: 6.24/06
Platform: x86_64-centos7
Compiler: Not Provided


I obtain 1D histograms from a RDataFrame with different filters and want to plot them in a divided TCanvas. However, only the last histogram could be successfully painted. Could you please help?

Here the minimal code to reproduce the problem:

import ROOT                                      
                                                 
ROOT.gROOT.SetBatch(ROOT.kTRUE)                  
ROOT.gStyle.SetOptStat(0)                        
                                                 
                                                 
df = ROOT.RDataFrame("DecayTree", "demo.root")   
c = ROOT.TCanvas("c", "c", 1200, 600)            
c.Divide(3, 2)                                   
for i in range(6):                                                      
    c.cd(i + 1)  # TCanvas.cd() starts from 1                                 
    bdt_cut = -1 + 0.1 * i                        
    h = df.Filter(f"BDT>{bdt_cut}").Histo1D(     
        (                                        
            f"h{i+1}",                           
            f"h{i+1}",                           
            90,                                  
            5050,                                
            5500,                                
        ),                                       
        "B_DTFDict_B_M",                         
    )                                            
    h.Draw()                                     
c.SaveAs("demo.pdf")                             

demo.pdf

demo.root (16.4 KB)
demo.py (488 字节)
demo.pdf (13.9 KB)

Hi @Patrick_Wu ,

the reason why all pads except the last one come up empty is that all histograms except the last one go out of scope and are deleted before the end of the script (every time you do h = ..., the new histogram is assigned to h and the previous histogram is deleted). This is a common source of confusion, see e.g. I draw a canvas with no picture as well as many similar posts on the forum.

There is also another issue with the script: every time you call h.Draw, RDF is forced to run the event loop to produce the histogram, so that example code is running 6 event loops. But RDF can produce the 6 histograms in a single event loop, which is much faster.

This is an example solution, we first book all operations with RDF, then use the results:

import ROOT

ROOT.gROOT.SetBatch(ROOT.kTRUE)
ROOT.gStyle.SetOptStat(0)

df = ROOT.RDataFrame("DecayTree", "demo.root")
histos = []
for i in range(6):
    bdt_cut = -1 + 0.1 * i
    h = df.Filter(f"BDT>{bdt_cut}").Histo1D(
        (
            f"h{i+1}",
            f"h{i+1}",
            90,
            5050,
            5500,
        ),
        "B_DTFDict_B_M",
    )
    histos.append(h)

c = ROOT.TCanvas("c", "c", 1200, 600)
c.Divide(3, 2)
for i in range(6):
    print(f"cd({i+1})")
    c.cd(i + 1)
    histos[i].Draw()

c.SaveAs("demo.pdf")

I hope this helps!
Enrico

Thank you so much, @eguiraud ! Your code works and your explanation resolves a lot of my puzzles.

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