Parsing informatio to Ttree with .format

Hello

I have a long list of systematics variables, each one corresponding to an input branch from the inFile.root

while looping, I need to save that in another .root file, and I need to save them in a format like

 self.var[0] = entry.var

So, I start by creating the branch var[0] which works ok,

for i_ in vars : 
    self.i_ = array('f',[0])
    self.tree.Branch("'"+i_+"'", self.i_, i_+'/F') 

but filling it fails

 for i_ in list : 
    j= '{0:s}.{1:s}'.format(entry,i_)
    ii = i_+'[0]'
    self.ii = float(j)

with an error like

self.ii =  float(j)
ValueError: could not convert string to float: <ROOT.TTree object ("Events") at 0x75415b0>.MET_pt_jesAbsoluteUp

I understand what the message means, but how could I convert my var->entry.var and change the datatype here ? The plain float(var) seems to not be working…

TIA,
Alexis

Have you checked that j actually contains a valid “float”? Try to print j on screen too, maybe there’s an invalid character.

right, for instance when I do a print(j)

<ROOT.TTree object ("Events") at 0x7525b70>.MET_pt_jesBBEC1_2016Down

and when looking directly in the inFile.root

 Events->Scan("MET_pt_jesBBEC1_2016Down")
************************
*    Row   * MET_pt_je *
************************
*        0 * 30.208169 *
*        1 * 85.090522 *
*        2 * 69.309433 *
*        3 * 46.088520 *

Also trying directly to print entry.i_ it returns
AttributeError: 'TTree' object has no attribute 'i_'

I’m no Python expert, but it seems that you are trying to convert this text

<ROOT.TTree object ("Events") at 0x7525b70>.MET_pt_jesBBEC1_2016Down

to float, which cannot be done; as I understand, to do float(something), that something must be a “number” (in text form), i.e. it only accepts digits and possibly a decimal point. I suppose you are trying to copy the entries from a branch to another branch (in a new tree), and I don’t understand the way you are trying to do it, sorry, but probably there are easier ways; I’d search how to copy trees with PyROOT (which I don’t use, so I cannot tell), or maybe someone else has better advice.

Ok, some more information

It appears that the getattr can correctly return the value I want. But now, I am suspecting that actually I am defining/creating the branches wrong.

So this is what I do

for i_ in self.allsyst :
    setattr(self, i_, array('f',[0]))
    self.t.Branch(i_, getattr(self,i_), '{0:s}/F'.format(i_))

and this is how later on I am filling it

 setattr(self, '{0:s}[0]'.format(i_), getattr(entry, "{0:s}".format(str(i_)), None))

but still, my branches are not filled properly in the end…

@couet Can you please take a look what am I missing ?

1 Like

It looks like more a python issue ? right ?
I am not sure being the best to answer in that case…

I mean, what the best way would be to create and fill branches from a list of variables when each of the variables corresponds to a float in the input tree ?

It looks like the description of a TNuple. There is plenty of examples around.

for a number of technical and practical reasons I cannot use TNuple. In the end, the setattr seems to be working, and I do get the right values on screen on the fly, but when actually Filling the TTree, the tree is filled with not the expected value

for i_ in self.allsyst :
    j = getattr(entry, "{0:s}".format(str(i_))) ## this reads the value from the input_tree
    setattr(self,i_,j) ## this one set the self.Branch_var_i_ = value
    print getattr(self,i_), 'var =', i_,  'value from tree:', j

116.465354919, var = MET_pt_jesEC2Up value from tree: 116.465354919

but in the end

Events->Scan("MET_pt_jesEC2Up")
************************
*    Row   * MET_pt_je *
************************
*        0 * 1.192e-37 *
*        1 * 1.192e-37 *
*        2 * 1.192e-37 *

Of course, if I manually type something like self.MET_pt_jesEC2Up[0] = float(j) then the Tree is filled with the correct value, but I want to avoid having to write few 100’s of similar lines…

May be @pcanal can help.

Actually this seems like a question for @etejedor (i.e. PyROOT syntax/magic). (In C++ you would call SetBranchAddress( branchname, address_of_float ) for each branch and/or use an std::vector and distribute its addresses to all the branches.

@etejedor, Can you please take a look ?

Hi,

From PyROOT you can also use SetBranchAddress to fill branches of a TTree. I recently added documentation for that to:

https://root.cern.ch/doc/master/classTTree.html

There is a “PyROOT” box at the end of the C++ docs. There you can find examples for several types of branches for both Branch and SetBranchAddress. There is actually one for branches of type float, which are written via an array.

would it be possible to tell me why the setattr although it seems to fill the tree.branch properly, in the end the tree is not filled ?

I mean I see in the documentation so it is still necessary to fill such object with the desired content before calling TTree::Fill but I cannot understand what step I am missing by using the setattr and then just self.tree.Fill

setattr just sets an attribute (on the Python side), but that will not be picked by tree.Fill afterwards. Instead of setattr, you need to do Branch before the loop (using some array variable), and once inside the loop you change the value you want to store at each iteration, then you call tree.Fill. The goal here is to create new branches in the output tree, and that can’t be done with setattr but with Branch, together with the setting of the right value and the call to Fill.

So, if I have two trees (input and output), I would do:

n = array('f', [ 0 ])
outputTree.Branch('outfloatb', n, 'outfloatb/F')

for event in inputTree:
   n[0] = event.infloatb
   outputTree.Fill()

thanks for the hint, but the problem as I said is that I have many-many branches… I tried to include the SetBranchAddress

setattr(self,var,array('f',[0]))
self.t.Branch(var, getattr(self,var), '{0:s}/F'.format(var))
self.t.SetBranchAddress( '{0:s}'.format(vv), array('f',[0]))

and while looping in the inputTree events

setattr(self,var,100)

and in the end self.tree.Fill()

but still the branch(es) are empty…

Hi,

I believe the SetBranchAddress should not be necessary here, since you are reading the branches of the input tree via the getattr syntax of Python.

In your case with many branches, isn’t this what you would do?

# Create a list with all the arrays that will hold the branches to be written
list_of_arrays = []
for _ in self.allsyst:
   list_of_arrays.append(array('f', [ 0 ]))

# Create the branches in the output tree
for i, v in enumerate(self.allsyst):
   outputTree.Branch(v, list_of_arrays[i], '{0:s}/F'.format(v))

# Iterate over the input tree and fill branches
for event in inputTree:
   for i, v in enumerate(self.allsyst): 
       list_of_arrays[i][0] = getattr(event, v)
   outputTree.Fill()

thanks @etejedor, that indeed seems to work! One last question, how would it be possible to define and work with 2D array ie going from
list_of_arrays.append(array('f', [ 0 ]))

to something like

rows, cols = (5, 15)
self.list_of_arrays.append( arr)

but then I cannot find how to properly define the

outputTree.Branch(v, list_of_arrays[i], '{0:s}/F'.format(v))

but for a 2D array

You mean you want to create branches that are 2d arrays of floats, instead of floats?

You can use a numpy array of 2d. For example:

import numpy as np

# Example numpy array of shape (5,15)
arr = np.arange(5*15).reshape(5,15)

outputTree.Branch(v, arr, '{0:s}[5][15]/F'.format(v))