Custom object in TTree => numpy structured array

Hello experts,

This PyROOT thread may not be the relevant place, but I am seeking for an advice from experienced experts in both ROOT and Python…

I have a custom data product object (namely C++ class inherited from TObject) stored in a TTree branch.
I would like to convert this into a numpy structured array to benefit scientific tools in Python.
For a simple data types (built-in types or 1 dimensional std::vector of such types), root_numpy seems to work perfectly fine.
But my trial on custom data product has failed.
What is a good way to do this?

I can write my own API, but I was wondering if there is a magic tool OR if there is a recommended way of writing such an API (planning to just write C API using numpy C API support, as I am not sure Cython works with C++ custom data product object…)

Thank you for your time and advice!!!



This perhaps may help:


G Ganis

I am not sure how that helps…
I can see the page repeats that my need is not supported by root_numpy…

“root_numpy can convert branches of strings and basic types such as bool, int, float, double, etc. as well as variable and fixed-length 1D arrays and 1D or 2D vectors of basic types and strings.”

I am sorry for not making this point explicit enough above… but what I have is a branch of C++ object that inherits from TObject. It is none of what is mentioned in the sentence above.

If I blindly try root2array on this TTree, I get an error.
For simple built-in types, it works as charm and I’m used to it.



not really sure what you’re asking … python buffers (which underly numpy arrays) only work for builtin types; there is no notion of TObject*'s. So turning a TObject* into an array of some structures (through a selection of some of the data members of the TObject derived?) does not sound like an automatable approach.

Cheers, Wim

Hi Kazu,

root_numpy can handle expressions of branches like TTree::Draw(), but I just fixed a bug that prevented it from handling expressions involving object branches (subbranches were not being activated). Update to 4.0.1 to gain this bugfix.

So this means you can convert your object into (possibly) multiple array fields determined by data members or method return values or expressions thereof:

>>> from root_numpy.testdata import get_filepath
>>> from root_numpy import root2array
>>> from ROOT import TLorentzVector  # load your class first!
>>> root2array(get_filepath('object1.root'), branches=['vect.Pt()', 'vect.Eta()'])
array([(1.0, 1.0), (2.0, 2.0000000000000004), (3.0, 3.0000000000000013),
       (4.0, 3.999999999999993), (5.0, 4.999999999999873),
       (6.0, 6.000000000000605), (7.0, 7.000000000004848),
       (8.0, 8.000000000145423), (9.0, 9.00000000042665),
       (10.0, 10.000000008986413)], 
      dtype=[('vect.Pt()', '<f8'), ('vect.Eta()', '<f8')])

This is now explicitly mentioned in the docs:

I’ve also been considering the possibility of allowing users to specify and register their own converters for handling arbitrary objects in custom ways. In the example above, instead of creating separate fields in the output array for each TLorentzVector value, you could have your converter generate 4-tuples of (pt, eta, phi, m), to be stored in a single column of the array.

For now, the TTree::Draw()-like expression handling should hopefully do what you need.


BTW root_numpy now supports Python 3 and ROOT 6.

Thank you all! I have been killed with some issue in the experiment and amazed to find your replies today!

As Wim mentioned I concluded this is not automatable after more studying.
I ended up writing my own Cython code, and it seems to work pretty well.

What Noel mentioned is interesting and I am willing to give a shot.
This gets rid of a need of writing a Cython code to some extent (if not full, I am not sure how this will work out to convert all information).
If I find something that might be useful to share, I post a reply.

Great to hear root_numpy supports ROOT6 as well!