All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
BinningSpecs.h
Go to the documentation of this file.
1 /**
2  * @file icarusalg/Utilities/BinningSpecs.h
3  * @brief Simple utility for human-friendly binning.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date September 21, 2021
6  *
7  *
8  */
9 
10 #ifndef ICARUSALG_UTILITIES_BINNINGSPECS_H
11 #define ICARUSALG_UTILITIES_BINNINGSPECS_H
12 
13 
14 // C/C++ standard libraries
15 #include <initializer_list>
16 #include <utility> // std::pair
17 
18 
19 // -----------------------------------------------------------------------------
20 namespace icarus::ns::util {
21 
22  class BinningSpecs;
23 
24  // --- BEGIN -- Algorithms for binning ---------------------------------------
25  /**
26  * @name Algorithms for binning.
27  *
28  * The `BinningSpecs` class collects the usual characteristics of a binning:
29  * full range boundaries, number of bins, bin width.
30  *
31  * A few functions are provided that create a binning with "human-friendly"
32  * characteristics for pleasant plots: given a selection of bin width hints,
33  * the functions try to create a binning accommodating those hints.
34  *
35  * These functions always require the full range of the binning to be
36  * specified, and can take either a desired number of bins, or their width.
37  *
38  */
39  /// @{
40 
41  /// Set of bin sizes to be considered by the binning algorithms.
42  inline constexpr std::initializer_list<double> DefaultBinningHints
43  { 1.0, 0.8, 2.0, 0.5, 4.0, 5.0, 10.0, 20.0 };
44 
45 
46  /// Stretch factor on the requested binning range an algorithm is allowed.
47  inline constexpr double DefaultAllowedBinningStretch { 0.5 };
48 
49  /**
50  * @brief Returns the "optimal" binning for the requested parameters.
51  * @param lower desired lower limit of the binning
52  * @param upper desired upper limit of the binning
53  * @param width desired bin width
54  * @param hints set of bin sizes to consider
55  * (not including the order of magnitude)
56  * @param allowedStretch how much the resulting range can differ from the
57  * desired one (`upper - lower`), as a factor
58  * @return the optimal binning
59  *
60  * Bin width is used as returned by `chooseBinningWidth()` function, which
61  * chooses it so that it is multiple (within its order of magnitude)
62  * of any of the hinted factors and the total range is not "too far"
63  * (the stretching factor is no larger than `allowedStretch`).
64  *
65  * Lower and upper limit are then aligned with that bin width (so that `0`
66  * would appear as a bin limit).
67  * Lower and upper limits are guaranteed to be included in the binning.
68  *
69  * @note The final bin width will likely differ from the requested one.
70  */
71  BinningSpecs makeBinningFromBinWidth(
72  double lower, double upper, double width,
73  std::initializer_list<double> hints = DefaultBinningHints,
74  double allowedStretch = DefaultAllowedBinningStretch
75  );
76 
77  /**
78  * @brief Returns the "optimal" binning for the requested parameters.
79  * @param lower desired lower limit of the binning
80  * @param upper desired upper limit of the binning
81  * @param nBins desired number of bins
82  * @param hints set of bin sizes to consider
83  * (not including the order of magnitude)
84  * @param allowedStretch how much the resulting range can differ from the
85  * desired one (`upper - lower`), as a factor
86  * @return the optimal binning
87  *
88  * Bin width is used as returned by `chooseBinningWidth()` function, which
89  * chooses it so that it is multiple (within its order of magnitude)
90  * of any of the hinted factors and the total range is not "too far"
91  * (the stretching factor is no larger than `allowedStretch`).
92  *
93  * Lower and upper limit are then aligned with that bin width (so that `0`
94  * would appear as a bin limit).
95  * Lower and upper limits are guaranteed to be included in the binning.
96  *
97  * @note The final number of bins may differ from the requested one.
98  */
99  BinningSpecs makeBinningFromNBins(
100  double lower, double upper, unsigned long nBins,
101  std::initializer_list<double> hints = DefaultBinningHints,
102  double allowedStretch = DefaultAllowedBinningStretch
103  );
104 
105 
106  /**
107  * @brief Returns a binning shifted to align with the specified `boundary`.
108  * @param binning the binning to be aligned
109  * @param boundary the point to align the binning with
110  * @param extendCoverage (default: `true`) increase number of bins if needed
111  * @return a new, aligned binning
112  *
113  * The binning lower and upper boundaries are moved so that one of the bins
114  * has `boundary` as a border.
115  * The shift is the minimal to achieve the goal.
116  * If `extendCoverage` is `true`, if the boundaries are shifted a single bin
117  * is also added to the binning to preserve (and extend) the original coverage
118  * region; otherwise, the size of the binning stays the same but part of the
119  * original range may not be covered by the returned binning.
120  */
121  BinningSpecs alignBinningTo
122  (BinningSpecs const& binning, double boundary, bool extendCoverage = true);
123 
124 
125  /**
126  * @brief Returns the "optimal" bin width for the requested parameters.
127  * @param lower desired lower limit of the binning
128  * @param upper desired upper limit of the binning
129  * @param width desired bin width
130  * @param nBins desired number of bins
131  * @param hints set of bin sizes to consider
132  * (not including the order of magnitude)
133  * @param allowedStretch how much the resulting range can differ from the
134  * desired one (`upper - lower`), as a factor
135  * @return the recommended bin width
136  *
137  * This is the core algorithm for determining a binning.
138  * Bin width is chosen so that it is multiple (within its order of magnitude)
139  * of any of the hinted factors and the total range is not "too far"
140  * (the stretching factor is no larger than `allowedStretch`).
141  *
142  * The hint is chosen that yields the lower stretch. On tie, priority is given
143  * to the earlier hint in the list.
144  * If no hint is good enough, `width` is returned unchanged.
145  */
146  double chooseBinningWidth(
147  double lower, double upper, double width, unsigned long nBins,
148  std::initializer_list<double> hints = DefaultBinningHints,
149  double allowedStretch = DefaultAllowedBinningStretch
150  );
151 
152  // --- END ---- Algorithms ---------------------------------------------------
153 
154 
155 } // namespace icarus::ns::util
156 
157 
158 // -----------------------------------------------------------------------------
159 /**
160  * @brief Data structure holding binning information.
161  *
162  * The binning is at fixed bin size.
163  *
164  * Nothing fancy so far.
165  *
166  * Functions like `icarus::ns::util::makeBinningFromBinWidth()` and
167  * `icarus::ns::util::makeBinningFromNBins()` are the recommended way to create
168  * a `BinningSpecs` object.
169  */
171 
172  double fLower { 0.0 }; ///< Lower range limit.
173  double fWidth { 0.0 }; ///< Width of all bins.
174  unsigned long fNBins{ 0UL }; ///< Number of bins.
175  double fUpper { 0.0 }; ///< Upper range limit.
176 
177  public:
178 
179  /// Constructor: all fields specified, no adjustment performed.
180  BinningSpecs(double lower, double upper, double width);
181 
182  // --- BEGIN -- Access to binning specifications -----------------------------
183  /// @name Access to binning specifications
184  /// @{
185 
186  /// Returns the value of the lower end of the first bin.
187  double lower() const { return fLower; }
188 
189  /// Returns the value of the upper end of the last bin.
190  double upper() const { return fUpper; }
191 
192  /// Returns the full range covered by the binning.
193  double range() const { return upper() - lower(); }
194 
195  /// Returns the number of bins.
196  unsigned long nBins() const { return fNBins; }
197 
198  /// Returns the width of the bins (all bins have the same width).
199  double binWidth() const { return fWidth; }
200 
201  /// @}
202  // --- END ---- Access to binning specifications -----------------------------
203 
204 
205  // --- BEGIN -- Access to binning specifications -----------------------------
206  /// @name Access to bins
207  /// @{
208 
209  // very incomplete interface; add freely!
210 
211  /// Returns the index of the bin with the specified value
212  /// (bin of `lower()` is `0`, bin of `upper()` is `nBins()`).
213  int binWith(double value) const;
214 
215  /// Returns the lower and upper borders of the bin with the specified index.
216  std::pair<double, double> binBorders(int iBin) const;
217 
218  /// @}
219  // --- END ---- Access to binning specifications -----------------------------
220 
221 
222  /// Returns a number of bins large enough to cover the specified range.
223  static unsigned long NBinsFor(double lower, double upper, double width);
224 
225 }; // class icarus::ns::util::BinningSpecs
226 
227 
228 // -----------------------------------------------------------------------------
229 
230 #endif // ICARUSALG_UTILITIES_BINNINGSPECS_H
double range() const
Returns the full range covered by the binning.
Definition: BinningSpecs.h:193
unsigned long fNBins
Number of bins.
Definition: BinningSpecs.h:174
constexpr double DefaultAllowedBinningStretch
Stretch factor on the requested binning range an algorithm is allowed.
Definition: BinningSpecs.h:47
int binWith(double value) const
BinningSpecs(double lower, double upper, double width)
Constructor: all fields specified, no adjustment performed.
double lower() const
Returns the value of the lower end of the first bin.
Definition: BinningSpecs.h:187
double chooseBinningWidth(double lower, double upper, double width, unsigned long nBins, std::initializer_list< double > hints=DefaultBinningHints, double allowedStretch=DefaultAllowedBinningStretch)
Returns the &quot;optimal&quot; bin width for the requested parameters.
BinningSpecs alignBinningTo(BinningSpecs const &binning, double boundary, bool extendCoverage=true)
Returns a binning shifted to align with the specified boundary.
double fUpper
Upper range limit.
Definition: BinningSpecs.h:175
Data structure holding binning information.
Definition: BinningSpecs.h:170
double binWidth() const
Returns the width of the bins (all bins have the same width).
Definition: BinningSpecs.h:199
std::pair< double, double > binBorders(int iBin) const
Returns the lower and upper borders of the bin with the specified index.
constexpr std::initializer_list< double > DefaultBinningHints
Set of bin sizes to be considered by the binning algorithms.
Definition: BinningSpecs.h:43
double fLower
Lower range limit.
Definition: BinningSpecs.h:172
double fWidth
Width of all bins.
Definition: BinningSpecs.h:173
double upper() const
Returns the value of the upper end of the last bin.
Definition: BinningSpecs.h:190
static unsigned long NBinsFor(double lower, double upper, double width)
Returns a number of bins large enough to cover the specified range.
BinningSpecs makeBinningFromNBins(double lower, double upper, unsigned long nBins, std::initializer_list< double > hints=DefaultBinningHints, double allowedStretch=DefaultAllowedBinningStretch)
Returns the &quot;optimal&quot; binning for the requested parameters.
temporary value
unsigned long nBins() const
Returns the number of bins.
Definition: BinningSpecs.h:196
BinningSpecs makeBinningFromBinWidth(double lower, double upper, double width, std::initializer_list< double > hints=DefaultBinningHints, double allowedStretch=DefaultAllowedBinningStretch)
Returns the &quot;optimal&quot; binning for the requested parameters.