|
| 1 | +/// \file |
| 2 | +/// \ingroup tutorial_dataframe |
| 3 | +/// \notebook |
| 4 | +/// Fill and draw a bar chart from a categorical/string column with RDataFrame. |
| 5 | +/// |
| 6 | +/// RDataFrame has no dedicated action for categorical/string columns, but the |
| 7 | +/// generic RDataFrame::Fill() action can fill any user-provided object that |
| 8 | +/// exposes Fill() and Merge() methods. This shows how to use it to build and |
| 9 | +/// draw a bar chart from a string column, reusing the "Nation" column of the |
| 10 | +/// classic CERN staff dataset also used in hist006_TH1_bar_charts.C. |
| 11 | +/// |
| 12 | +/// Classic ROOT trees often store short strings in fixed-size char[] branches |
| 13 | +/// (leaf type "C"), which RDataFrame reads as ROOT::VecOps::RVec<char> rather |
| 14 | +/// than std::string. A one-line Define() converts such a column into a real |
| 15 | +/// std::string column before filling. |
| 16 | +/// |
| 17 | +/// \macro_image |
| 18 | +/// \macro_code |
| 19 | +/// \macro_output |
| 20 | +/// |
| 21 | +/// \date July 2026 |
| 22 | +/// \author Mehkaan Khan |
| 23 | + |
| 24 | +// A small adapter around TH1D that lets RDataFrame's generic Fill() action |
| 25 | +// build a histogram from a std::string column: TH1D::Fill(const char*, Double_t) |
| 26 | +// takes no default weight and std::string has no implicit conversion to |
| 27 | +// const char*, so RDataFrame can't call it directly on a plain TH1D. Wrapping |
| 28 | +// it in a class with our own Fill(const std::string&) sidesteps both issues. |
| 29 | +struct AlphaNumHist { |
| 30 | + TH1D h; |
| 31 | + |
| 32 | + AlphaNumHist(const char *name, const char *title) : h(name, title, 1, 0, 1) |
| 33 | + { |
| 34 | + h.GetXaxis()->SetAlphanumeric(true); |
| 35 | + h.SetCanExtend(TH1::kAllAxes); |
| 36 | + } |
| 37 | + |
| 38 | + void Fill(const std::string &s) { h.Fill(s.c_str(), 1.); } |
| 39 | + |
| 40 | + // Required so RDataFrame can merge partial per-slot results when run with |
| 41 | + // implicit multi-threading enabled. |
| 42 | + void Merge(const std::vector<AlphaNumHist *> &others) |
| 43 | + { |
| 44 | + TList l; |
| 45 | + for (auto *o : others) |
| 46 | + l.Add(&o->h); |
| 47 | + h.Merge(&l); |
| 48 | + } |
| 49 | +}; |
| 50 | + |
| 51 | +void df041_alphanumericHistograms() |
| 52 | +{ |
| 53 | + // Reuse the same "cernstaff.root" dataset as hist006_TH1_bar_charts.C, |
| 54 | + // generating it first if it doesn't already exist. |
| 55 | + TString filedir = gROOT->GetTutorialDir(); |
| 56 | + filedir += TString("/io/tree/"); |
| 57 | + TString filename = "cernstaff.root"; |
| 58 | + bool fileNotFound = gSystem->AccessPathName(filename); |
| 59 | + if (fileNotFound) { |
| 60 | + TString macroName = filedir + "tree500_cernbuild.C"; |
| 61 | + if (!gInterpreter->IsLoaded(macroName)) |
| 62 | + gInterpreter->LoadMacro(macroName); |
| 63 | + gROOT->ProcessLineFast("tree500_cernbuild()"); |
| 64 | + } |
| 65 | + |
| 66 | + ROOT::RDataFrame df0("T", filename.Data()); |
| 67 | + |
| 68 | + // "Nation" is a classic char[] branch, read by RDataFrame as |
| 69 | + // ROOT::VecOps::RVec<char>. Converting it to a real std::string column |
| 70 | + // makes it fillable through the AlphaNumHist adapter above. |
| 71 | + auto df = df0.Define("NationStr", [](const ROOT::VecOps::RVec<char> &c) { return std::string(c.begin(), c.end()); }, |
| 72 | + {"Nation"}); |
| 73 | + |
| 74 | + AlphaNumHist model("hNation", "Staff by nation;Nation;Count"); |
| 75 | + auto h = df.Fill<std::string>(model, {"NationStr"}); |
| 76 | + h->h.LabelsDeflate("X"); |
| 77 | + |
| 78 | + auto c1 = new TCanvas("c1", "Bar chart from RDataFrame", 700, 500); |
| 79 | + h->h.SetFillColor(45); |
| 80 | + h->h.DrawCopy("bar2"); |
| 81 | +} |
0 commit comments