All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
BinningSpecs.cxx
Go to the documentation of this file.
1 /**
2  * @file icarusalg/Utilities/BinningSpecs.cxx
3  * @brief Simple utility for human-friendly binning.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date September 21, 2021
6  * @see icarusalg/Utilities/BinningSpecs.h
7  *
8  *
9  */
10 
11 // library header
13 
14 // C/C++ standard libraries
15 #include <utility> // std::move(), std::pair
16 #include <cmath>
17 #include <cassert>
18 
19 
20 // -----------------------------------------------------------------------------
21 // --- Implementation
22 // -----------------------------------------------------------------------------
23 namespace {
24 
25  /// Returns the smallest multiple of `factor` not larger than `value`.
26  double floorMult(double value, double factor)
27  { return factor * std::floor(value / factor); }
28 
29  /// Returns a binning with boundaries aligned to `0`.
30  icarus::ns::util::BinningSpecs makeBinningAlignedTo0
31  (double lower, double upper, double width)
32  {
34  { floorMult(lower, width), upper, width };
35  }
36 
37 } // local namespace
38 
39 
40 // -----------------------------------------------------------------------------
41 // --- icarus::ns::util::BinningSpecs
42 // -----------------------------------------------------------------------------
44  (double lower, double upper, double width)
45  : fLower{ lower }
46  , fWidth{ width }
47  , fNBins{ NBinsFor(fLower, upper, fWidth) }
48  , fUpper{ fLower + fNBins * fWidth }
49 {
50  assert(lower <= upper);
51  assert(width >= 0.0);
52  assert(fLower <= lower);
53  assert(fUpper >= upper);
54 }
55 
56 
57 // -----------------------------------------------------------------------------
59  { return static_cast<int>(std::floor((value - lower()) / binWidth())); }
60 
61 
62 // -----------------------------------------------------------------------------
63 std::pair<double, double> icarus::ns::util::BinningSpecs::binBorders
64  (int iBin) const
65 {
66  double const low = lower() + binWidth() * iBin;
67  return { low, low + binWidth() };
68 } // icarus::ns::util::BinningSpecs::binBorders()
69 
70 
71 // -----------------------------------------------------------------------------
73  (double lower, double upper, double width)
74  { return static_cast<unsigned long>(std::ceil((upper - lower) / width)); }
75 
76 
77 // -----------------------------------------------------------------------------
78 // --- functions
79 // -----------------------------------------------------------------------------
81  double lower, double upper, double width,
82  std::initializer_list<double> hints /* = DefaultBinningHints */,
83  double allowedStretch /* = DefaultAllowedBinningStretch */
84 ) -> BinningSpecs {
85 
86  double const finalWidth = chooseBinningWidth(
87  lower, upper, width, BinningSpecs::NBinsFor(lower, upper, width),
88  std::move(hints), allowedStretch
89  );
90  return makeBinningAlignedTo0(lower, upper, finalWidth);
91 
92 } // icarus::ns::util::makeBinningFromBinWidth()
93 
94 
95 // -----------------------------------------------------------------------------
97  double lower, double upper, unsigned long nBins,
98  std::initializer_list<double> hints /* = DefaultBinningHints */,
99  double allowedStretch /* = DefaultAllowedBinningStretch */
100 ) -> BinningSpecs {
101 
102  double const finalWidth = chooseBinningWidth(
103  lower, upper, (upper - lower) / nBins, nBins,
104  std::move(hints), allowedStretch
105  );
106  return makeBinningAlignedTo0(lower, upper, finalWidth);
107 
108 } // icarus::ns::util::makeBinningFromNBins()
109 
110 
111 // -----------------------------------------------------------------------------
113  BinningSpecs const& binning, double boundary, bool extendCoverage /* = true */
114 ) -> BinningSpecs {
115 
116  int const iBin = binning.binWith(boundary);
117  std::pair<double, double> const binBorders = binning.binBorders(iBin);
118 
119  double const shift { boundary - (
120  ((boundary - binBorders.first) <= (binBorders.second - boundary))
121  ? binBorders.first
122  : boundary - binBorders.second
123  )
124  };
125 
126  double const width = binning.binWidth();
127  double lower = binning.lower() + shift;
128  double upper = binning.upper() + shift;
129  if (extendCoverage && (shift != 0.0)) { // rounding may be trouble here...
130  if (shift > 0.0) lower -= width;
131  else upper += width;
132  }
133 
134  return BinningSpecs{ lower, upper, width };
135 
136 } // icarus::ns::util::alignBinningTo()
137 
138 
139 // -----------------------------------------------------------------------------
141  double lower, double upper,
142  double width, unsigned long nBins,
143  std::initializer_list<double> hints /* = DefaultBinningHints */,
144  double allowedStretch /* = DefaultAllowedBinningStretch */
145 ) {
146 
147  assert(allowedStretch > 0.0);
148  assert(width > 0.0);
149  assert(lower <= upper);
150 
151  // order of magnitude of the bins: width will be chosen as this power-of-ten
152  // multiplied by one of the hinted values
153  double const order = std::pow(10.0, std::floor(std::log10(width)));
154 
155  double span = upper - lower;
156 
157  // don't consider binnings stretching the range more than allowed;
158  // if no hinted binning is good enough, exact `width` will be used
159  using Quality_t = std::pair<double, double>; // stretch/distance from request
160 
161  double best_w = width;
162  Quality_t best_d { allowedStretch, 0.0 };
163  for (double const factor: hints) {
164  double const w = order * factor;
165  Quality_t const d {
166  std::abs((w * nBins / span) - 1.0),
167  std::abs(w - width)
168  };
169  if (d >= best_d) continue;
170  best_d = d;
171  best_w = w;
172  } // for
173 
174  return best_w;
175 
176 } // icarus::ns::util::chooseBinningWidth()
177 
178 
179 // -----------------------------------------------------------------------------
int binWith(double value) const
Simple utility for human-friendly binning.
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
shift
Definition: fcl_checks.sh:26
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.
standard_dbscan3dalg useful for diagnostics hits not in a line will not be clustered on on only for track like only for track like on on the smaller the less shower like tracks low
BinningSpecs alignBinningTo(BinningSpecs const &binning, double boundary, bool extendCoverage=true)
Returns a binning shifted to align with the specified boundary.
T abs(T value)
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.
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
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.