Saving histograms into a pdf / png format directly from ROOT

Dear experts,

I have been reading through other forums on how to save pdfs, but nothing is helping my particular situation. I am attempting to save selected histograms into a pdf or png file format directly from a python file, in this manner so far:

def validateFits(inFile):
    fit = ROOT.TFile.Open(inFile, "READ")
[...]
     if nSigFail < 1.0:
        #print(nSigPass, nSigFail)                                                                                                           
        print(inFile)
        c = fit.Get(inFile.split('/')[-1][:-5]+'Canv')

        #print(vars(inFile).__dict__)                                                                                                        
        #c.Draw()
        if not os.path.exists('plots/'): os.makedirs('plots/')
        #myfile = ROOT.TFile.Get(inFile)                                                                                                     
        filename = fit.Get(inFile.split('/')[-1][:-5])
        c.SaveAs('plots/'+ str(filename) + '.png')

But, I get this error:

./UL_2018/fits_data/muon/generalTracks/Z/Run2018_UL/AltBkg/NUM_LooseID_DEN_genTracks/NUM_LooseID_DEN_genTracks_abseta_1_pt_2.root

 *** Break *** segmentation violation



===========================================================
There was a crash.
This is the entire stack trace of all threads:
===========================================================

Thread 2 (Thread 0x7f39675ce700 (LWP 25110)):
#0  0x00007f3986703de2 in pthread_cond_timedwait

GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x00007f3986cb7069 in PyCOND_TIMEDWAIT (cond=<optimized out>, mut=<optimized out>, us=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Python/condvar.h:90
#2  take_gil (tstate=tstate
entry=0x453fab0) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Python/ceval_gil.h:208
#3  0x00007f3986cb735f in PyEval_RestoreThread (tstate=tstate
entry=0x453fab0) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Python/ceval.c:271
#4  0x00007f3986d3cbf2 in pysleep (secs=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Modules/timemodule.c:1844
#5  time_sleep (self=<optimized out>, obj=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Modules/timemodule.c:371
#6  0x00007f3986be1e30 in _PyMethodDef_RawFastCallKeywords (method=0x7f3987053c20 <time_methods+288>, self=<optimized out>, args=<optimized out>, nargs=1, kwnames=0x0) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Objects/call.c:648
#7  0x00007f3986be1f05 in _PyCFunction_FastCallKeywords (func=0x7f397eeb73c0, args=<optimized out>, nargs=<optimized out>, kwnames=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Objects/call.c:734
#8  0x00007f3986bbebd8 in call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Python/ceval.c:4568
#9  _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Python/ceval.c:3093
#10 0x00007f3986bb5beb in function_code_fastcall (co=co
entry=0x7f395cdfaae0, args=<optimized out>, args
entry=0x7f397336d6e8, nargs=nargs
entry=1, globals=globals
entry=0x7f395cb8a2d0) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Objects/call.c:283
#11 0x00007f3986be1872 in _PyFunction_FastCallDict (func=0x7f3973360cb0, args=0x7f397336d6e8, nargs=1, kwargs=0x7f39733645f0) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Objects/call.c:322
#12 0x00007f3986bb9b3f in do_call_core (kwdict=0x7f39733645f0, callargs=0x7f397336d6d0, func=0x7f3973360cb0) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Python/ceval.c:4645
#13 _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Python/ceval.c:3191
#14 0x00007f3986bb5beb in function_code_fastcall (co=<optimized out>, args=<optimized out>, nargs=1, globals=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Objects/call.c:283
#15 0x00007f3986be1a37 in _PyFunction_FastCallKeywords (func=<optimized out>, stack=<optimized out>, nargs=<optimized out>, kwnames=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Objects/call.c:415
#16 0x00007f3986bbe715 in call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Python/ceval.c:4616
#17 _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Python/ceval.c:3110
#18 0x00007f3986bb5beb in function_code_fastcall (co=<optimized out>, args=<optimized out>, nargs=1, globals=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Objects/call.c:283
#19 0x00007f3986be1a37 in _PyFunction_FastCallKeywords (func=<optimized out>, stack=<optimized out>, nargs=<optimized out>, kwnames=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Objects/call.c:415
#20 0x00007f3986bbe715 in call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Python/ceval.c:4616
#21 _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Python/ceval.c:3110
#22 0x00007f3986bb5beb in function_code_fastcall (co=co
entry=0x7f397eeae300, args=<optimized out>, args
entry=0x7f39675cde10, nargs=nargs
entry=1, globals=globals
entry=0x7f397f12a550) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Objects/call.c:283
#23 0x00007f3986be1872 in _PyFunction_FastCallDict (func=0x7f397eed27a0, args=0x7f39675cde10, nargs=1, kwargs=0x0) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Objects/call.c:322
#24 0x00007f3986be295d in _PyObject_Call_Prepend (callable=0x7f397eed27a0, obj=<optimized out>, args=0x7f398722d050, kwargs=0x0) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Objects/call.c:908
#25 0x00007f3986be3b65 in PyObject_Call (callable=0x7f3987142320, args=0x7f398722d050, kwargs=0x0) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Objects/call.c:245
#26 0x00007f3986d3e063 in t_bootstrap (boot_raw=boot_raw
entry=0x7f395cbbb8d0) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Modules/_threadmodule.c:994
#27 0x00007f3986cf2c17 in pythread_wrapper (arg=<optimized out>) at /workspace/build/externals/Python-3.7.6/src/Python/3.7.6/Python/thread_pthread.h:174
#28 0x00007f39866ffea5 in start_thread () from /lib64/libpthread.so.0
#29 0x00007f3985d1fb0d in clone () from /lib64/libc.so.6

Thread 1 (Thread 0x7f398726d740 (LWP 25052)):
#0  0x00007f3985ce6659 in waitpid () from /lib64/libc.so.6
#1  0x00007f3985c63f62 in do_system () from /lib64/libc.so.6
#2  0x00007f3985c64311 in system () from /lib64/libc.so.6
#3  0x00007f395aaff663 in TUnixSystem::StackTrace() () from /cvmfs/sft.cern.ch/lcg/views/LCG_97apython3/x86_64-centos7-gcc8-opt/lib/libCore.so
#4  0x00007f395ab01eb4 in TUnixSystem::DispatchSignals(ESignals) () from /cvmfs/sft.cern.ch/lcg/views/LCG_97apython3/x86_64-centos7-gcc8-opt/lib/libCore.so
#5  <signal handler called>
#6  0x00007f395a9f9ce3 in cling::printValue[abi:cxx11](TObject*) () from /cvmfs/sft.cern.ch/lcg/views/LCG_97apython3/x86_64-centos7-gcc8-opt/lib/libCore.so
#7  0x00007f3977d1b02b in ?? ()
#8  0x0000000000000000 in ?? ()
===========================================================


The lines below might hint at the cause of the crash.
You may get help by asking at the ROOT forum http://root.cern.ch/forum
Only if you are really convinced it is a bug in ROOT then please submit a
report at http://root.cern.ch/bugs Please post the ENTIRE stack trace
from above as an attachment in addition to anything else
that might help us fixing this issue.
===========================================================
#6  0x00007f395a9f9ce3 in cling::printValue[abi:cxx11](TObject*) () from /cvmfs/sft.cern.ch/lcg/views/LCG_97apython3/x86_64-centos7-gcc8-opt/lib/libCore.so
#7  0x00007f3977d1b02b in ?? ()
#8  0x0000000000000000 in ?? ()
===========================================================

Curious if this means I need to submit a source command like here: AFS phaseout | EP-SFT (?)

Anyway, looking at the particular root files that should be printing in this loop, I see these keys:

 KEY: TCanvas	NUM_LooseID_DEN_genTracks_abseta_1_pt_2_Canv;1	c
  KEY: RooFitResult	NUM_LooseID_DEN_genTracks_abseta_1_pt_2_resP;1	Result of fit of p.d.f. pdfPass to dataset hPass
  KEY: TProcessID	ProcessID0;1	8c609ae0-cc6e-11ec-b0d3-0f3d8e80beef
  KEY: RooFitResult	NUM_LooseID_DEN_genTracks_abseta_1_pt_2_resF;1	Result of fit of p.d.f. pdfFail to dataset hFail
  KEY: TTree	NUM_LooseID_DEN_genTracks_abseta_1_pt_2_statTests;1	statTests
  KEY: TH1D	NUM_LooseID_DEN_genTracks_abseta_1_pt_2_GenPass;1	
  KEY: TH1D	NUM_LooseID_DEN_genTracks_abseta_1_pt_2_GenFail;1	
  KEY: TH1D	NUM_LooseID_DEN_genTracks_abseta_1_pt_2_Pass;1	
  KEY: TH1D	NUM_LooseID_DEN_genTracks_abseta_1_pt_2_Fail;1	

I am attempting to save the TCanvas into a pdf format, but within ROOT, I usually do:

NUM_LooseID_DEN_genTracks_abseta_1_pt_2_Canv->Draw()

and take a screenshot. It’s a very long procedure. What’s the best way to go about retrieving these canvases that meet the criteria I’ve set, and then turn them into individual pdfs so I can dump these plots into my analysis? Root on LXplus for me is ROOT 6.20/06.

Any help would be much appreciated! Thanks so much!

Tay

Can you try something simpler like;

c.SaveAs(`foo.png`)

if it works it means that the constructed filename is incorrect.

Hi @couet definitely–when I do so, I get a null pointer error:

./UL_2018/fits_data/muon/generalTracks/Z/Run2018_UL/AltBkg/NUM_LooseID_DEN_genTracks/NUM_LooseID_DEN_genTracks_abseta_1_pt_2.root
Traceback (most recent call last):
  File "./tnp_fitter.py", line 445, in <module>
    status = main()
  File "./tnp_fitter.py", line 423, in main
    validateFits(job[0])
  File "/eos/home-t/tcarnaha/spark_tnp/scripts/validateFits.py", line 74, in validateFits
    c.SaveAs('foo.png')
ReferenceError: attempt to access a null-pointer

Your code is not complete so I cannot tell for sure, but it seems c is not defined.

Ah, so you’re saying

c = fit.Get(inFile.split('/')[-1][:-5]+'Canv')

does not define c here correctly?

It looks like. But it is had to tell a we do not have your file fit.

Maybe my problem is moreso that fit is more like a handle than a location for the actual TCanvas? I define it to open the files fit = ROOT.TFile.Open(inFile, "READ"), so is there a better way to define how to access a TCanvas and save it in a pdf?

If you have a TCanvas saved in a ROOT file you can do (C++ code):

   auto f = new TFile("your_root_file.root");
   TCanvas *c = (TCanvas *) f->get("name_of_the_canvas_stored_in_the_root_file");
   c->Print("image.pdf");

I do have a TCanvas saved in each ROOT file. But, I must confess that I am not well-versed in C++. In Python, there are no pointers as in your example; how would I format into something more like PyROOT? (i.e. if you could type it to me like a “coding for dummies” book, that would be much appreciated) :slight_smile:

@couet 's code translated to python is:

import ROOT

f = ROOT.TFile("your_root_file.root")
c = f.Get("name_of_the_canvas_stored_in_the_root_file")
c.Print("image.pdf")
1 Like

a small sanity check, produce a canvas from this macro:

void Canvas(){
  TFile* f = TFile::Open("test.root","RECREATE");
  TH1F* h = new TH1F("h","h",100,-5,5);
  h->FillRandom("gaus");
  h->SetLineColor(kRed);
  h->SetFillColor(kGreen+3);
  TCanvas* c = new TCanvas("c","c",800,600);
  h->Draw("hist");
  c->Write();
}

then:

import ROOT

f = ROOT.TFile("test.root")
c = f.Get("c")
c.Print("img_from_py.pdf")

is the same as:

root [0]    auto f = new TFile("test.root");
root [1]    TCanvas *c = (TCanvas *) f->Get("c");
root [2]    c->Print("image.pdf");
Info in <TCanvas::Print>: pdf file image.pdf has been created
1 Like

Thanks so much @ikabadzhov for the translation; super helpful! I attempted the void Canvas(){} total block inside my root application, then wrote the import ROOT inside my python file. When I ran my python file, I get that test.root does not exist. What am I doing incorrectly?
Whole code here:

root [10] void Canvas(){
root (cont'ed, cancel with .@) [11]  TFile* f = TFile::Open("test.root","RECREATE"); 
root (cont'ed, cancel with .@) [12]  TH1F* h = new TH1F("h","h",100,-5,5);
root (cont'ed, cancel with .@) [13]  h->FillRandom("gaus");
root (cont'ed, cancel with .@) [14]  h->SetLineColor(kRed);
root (cont'ed, cancel with .@) [15]  h->SetFillColor(kGreen+3);
root (cont'ed, cancel with .@) [16]  TCanvas* c = new TCanvas("c","c",800,600);
root (cont'ed, cancel with .@) [17]  h->Draw("hist");
root (cont'ed, cancel with .@) [18]  c->Write();
root (cont'ed, cancel with .@) [19]}
root [20] .q
[tcarnaha@lxplus751 spark_tnp]$  ./tnp_fitter.py fit muon generalTracks Z Run2018_UL configs/muon_pog_official_run2_Z.json --baseDir ./UL_2018 --registry data/registry_muon_Z_generalTracks_oldNtuples.json --validate

RooFit v3.60 -- Developed by Wouter Verkerke and David Kirkby 
                Copyright (C) 2000-2013 NIKHEF, University of California & Stanford University
                All rights reserved, please read http://roofit.sourceforge.net/license.txt

Error in <TFile::TFile>: file test.root does not exist
Traceback (most recent call last):
  File "./tnp_fitter.py", line 445, in <module>
    status = main()
  File "./tnp_fitter.py", line 423, in main
    validateFits(job[0])
  File "/eos/home-t/tcarnaha/spark_tnp/scripts/validateFits.py", line 80, in validateFits
    c.Print("img_from_py.pdf")
ReferenceError: attempt to access a null-pointer

You could also save directly the canvases as PDF at the same time that you write the ROOT file, rather than doing that in a separate script later on.

To save all open canvases in a ROOT session, just type the following in a terminal:

        TIter next(gROOT->GetListOfCanvases());
        TCanvas* c = nullptr;
        while((c = (TCanvas*)next()))
        {
            const TString canvName = c->GetName();
            c->SaveAs(canvName+".pdf");
        }

You could do something similar if the canvases are not open, but rather inside a TFile.
Just iterate over all keys in the TFile and only call SaveAs if the key->ClassName()=="TCanvas".

Hey @ferhue, thank you for the advice. The root files are already generated, and I’m building a validation tool in order to open them and inspect them for future applications, so that unfortunately wouldn’t be the best way for me to continue. Thank you for the idea though!

Then just use sth like this:

f = ROOT.TFile("mydata.root")
for k, o in getall(f):
    if o.ClassName() == "TCanvas":
        o.SaveAs("abcd.pdf")

Hey @ferhue! Tried something like the above, but I think I’m getting hung up on the f = ROOT.TFile("mydata.root") portion.

All of my root files come from a base directory, UL_2018, which has numerous folders that are opened and sifted through before getting to a single root file. I don’t know how to call “mydata.root” in this instance because one file that will be caught in the if loop will be:
./UL_2018/fits_data/muon/generalTracks/Z/Run2018_UL/AltBkg/NUM_LooseID_DEN_genTracks/NUM_LooseID_DEN_genTracks_abseta_1_pt_2.root

and another will be:

./UL_2018/fits_mc/muon/generalTracks/Z/Run2018_UL/AltSigOld/NUM_LooseID_DEN_genTracks/NUM_LooseID_DEN_genTracks_abseta_1_pt_3.root

and so on.

I can get these files from calling: Print(inFile) inside my script…but ROOT.TFile(inFile) does not work.

How would you go about defining “mydata.root” in your example for my data?

It must be an issue with where your script is located, compared to where the data are.
It seems that your script is in:

/eos/home-t/tcarnaha/spark_tnp/scripts/validateFits.py

whereas your data might be in a different folder.

For example, if your data are under spark_tnp, then inFile should probably be ../UL_2018 instead of ./UL_2018. Alternatively, try to specify the full path to avoid this kind of nuisances. So to say: inFile = "/eos/home-t/tcarnaha/UL_2018/...."

Ah maybe! Yes, the data is /eos/…/spark_tnp/UL_2018.
inFile is already tagged with what is necessary to pull through all of those folders, but if I redefine a pathway to insert, say:
dir = "eos/home/tcarnaha/UL_2018/

And, ROOT.TFile(dir), it complains. Also, what header do you use to have getall be defined?

Aren’t you missing the leading slash when you define dir?

Try this:

dir = "/eos/home/tcarnaha/UL_2018/ ROOT.TFile(dir+"fits_data/muon/generalTracks/Z/Run2018_UL/AltBkg/NUM_LooseID_DEN_genTracks/NUM_LooseID_DEN_genTracks_abseta_1_pt_2.root")

Does that work?

For getall to work, take a look at Loop over all objects in a ROOT file

Hey @ferhue , thank you for the loop reference. And, I did originally have the forward slash in my code; sorry for the typo in rewriting it in this forum. Still doesn’t work to output an actual png file. Here’s what I’ve got so far:

        c = ROOT.TCanvas('','',200,10,700,500)                                                                 \

        dir1 = '/eos/user/t/tcarnaha/spark_tnp/'                                                               \

        file_name = (dir1 +inFile.strip(".")[1:])
        #print('Loading file: ', file_name)                                                                    \
                                                                                                                
        myfile = ROOT.TFile.Open(file_name, "READ")                                                            \

        root_dir = myfile.GetDirectory(file_name+"_Canv") #may cause problems                                   

	#List of Keys()                                                                                        \
                                                                                                                
        root_keys = myfile.GetListOfKeys()                                                                     \

        c.cd()                                                                                                 \

        for key in root_keys:                                                                                  \

            if key.ClassName() == "TCanvas":
                key.SaveAs('folder/' + mytitle() + '.png')
                print("Saving: " + mytitle() + ".png")

where “mytitle()” is just a name-tagging function. I’m just wondering why my SaveAs is not saving a png now. It seems like the last if block is not returning anything at all (no print statements either).