RDataFrame apply method on vector of LorentzVectors


I can not figure out what is the correct syntax to apply a method on a RVecROOT::Math::PtEtaPhiEVector> in python.
I made an example
“”
import ROOT
import os,sys

if name == “main”:

path = “root://eospublic.cern.ch//eos/opendata/atlas/OutreachDatasets/2020-01-22/”
filename=path+‘GamGam/Data/data_A.GamGam.root’
print ('filename= ',filename)
d = ROOT.RDataFrame(“mini”, filename)

d=d.Filter(‘photon_n>0’,‘At least 1 photon’)
d = d.Define(“photons”, ’ ROOT::VecOps::ConstructROOT::Math::PtEtaPhiEVector(photon_pt , photon_eta, photon_phi, photon_E )')
d = d.Define(“ph_pt”,‘ROOT::VecOps::Map(photons, photons.Pt())’)
hnom = d.Histo1D(“ph_pt”)
h1.Draw()
“”
What is the correct syntax to call the Map in python ?
Thank you for the help.
Tancredi


Hi @tancredi ,

I don’t think you are forced to use helpers like Construct or even Map. With this example I think I am doing the same thing you are trying to do, correct me if I am wrong:

import ROOT

if __name__ == "__main__":
    df = (
        ROOT.RDataFrame(5)
            .Define("photon_pt", "1.")
            .Define("photon_eta", "2.")
            .Define("photon_phi", "3.")
            .Define("photon_E", "4.")
            .Define("photons", "ROOT::Math::PtEtaPhiEVector(photon_pt , photon_eta, photon_phi, photon_E)")
            .Define("ph_pt", "photons.Pt()")
    )
    df.Display(("ph_pt",)).Print()

Hello,
yes, but in my case the photon is a RVec (many photons in the event) and I need to apply a method from ROOT::Math::PtEtaPhiEVector on all elements in the RVec. I saw that the Map is needed for that.
Regards,
Tancredi

Right,

The following snippet should then be closer to your case:

import ROOT

if __name__ == "__main__":
    df = (
        ROOT.RDataFrame(5)
            .Define("photon_pt", "ROOT::RVecD(3, 1.)")
            .Define("photon_eta", "ROOT::RVecD(3, 2.)")
            .Define("photon_phi", "ROOT::RVecD(3, 3.)")
            .Define("photon_E", "ROOT::RVecD(3, 4.)")
            .Define("photons", "ROOT::VecOps::Construct<ROOT::Math::PtEtaPhiEVector>(photon_pt, photon_eta, photon_phi, photon_E)")
            .Define("ph_pt", "return ROOT::VecOps::Map(photons, [](const ROOT::Math::PtEtaPhiEVector &v){return v.Pt();}); ")
    )
    df.Display(("photons",)).Print()

First off, with respect to your original example which you said doesn’t work, I don’t think photons.Pt() is doing what you think. The docs of the Map function state that you should pass a callable that will be applied to all the elements of the collection. In your case, the collection is photons (i.e. an RVec<ROOT::Math::PtEtaPhiEVector>). The other argument you are passing, i.e. photons.Pt(), is not a callable. Logically, that would mean the result of the method Pt, which is a double. Not even photons.Pt would work since that is a member function of the ROOT::Math::PtEtaPhiEVector class, and not the RVec class which is the type of photons. So, practically, you have to pass a callable that takes a ROOT::Math::PtEtaPhiEVector as input argument and calls its Pt method (that is what you see in my example above).

That being said, pay extra attention in this particular case: passing a C++ lambda to the Map call inside the string hits a non-ideal behaviour which is documented in the docs (incidentally with exactly your use case as an example) and has also been explained in the forum in other posts (here for example). Make sure to always explicitly use return in the strings you pass to Define and other operations whenever another return keyword is already present in the string.

Cheers,
Vincenzo

Hello Vincenzo,
thank you for the detailed explaination. I did not know how to construct the lambda function.
It works now !
Regards,
Tancredi