TTreeReader の使い方 (CERN ROOT)

## TTreeReader とは

[ROOT](https://root.cern/) では [TTree](https://root.cern/doc/master/classTTree.html) や [TChain](https://root.cern.ch/doc/master/classTChain.html) からイベントの情報を取り出してデータ解析を行います。それには `SetBranchAddress()` してから `GetEntry()` して、というのが伝統的な方法でしたが、よりモダンな方法として [TTreeReader](https://root.cern.ch/doc/v608/classTTreeReader.html) を使う方法を紹介します。
実際、以下のページでは

- [7. Using the TTreeReader | ROOT a Data analysis Framework](https://root.cern.ch/7-using-ttreereader)

>There is a traditional way of doing this (TTree::SetBranchAddress() etc) and a new, recommended one: the TTreeReader. 

とあり、`TTreeReader` がおすすめされています。
[ヘッダファイルのコメント](https://github.com/root-project/root/blob/master/tree/treeplayer/inc/TTreeReader.h) を見る限りでは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](https://root.cern.ch/doc/master/classTBranch.html) を含んだ [TNtuple](https://root.cern.ch/doc/master/classTNtuple.html) が入っています[^1]。

[^1]: `TNtuple` は float しか入れられない `TTree` だと思えば良いです。この例は `TNtuple` を用いているせいで float 以外の型が出て来ないし、配列を扱わないためあまり良くないのですが、自分で root ファイルを準備するのが面倒だったためこれでいきます。 

## 旧来の方法

先に伝統技法による読み込みをやってみます。

```c++
{
  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` を使う方法。

```C++
{
  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

- [ROOT: TTreeReader Class Reference](https://root.cern.ch/doc/v608/classTTreeReader.html)

(3年ちょっとぶりの ROOT の記事でした。 [前回](https://qiita.com/pn11/items/36af3e3067f5d11f18eb))