Saving histograms into a pdf / png format directly from ROOT

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).

hmm what is the output of:

for key in root_keys:
    print(key,key.ClassName())

?

One example output of:

for key in root_keys:
    print("key: ", key, "ClassName: ", key.ClassName())
key:  Name: NUM_LooseID_DEN_genTracks_abseta_1_pt_2_Canv Title: c ClassName:  TKey
key:  Name: NUM_LooseID_DEN_genTracks_abseta_1_pt_2_resP Title: Result of fit of p.d.f. pdfPass to dataset hPass ClassName:  TKey
key:  Name: ProcessID0 Title: 8555bfbe-cc6e-11ec-a3de-513b8e80beef ClassName:  TKey
key:  Name: NUM_LooseID_DEN_genTracks_abseta_1_pt_2_resF Title: Result of fit of p.d.f. pdfFail to dataset hFail ClassName:  TKey
key:  Name: NUM_LooseID_DEN_genTracks_abseta_1_pt_2_statTests Title: statTests ClassName:  TKey
key:  Name: NUM_LooseID_DEN_genTracks_abseta_1_pt_2_GenPass Title:  ClassName:  TKey
key:  Name: NUM_LooseID_DEN_genTracks_abseta_1_pt_2_GenFail Title:  ClassName:  TKey
key:  Name: NUM_LooseID_DEN_genTracks_abseta_1_pt_2_Pass Title:  ClassName:  TKey
key:  Name: NUM_LooseID_DEN_genTracks_abseta_1_pt_2_Fail Title:  ClassName:  TKey
key:  Name: NUM_LooseID_DEN_genTracks_abseta_2_pt_1_Canv Title: c ClassName:  TKey
key:  Name: NUM_LooseID_DEN_genTracks_abseta_2_pt_1_resP Title: Result of fit of p.d.f. pdfPass to dataset hPass ClassName:  TKey
key:  Name: ProcessID0 Title: c5340b0e-cc6e-11ec-bcef-8ca0b8bcbeef ClassName:  TKey
key:  Name: NUM_LooseID_DEN_genTracks_abseta_2_pt_1_resF Title: Result of fit of p.d.f. pdfFail to dataset hFail ClassName:  TKey

Try instead with:

for key in root_keys:
    if key.InheritsFrom('TCanvas'):
        print(key)
        c = key.ReadObj()
        c.SaveAs("test.png")

Here I get the error:

SysError in <TFile::ReadBuffer>: error reading from file ./UL_2018/fits_data/muon/generalTracks/Z/Run2018_UL/massRangeDown/NUM_TightID_DEN_genTracks/NUM_TightID_DEN_genTracks_abseta_3_pt_7.root (Input/output error)
Error in <TFile::Init>: ./UL_2018/fits_data/muon/generalTracks/Z/Run2018_UL/massRangeDown/NUM_TightID_DEN_genTracks/NUM_TightID_DEN_genTracks_abseta_3_pt_7.root failed to read the file type data.

Does it happen with all files, or only with one?

If only with one, it seems that this particular ROOT file is corrupt. See: Corrupted ROOT files without reason ?! - #15 by linev

Also, please check if you have read permission on this file/folder.

Ah, just with this one! When I go to the actual file and look around, I get:

SysError in <TFile::ReadBuffer>: error reading from file ./UL_2018/fits_data/muon/generalTracks/Z/Run2018_UL/massRangeDown/NUM_TightID_DEN_genTracks/NUM_TightID_DEN_genTracks_abseta_3_pt_7.root (Remote I/O error)
Error in <TFile::Init>: ./UL_2018/fits_data/muon/generalTracks/Z/Run2018_UL/massRangeDown/NUM_TightID_DEN_genTracks/NUM_TightID_DEN_genTracks_abseta_3_pt_7.root failed to read the file type data.
(TFile *) nullptr

@ferhue Thank you so much for that! Is there a way I can go around those null pointers (not include them when trying to get TCanvases from ~200 other files that are good)?

Maybe the following:

try:
    myfile = ROOT.TFile.Open(file_name, "READ")
    root_keys = myfile.GetListOfKeys()
    for key in root_keys:
        ...HERE YOUR CODE
except:
    print('Skipping file',file_name)

Hmm…@ferhue, it’s still giving me a similar error:

SysError in <TFile::ReadBuffer>: error reading from file ./UL_2018/fits_data/muon/generalTracks/Z/Run2018_UL/massRangeDown/NUM_TightID_DEN_genTracks/NUM_TightID_DEN_genTracks_abseta_3_pt_7.root (Remote I/O error)
Error in <TFile::Init>: ./UL_2018/fits_data/muon/generalTracks/Z/Run2018_UL/massRangeDown/NUM_TightID_DEN_genTracks/NUM_TightID_DEN_genTracks_abseta_3_pt_7.root failed to read the file type data.

Can you just ignore the error? Or is the script crashing?

The script is crashing :frowning_face:

hmm maybe try this after TFile.Open:

if myfile.IsZombie():
    continue

Otherwise, check beforehand what the size of your file is, and skip it if it is very low.
See How do I check file size in Python? - Stack Overflow

So I have:

if nSigFail < 1.0:
        c = ROOT.TCanvas('','',200,10,700,500)
        #c = inFile.Get("TCanvas")                                                                                                                               
        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")

        if myfile.IsZombie():
            continue

        root_dir = myfile.GetDirectory(file_name+"_Canv") #may cause problems                                                                                    
        c = root_dir
        c.cd()
	c.SaveAs("test.png")

The scripts doesn’t like this very much; the file size is only 49KB though, so small but of similar size as the others.