TTreeReader とは
ROOT では TTree や TChain からイベントの情報を取り出してデータ解析を行います。それには SetBranchAddress()
してから GetEntry()
して、というのが伝統的な方法でしたが、よりモダンな方法として TTreeReader を使う方法を紹介します。
実際、以下のページでは
There is a traditional way of doing this (TTree::SetBranchAddress() etc) and a new, recommended one: the TTreeReader.
とあり、TTreeReader
がおすすめされています。
ヘッダファイルのコメント を見る限りでは2010年からあったようですが、現時点であまり広まっていないように思えます。というわけで広めるべく使っていきます。
準備
上記のネタ元のページに従って、ROOT に同梱されている $ROOTSYS/tutorials/hsimple.root
を使ってやっていきます。
$ root $ROOTSYS/tutorials/hsimple.root
root [0]
Attaching file root/tutorials/hsimple.root as _file0...
(TFile *) 0x7fffba2ba700
root [1] _file0->ls()
TFile** root/tutorials/hsimple.root Demo ROOT file with histograms
TFile* root/tutorials/hsimple.root Demo ROOT file with histograms
KEY: TH1F hpx;1 This is the px distribution
KEY: TH2F hpxpy;1 py vs px
KEY: TProfile hprof;1 Profile of pz versus px
KEY: TNtuple ntuple;1 Demo ntuple
root [2] ntuple->Print()
******************************************************************************
*Tree :ntuple : Demo ntuple *
*Entries : 25000 : Total = 504176 bytes File Size = 429479 *
* : : Tree compression factor = 1.17 *
******************************************************************************
*Br 0 :px : Float_t *
*Entries : 25000 : Total Size= 100755 bytes File Size = 92913 *
*Baskets : 4 : Basket Size= 32000 bytes Compression= 1.08 *
*............................................................................*
*Br 1 :py : Float_t *
*Entries : 25000 : Total Size= 100755 bytes File Size = 92934 *
*Baskets : 4 : Basket Size= 32000 bytes Compression= 1.08 *
*............................................................................*
*Br 2 :pz : Float_t *
*Entries : 25000 : Total Size= 100755 bytes File Size = 91194 *
*Baskets : 4 : Basket Size= 32000 bytes Compression= 1.10 *
*............................................................................*
*Br 3 :random : Float_t *
*Entries : 25000 : Total Size= 100787 bytes File Size = 90003 *
*Baskets : 4 : Basket Size= 32000 bytes Compression= 1.11 *
*............................................................................*
*Br 4 :i : Float_t *
*Entries : 25000 : Total Size= 100747 bytes File Size = 61673 *
*Baskets : 4 : Basket Size= 32000 bytes Compression= 1.63 *
*............................................................................*
というわけでこのファイルには、px
, py
, pz
の Float_t
型の TBranch を含んだ TNtuple が入っています1。
旧来の方法
先に伝統技法による読み込みをやってみます。
{
auto file = new TFile("$ROOTSYS/tutorials/hsimple.root");
auto tree = static_cast<TTree*>(file->Get("ntuple"));
float px, py, pz;
tree->SetBranchAddress("px", &px);
tree->SetBranchAddress("py", &py);
tree->SetBranchAddress("pz", &pz);
for (auto i=0; i < tree->GetEntries(); ++i){
tree->GetEntry(i);
std::cout << px << " " << py << " " << pz << std::endl;
}
}
あらかじめ px
, py
, pz
という float の変数を用意しておき、それぞれを TTree
の Branch に割り当ててから、イベントを読み込むループを回すという手順です。
TTreeReader を使う方法
次に、TTreeReader
を使う方法。
{
auto file = new TFile("$ROOTSYS/tutorials/hsimple.root");
TTreeReader reader("ntuple", file);
TTreeReaderValue<float> pxit(reader, "px");
TTreeReaderValue<float> pyit(reader, "py");
TTreeReaderValue<float> pzit(reader, "pz");
while (reader.Next()) {
std::cout << *pxit << " " << *pyit << " " << *pzit << std::endl;
}
}
このコードからは以下のような特徴が見てとれます。
-
TTree
は直接このコードには出て来ず、TTreeReader
を通してしかアクセスしていない。 - 旧来のコードで
SetBranchAddress
していた部分ではTTreeReaderValue
というのを使っている。 - イベントループはイタレータっぽくなっている。
ループの書き方は少し見易くなった感はあるものの、それ以上に違うのは SetBranchAddress
がなくなったことかと思います。SetBranchAddress
は scanf
のような見た目をしていて C っぽいです。一方 TTreeReaderValue<>
はテンプレート引数が出て来て C++ っぽいです。
見た目だけではなく、こう書くことによる利点として、実行時に型チェックが行われるというのがあります。例えば、TTreeReaderValue<double> pxit(reader, "px");
とすると、TTree
に記録された float
と合わないため実行時エラーとなります。一方で、旧来の方法では double px; tree->SetBranchAddress("px", &px);
としてもエラーになってくれません。
その他、注意点として、
- Branch に配列が入っている場合、
TTreeReaderValue<float>
ではなくTTreeReaderArray<float>
のようになります。 -
TTreeReaderValue
の値にアクセスするには、std::vector::iterator
などと同様、*pxit
のようにする必要があります。 - 速度については未検証です。少なくとも今回のような小さなサイズで、マクロとして実行した場合には違いは見られませんでした。
Reference
(3年ちょっとぶりの ROOT の記事でした。 前回)
-
TNtuple
は float しか入れられないTTree
だと思えば良いです。この例はTNtuple
を用いているせいで float 以外の型が出て来ないし、配列を扱わないためあまり良くないのですが、自分で root ファイルを準備するのが面倒だったためこれでいきます。 ↩