Selecting events in TTree

Hello,

I am curious if there is an easier/faster way to get selection from a TTree.
What I currently do:

I have a function makes average of waveforms for a given standard class that I work with. However, I like to make some selection in the waveforms before adding it to the average. Besides, I’ve different cuts for different data sets. To be able to use the same function aways, I work in the following way:

class MyDataClass{

  public:
  static const Int_t npts = 1000;
  Double_t wvf[npts];

};

void makeAverage(string selection = ""){
   TFile *f = new TFile("mydata.root","READ");
   TTree *t1 = (TTree*)f->Get("mytree");

   MyDataClass data;
   TBranch *b = t1->GetBranch("data");
   b->SetAddress(&data);

   for(Int_t i = 0; i < t1->GetEntries(); i++){
     
     if (selection != ""){
       Int_t nselec = t1->Draw("1",Form("Entry$==%d && %s",i,selection.c_str()),"goff");
       if(nselec == 0) continue;
     }
     b->GetEvent(i); 
   }

   // from here, actually make average... etc
}

Doing like that allows me to call any selection I want. Using Entry$ == makes things much faster.

Is there any faster or better way to make this sort of selection?

Cheers,
Henrique


Hi Henrique,

sorry for the high latency.

If I understand correctly, for each event in the TTree you are looping over the whole TTree again, filling a histogram with just one entry and checking it has more than zero entries. Indeed this is extremely wasteful.

If the cut must be a valid TTree::Draw selection as a string, something you could with TTree do is apply the cut once (before the explicit loop over entries) with t1->Draw(">>entrylist", cut). This creates a list of entries entrylist that satisfy the cut in a single loop over the TTree. Then you can loop again over just the entries in entrylist to do the rest of the processing.

In general I would recommend looking at RDataFrame where you would express your logic like this:

ROOT::RDataFrame df("mytree", "mydata.root");
df.Filter("x > 0").Foreach(processSomeColumns, {"x", "y", "z"});

P.S.

I guess it should be t1->GetEntries() (as a function call) in for(Int_t i = 0; i < t1->GetEntries; i++)?

Dear Enrico,

Thank you for the reply!

That is the solution I was looking for! Thanks.
(I do not understand properly RDataFrame as I never used it, I will look better in the future)

The solution with “Entry$==” is still fast when the selection is not strong, because the time of calling “Entry$==1” or “Entry$==20000” is the same. However, there are the annoying warning messages when there is no event in the graph, which can make things much slower.

I’ve corrected the typo, thanks.

Cheers,
Henrique

I think you get the same time with “t1->Draw(...)” for any “Entry$” because it internally always runs through all tree entries (so inside of your own “for” loop over events, for each of them, it internally runs another one).

You could easily improve it, I think:

Int_t nselec = t1->Draw("1", selection.c_str(), "goff", 1, i); // just the entry "i"
if (nselec < 1) continue; // -1 in case of error, 0 in case selection not fulfilled

Maybe @couet can give a better way (unfortunately, there is no “Cut” method in TTree).

I have no better solution.

I would strongly suggest to refactor things so that you don’t have to execute a TTree::Draw call for each entry in the tree. As @Wile_E_Coyote says, this runs another full event loop for each entry in the TTree, so it’s way, way slower than it could be.

Cheers,
Enrico

Thank you very much! Indeed this is a much better approach.

Hi Enrico,

I’ve change for the TEventList :slight_smile:

I tested the method with an empty selection:
t1->Draw(">>entrylist", "")
and the time was basically the same as just calling b->GetEvent(i)

Thanks again.