mist-hep 0.1.0
ROOT-backed analysis helpers built on mist
Loading...
Searching...
No Matches
transforms.h
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2//
3// mist/hep/graph/transforms.h — point-wise TGraph transforms.
4//
5// Header-only. Each function takes a const reference and returns an owning
6// mist::hep::owned::root_ptr — callers decide lifetime, nothing leaks at TU
7// scope, and returned histograms/graphs carry ROOT-correct ownership.
8//
9// Ports / rewrites of:
10// - graphutils `diff` -> relative_diff (F-11)
11// - graphutils `fromZero` -> shift_x_to_origin (F-12)
12// - graphutils `invertY` -> negate_y (F-13)
13// - BLU `uInvertX`/`uInvertXY` -> negate_x / negate_xy (F-18)
14// - BLU `uScale` -> scale (F-17)
15// - BLU `uMakeMeTGraphErrors` -> to_graph_errors (F-32)
16// - DHL `DHLgraph::Swap_xy` -> swap_xy (F-34)
17//
18#pragma once
19
20#include <cmath>
21
22#include <TGraph.h>
23#include <TGraphErrors.h>
24#include <TProfile.h>
25
26#include <mist/hep/owned.h>
27
28namespace mist::hep::graph {
29
30namespace owned = ::mist::hep::owned;
31
32// ---------------------------------------------------------------------------
33// Out-of-range policy for relative_diff: what to do with a subject point
34// whose x lies outside the reference's x-range.
35// ---------------------------------------------------------------------------
36enum class out_of_range {
37 skip,
38 nan,
40};
41
42// ---------------------------------------------------------------------------
43// relative_diff: (y - y_ref) / y_ref, evaluated point-wise at the subject's
44// x coordinates against a linearly-interpolated reference.
45//
46// Returns NaN for any point where the reference value is zero (the
47// graphutils original divided silently, yielding +-inf).
48//
49// `reference` is assumed sorted ascending in x (its first/last points define
50// the in-range interval).
51// ---------------------------------------------------------------------------
52[[nodiscard]] inline owned::root_ptr<TGraph>
53relative_diff(const TGraph& subject,
54 const TGraph& reference,
56{
57 auto out = owned::make<TGraph>();
58 const int n_ref = reference.GetN();
59 if (n_ref == 0) return out;
60
61 const double x_lo = reference.GetPointX(0);
62 const double x_hi = reference.GetPointX(n_ref - 1);
63
64 for (int i = 0; i < subject.GetN(); ++i) {
65 const double x = subject.GetPointX(i);
66 const double y = subject.GetPointY(i);
67 const bool in_range = (x >= x_lo && x <= x_hi);
68
69 if (!in_range && policy == out_of_range::skip) continue;
70
71 double value;
72 if (!in_range && policy == out_of_range::nan) {
73 value = std::nan("");
74 } else {
75 const double ref = reference.Eval(x);
76 value = (ref == 0.0) ? std::nan("") : (y - ref) / ref;
77 }
78 out->SetPoint(out->GetN(), x, value);
79 }
80 return out;
81}
82
83// ---------------------------------------------------------------------------
84// shift_x_to_origin: translate the x-axis so the first point lands at x = 0.
85// ---------------------------------------------------------------------------
86[[nodiscard]] inline owned::root_ptr<TGraph>
87shift_x_to_origin(const TGraph& source)
88{
89 auto out = owned::make<TGraph>();
90 if (source.GetN() == 0) return out;
91 const double x0 = source.GetPointX(0);
92 for (int i = 0; i < source.GetN(); ++i)
93 out->SetPoint(i, source.GetPointX(i) - x0, source.GetPointY(i));
94 return out;
95}
96
97// ---------------------------------------------------------------------------
98// negate_y / negate_x / negate_xy: copy with the chosen coordinate(s) negated.
99// ---------------------------------------------------------------------------
100[[nodiscard]] inline owned::root_ptr<TGraph>
101negate_y(const TGraph& source)
102{
103 auto out = owned::make<TGraph>();
104 for (int i = 0; i < source.GetN(); ++i)
105 out->SetPoint(i, source.GetPointX(i), -source.GetPointY(i));
106 return out;
107}
108
109[[nodiscard]] inline owned::root_ptr<TGraph>
110negate_x(const TGraph& source)
111{
112 auto out = owned::make<TGraph>();
113 for (int i = 0; i < source.GetN(); ++i)
114 out->SetPoint(i, -source.GetPointX(i), source.GetPointY(i));
115 return out;
116}
117
118[[nodiscard]] inline owned::root_ptr<TGraph>
119negate_xy(const TGraph& source)
120{
121 auto out = owned::make<TGraph>();
122 for (int i = 0; i < source.GetN(); ++i)
123 out->SetPoint(i, -source.GetPointX(i), -source.GetPointY(i));
124 return out;
125}
126
127// ---------------------------------------------------------------------------
128// scale: multiply x by scale_x and y by scale_y, point-wise.
129//
130// The TGraphErrors overload also scales the error bars (by |scale_x| /
131// |scale_y| so errors stay non-negative). The BLU original scaled the input
132// graph in place by accident; this writes only the returned copy.
133// ---------------------------------------------------------------------------
134[[nodiscard]] inline owned::root_ptr<TGraph>
135scale(const TGraph& source, double scale_x, double scale_y)
136{
137 auto out = owned::make<TGraph>();
138 for (int i = 0; i < source.GetN(); ++i)
139 out->SetPoint(i, scale_x * source.GetPointX(i),
140 scale_y * source.GetPointY(i));
141 return out;
142}
143
144[[nodiscard]] inline owned::root_ptr<TGraphErrors>
145scale(const TGraphErrors& source, double scale_x, double scale_y)
146{
147 auto out = owned::make<TGraphErrors>();
148 for (int i = 0; i < source.GetN(); ++i) {
149 out->SetPoint(i, scale_x * source.GetPointX(i),
150 scale_y * source.GetPointY(i));
151 out->SetPointError(i, std::fabs(scale_x) * source.GetErrorX(i),
152 std::fabs(scale_y) * source.GetErrorY(i));
153 }
154 return out;
155}
156
157// ---------------------------------------------------------------------------
158// swap_xy: exchange the x and y coordinates of every point. The inverse of
159// reflecting across the diagonal; turns y = f(x) data into x = f(y).
160//
161// Rewrite of DHL's DHLgraph::Swap_xy. The DHL original was an in-place
162// method on a TGraph subclass; this is the mist-hep idiom — a free function
163// that returns an owning copy and leaves the source untouched.
164// ---------------------------------------------------------------------------
165[[nodiscard]] inline owned::root_ptr<TGraph>
166swap_xy(const TGraph& source)
167{
168 auto out = owned::make<TGraph>();
169 for (int i = 0; i < source.GetN(); ++i)
170 out->SetPoint(i, source.GetPointY(i), source.GetPointX(i));
171 return out;
172}
173
174// TGraphErrors overload: x/y values and their errors are exchanged together.
175[[nodiscard]] inline owned::root_ptr<TGraphErrors>
176swap_xy(const TGraphErrors& source)
177{
178 auto out = owned::make<TGraphErrors>();
179 for (int i = 0; i < source.GetN(); ++i) {
180 out->SetPoint(i, source.GetPointY(i), source.GetPointX(i));
181 out->SetPointError(i, source.GetErrorY(i), source.GetErrorX(i));
182 }
183 return out;
184}
185
186// ---------------------------------------------------------------------------
187// to_graph_errors: copy a TGraph into a TGraphErrors with zero error bars.
188// Useful as a bridge before feeding a plain TGraph to error-aware code.
189// ---------------------------------------------------------------------------
190[[nodiscard]] inline owned::root_ptr<TGraphErrors>
191to_graph_errors(const TGraph& source)
192{
193 auto out = owned::make<TGraphErrors>();
194 for (int i = 0; i < source.GetN(); ++i) {
195 out->SetPoint(i, source.GetPointX(i), source.GetPointY(i));
196 out->SetPointError(i, 0.0, 0.0);
197 }
198 return out;
199}
200
201// ---------------------------------------------------------------------------
202// from_profile: convert a TProfile to a TGraphErrors. Each filled bin becomes
203// a point at (bin centre, mean) with x-error = half bin width and y-error =
204// the profile's bin error. Salvaged from the SiPM TProfile_to_TGraphErrors
205// (and fixed: the original dropped the last bin with a `< GetNbinsX()` loop).
206// ---------------------------------------------------------------------------
207[[nodiscard]] inline owned::root_ptr<TGraphErrors>
208from_profile(const TProfile& source)
209{
210 auto out = owned::make<TGraphErrors>();
211 for (int i = 1; i <= source.GetNbinsX(); ++i) {
212 const int n = out->GetN();
213 out->SetPoint(n, source.GetBinCenter(i), source.GetBinContent(i));
214 out->SetPointError(n, source.GetBinWidth(i) * 0.5, source.GetBinError(i));
215 }
216 return out;
217}
218
219} // namespace mist::hep::graph
Definition algebra.h:32
owned::root_ptr< TGraph > negate_x(const TGraph &source)
Definition transforms.h:110
out_of_range
Definition transforms.h:36
@ skip
Drop the point (the graphutils-original behaviour).
@ extrapolate
Use TGraph::Eval's linear extrapolation regardless.
@ nan
Emit the point with a NaN y-value.
owned::root_ptr< TGraph > swap_xy(const TGraph &source)
Definition transforms.h:166
owned::root_ptr< TGraph > negate_xy(const TGraph &source)
Definition transforms.h:119
owned::root_ptr< TGraph > shift_x_to_origin(const TGraph &source)
Definition transforms.h:87
owned::root_ptr< TGraph > scale(const TGraph &source, double scale_x, double scale_y)
Definition transforms.h:135
owned::root_ptr< TGraphErrors > from_profile(const TProfile &source)
Definition transforms.h:208
owned::root_ptr< TGraph > negate_y(const TGraph &source)
Definition transforms.h:101
owned::root_ptr< TGraphErrors > to_graph_errors(const TGraph &source)
Definition transforms.h:191
owned::root_ptr< TGraph > relative_diff(const TGraph &subject, const TGraph &reference, out_of_range policy=out_of_range::skip)
Definition transforms.h:53
Definition owned.h:33
std::unique_ptr< T, root_deleter > root_ptr
Definition owned.h:60
root_ptr< T > make(Args &&... args)
Definition owned.h:67