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?
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:
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 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 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.