Attaching vector as a branch to an existing TTree

Hi all, hopefully, a simple question for someone who knows it, but something I’ve spent ages banging away at. I want to clone an existing TTree then create a new branch, filling it from a vector.

Below is my attempt so far. The trouble is, this seems to be creating new rows in the final TTree with each fill, instead of filling from row 0 as I was expecting. It also seems to be duplicating the values from the final row of the TTree I was attaching to.

 void Chi2_Append() {
        TFile *f1 = new TFile("File1", "read");
        TTree *t1 = (TTree *) f1->Get("BaseTTree"); // base tree to append to

        TFile *f2 = new TFile("File2", "read");
        std::vector<double> *best_fit_toy = (std::vector<double> *) f2->Get("chi2_best_fit_toy");

        TFile *newFile = new TFile("FinalFile","recreate");
        TTree *newTree = (TTree *)t1->CloneTree(); // TODO: Look into this creating TH1 error.
        std::string vector_name = "chi2_best_fit_toy"; // chi2_best_fit_toy is of type vector<double>
        std::string branch_info = vector_name + "/D";
        double toy_branch = -999;

        newTree->Branch(vector_name.c_str(), &(toy_branch), branch_info.c_str()); //Create Branch for best fit toy

    newTree->SetBranchStatus("chi2_best_fit_toy",1); //Only Fill ChiSquared branch

    for (std::vector<double>::iterator i = best_fit_toy->begin(); i != best_fit_toy->end(); i++) {
        toy_branch = *i;



Welcome to the ROOT forum!

You can use RDataFrame's snapshot capability to attach the vector:

ROOT::RDataFrame df("BaseTTree", "File1");
df.Define("chi2_best_fit_toy", [best_fit_toy](ULong64_t idx){ return best_fit_toy->at(idx); }, {"rdfentry_"})
  .Snapshot("BaseTTree", "FinalFile");

The Define statement creates a new RDataFrame column using the lambda function given as a second parameter. This lamda function binds the best_fit_toy vector pointer ([best_fit_toy]), which you’d have previously read from File2. It then takes an index as parameter and returns the element of the vector at the index. The index is provided by RDataFrame’s virtual rdfentry_ column, which contains the entry number as RDataFrame loops over the entries of BaseTTree. Finally, Snapshot recreates the file FinalFile and writes out the resulting data set into a new tree.

That will write the entire tree once more and not only the chi2_best_fit_toy branch but if your file is not too large that might be outweighed by the shorter code.


1 Like

For completeness sake, back filling branch should be done without call TTree::Fill (nor SetBranchStatus). but rather:

auto branch = newTree->Branch(vector_name.c_str(), &(toy_branch), branch_info.c_str()); //Create Branch for best fit toy

for (std::vector<double>::iterator i = best_fit_toy->begin(); i != best_fit_toy->end(); i++) {
   toy_branch = *i;
1 Like

Ok, I will try those both once I get back to work in the morning. Thank you jblomer and pcanal!

Hi Jakob,
Thank you for the explanation on RDataFrame! It’s a tool I’ll definitely be using in the future. I believe this solution is being stopped from working by an error I encountered in previous code, but ignored on the basis it seemed to have no effect on my output. This error is detailed here: Creating a TH1 type branch error

The error the RDataFrame produces looks like:

Error in <TBranchProxy::Read>: Unable to initialize BranchName
Error in <TTree::Branch>: Reference interface requires a valid object (for branch: BranchName!)
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 1) >= this->size() (which is 1)

Best wishes,

Thank you for your help on the other question! With that issue resolved, I am left with only the std::out_of_range error detailed in the previous reply. I believe this is because the vector I am producing at the moment is only 1 element long whilst the TTree I am trying to attach to is 2 rows. The vector will be variable in length (1 or TTree length) when properly applied beyond these tests. Do you know how to workaround this? Here is the current code (now much shortened due to this new method):

void Chi2_AppendDataFrame(
            const char* ParameterFile = "", //Output from saveparamthrows during toy generation.
            const char* FitFile = "", //Output file from fitting the toys.
            const char* OutputFile = "parameter_values_with_ChiSquareDataFrame.root",
            const char* BaseTTree = "marg_parameter_values",
            const char* BestFitToy = "chi2_best_fit_toy"
            ) {
	TFile *f2 = new TFile(FitFile, "read");
        std::vector<double> *best_fit_toy = (std::vector<double> *) f2->Get(BestFitToy);

        ROOT::RDataFrame df(BaseTTree, ParameterFile);
        df.Define(BestFitToy, [best_fit_toy](ULong64_t idx){ return best_fit_toy->at(idx); }, {"rdfentry_"})
                .Snapshot(BaseTTree, OutputFile);


You’re right, I assumed that the size of the best_fit_toy vector is identical to the number of rows in the tree. Is this not the case for your actual data?

1 Like

Sorry for the long reply time, I’ve checked and there should be no case in actual data where the vector should be less than the length of the TTree. It was just an issue whilst I was testing (so I’ll put in an error check), so that problem is solved! Thank you!. The last issue it seems to be having is that since the TH1 error has been resolved by instead allowing my branches to be made as TH1D and TH2D objects instead. It seems to be disliking the TH2D branches. I have checked and they are filled correctly and are able to be printed. Does RDataFrame usually object to these objects? Below is the error and the printed branch (with a small name change).

Error in <TTree::Branch>: Reference interface requires a valid object (for branch: TwoDimensionalBranch)!

Error in <TBranchProxy::Read>: Unable to initialize TwoDimensionalBranch
*Br  147 : TwoDimensionalBranch : TH2D                                  *
*Entries :        2 : Total  Size=       3005 bytes  File Size  =        537 *
*Baskets :        1 : Basket Size=      32000 bytes  Compression=   4.58     *

It seems that somewhere there is still a call to TTree::Branch() with an object that is nullptr. Can you send me a minimal macro that produces the error?

1 Like

Hi, I think I have managed it :slight_smile: If you have any questions/comments about it, especially about my coding style, then please say! This macro combines the Tree generation, vector generation, and Data Frame snapshot whereas in the actual code, all three are separate apps/macros.
MinimalTreeMacro.C (2.8 KB)

Thanks! I added _file_pars->Close(); in line 70 to avoid the warning Warning in <TFile::Init>: file ParameterFile.root probably not closed, trying to recover.

On the latest ROOT, the test macro runs without the TTree::Branch warning. Can you try with ROOT 6.22/02?

1 Like

That would unfortunately be quite difficult with my current work setup. I forgot to include it in the original but the ROOT version of the framework I am working on is 6.18/04. Is there an online terminal that I can test a simple macro in for the current root version?

Anyway, if this requires a more up to date version of root than the framework runs on, I won’t be able to impliment the RDataFrame version of the macro but will make a note that this is a possible solution for future developers/users. Thank you so much for all your help, I have learnt a lot!
Best wishes,

If you have ssh access to, you can use the default ROOT there.

Yep, I see it working too! Other than the parameters giving me bad numerical expressions (because I forgot to cast ip from an int to double during array filling) I see no other error. Thank you so much!
P.S. I don’t think I can mark multiple solutions here so that’s why I haven’t marked yours :slight_smile:

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.