|
YODA - Yet more Objects for Data Analysis 2.0.2
|
Go to the documentation of this file.
6#ifndef YODA_MathUtils_H
7#define YODA_MathUtils_H
39 const static double MAXINT = INT_MAX;
42 static const double PI = M_PI;
45 static const double TWOPI = 2*M_PI;
61 template < typename NUM>
62 inline typename std::enable_if_t<std::is_floating_point_v<NUM>, bool>
63 isZero(NUM val, double tolerance=1e-8) {
64 return fabs(val) < tolerance;
71 template < typename NUM>
72 inline typename std::enable_if_t<std::is_integral_v<NUM>, bool>
78 template < typename NUM>
79 inline typename std::enable_if_t<std::is_floating_point_v<NUM>, bool>
80 isNaN(NUM val) { return std::isnan(val); }
83 template < typename NUM>
84 inline typename std::enable_if_t<std::is_floating_point_v<NUM>, bool>
85 notNaN(NUM val) { return !std::isnan(val); }
92 template < typename N1, typename N2>
93 inline typename std::enable_if_t<
94 std::is_arithmetic_v<N1> && std::is_arithmetic_v<N2> &&
95 (std::is_floating_point_v<N1> || std::is_floating_point_v<N2>), bool>
97 const double absavg = (std::abs(a) + std::abs(b))/2.0;
98 const double absdiff = std::abs(a - b);
99 const bool rtn = ( isZero(a) && isZero(b)) || absdiff < tolerance*absavg;
107 template < typename N1, typename N2>
108 inline typename std::enable_if_t<
109 std::is_integral_v<N1> && std::is_integral_v<N2>, bool>
115 static std::function<bool( const double, const double)> fuzzyEqComp =
116 []( const double& lhs, const double& rhs) { return fuzzyEquals(lhs, rhs); };
122 template < typename N1, typename N2>
123 inline typename std::enable_if_t<
124 std::is_arithmetic_v<N1> && std::is_arithmetic_v<N2>, bool>
132 template < typename N1, typename N2>
133 inline typename std::enable_if_t<
134 std::is_arithmetic_v<N1> && std::is_arithmetic_v<N2>, bool>
140 inline double approx( double a, int n = 5) {
141 double roundTo = pow(10.0,n);
163 template< typename NUM>
164 inline bool inRange(NUM value, NUM low, NUM high,
166 if (lowbound == OPEN && highbound == OPEN) {
167 return (value > low && value < high);
168 } else if (lowbound == OPEN && highbound == CLOSED) {
169 return (value > low && value <= high);
170 } else if (lowbound == CLOSED && highbound == OPEN) {
171 return (value >= low && value < high);
173 return (value >= low && value <= high);
178 template< typename NUM>
179 inline bool inRange(NUM value, std::pair<NUM, NUM> lowhigh,
181 return inRange(value, lowhigh.first, lowhigh.second, lowbound, highbound);
189 inline bool inRange( int value, int low, int high,
191 if (lowbound == OPEN && highbound == OPEN) {
192 return (value > low && value < high);
193 } else if (lowbound == OPEN && highbound == CLOSED) {
194 return (value > low && value <= high);
195 } else if (lowbound == CLOSED && highbound == OPEN) {
196 return (value >= low && value < high);
198 return (value >= low && value <= high);
203 inline bool inRange( int value, std::pair<int, int> lowhigh,
205 return inRange(value, lowhigh.first, lowhigh.second, lowbound, highbound);
215 template < typename NUM>
221 template < typename Num>
223 return sqrt(a*a + b*b);
227 template < typename Num>
229 return sqrt(a*a + b*b + c*c);
235 const int valsign = (val > 0) ? PLUS : MINUS;
241 if (val == 0) return ZERO;
247 if (val == 0) return ZERO;
261 inline std::vector<double> linspace( size_t nbins, double xmin, double xmax, bool include_end= true) {
262 if (xmax < xmin) throw RangeError( "xmax should not be smaller than xmin!");
263 if (nbins == 0) throw RangeError( "Requested number of bins is 0!");
264 std::vector<double> rtn;
265 const double interval = (xmax-xmin)/ static_cast<double>(nbins);
266 for ( size_t i = 0; i < nbins; ++i) {
267 rtn.push_back(xmin + i*interval);
269 assert(rtn.size() == nbins);
270 if (include_end) rtn.push_back(xmax);
280 inline std::vector<double> logspace( size_t nbins, double xmin, double xmax, bool include_end= true) {
281 if (xmax < xmin) throw RangeError( "xmax should not be smaller than xmin!");
282 if (xmin < 0) throw RangeError( "xmin should not be negative!");
283 if (nbins == 0) throw RangeError( "Requested number of bins is 0!");
284 const double logxmin = std::log(xmin);
285 const double logxmax = std::log(xmax);
286 const std::vector<double> logvals = linspace(nbins, logxmin, logxmax);
287 assert(logvals.size() == nbins+1);
288 std::vector<double> rtn; rtn.reserve(logvals.size());
290 for ( size_t i = 1; i < logvals.size()-1; ++i) {
291 rtn.push_back(std::exp(logvals[i]));
293 assert(rtn.size() == nbins);
294 if (include_end) rtn.push_back(xmax);
322 inline std::vector<double> pdfspace( size_t nbins, double xmin, double xmax, std::function< double( double)>& fn, size_t nsample=10000) {
323 const double dx = (xmax-xmin)/( double)nsample;
324 const std::vector<double> xs = linspace(nsample, xmin, xmax);
325 std::vector<double> ys(0, nsample);
326 auto posfn = [&]( double x){ return std::max(fn(x), 0.0);};
327 std::transform(xs.begin(), xs.end(), ys.begin(), posfn);
328 std::vector<double> areas; areas.reserve(nsample);
330 for ( size_t i = 0; i < ys.size()-1; ++i) {
331 const double area = (ys[i] + ys[i+1])*dx/2.0;
335 const double df = areasum/(double)nbins;
336 std::vector<double> xedges{xmin}; xedges.reserve(nbins+1);
338 for ( size_t i = 0; i < nsample-1; ++i) {
342 xedges.push_back(xs[i+1]);
345 xedges.push_back(xmax);
346 assert(xedges.size() == nbins+1);
354 template < typename NUM>
355 inline int index_between( const NUM& val, const std::vector<NUM>& binedges) {
356 if (! inRange(val, binedges.front(), binedges.back())) return -1;
358 for ( size_t i = 1; i < binedges.size(); ++i) {
359 if (val < binedges[i]) {
364 assert( inRange(index, -1, binedges.size()-1));
376 if ( isZero(sumW2)) return 0;
377 return sqr(sumW) / sumW2;
382 double sumW = 0.0, sumW2 = 0.0;
383 for ( size_t i = 0; i < weights.size(); ++i) {
385 sumW2 += sqr(weights[i]);
391 inline double mean( const std::vector<int>& sample) {
393 for ( size_t i=0; i<sample.size(); ++i) {
396 return mean/sample.size();
400 inline double mean( const double sumWX, const double sumW) {
401 return sumW? sumWX / sumW : std::numeric_limits<double>::quiet_NaN();
405 inline double mean( const std::vector<double>& sample,
406 const std::vector<double>& weights) {
407 if (sample.size() != weights.size()) throw RangeError( "Inputs should have equal length!");
408 double sumWX = 0., sumW = 0.;
409 for ( size_t i = 0; i < sample.size(); ++i) {
411 sumWX += weights[i]*sample[i];
413 return mean(sumWX, sumW);
421 inline double variance( const double sumWX, const double sumW,
422 const double sumWX2, const double sumW2) {
423 const double num = sumWX2*sumW - sqr(sumWX);
424 const double den = sqr(sumW) - sumW2;
435 return den? fabs(num/den): std::numeric_limits<double>::quiet_NaN();
439 inline double variance( const std::vector<double>& sample,
440 const std::vector<double>& weights) {
441 if (sample.size() != weights.size()) throw RangeError( "Inputs should have equal length!");
444 return std::numeric_limits<double>::quiet_NaN();
446 double sumWX = 0., sumW = 0.;
447 double sumWX2 = 0., sumW2 = 0.;
448 for ( size_t i = 0; i < sample.size(); ++i) {
450 sumWX += weights[i]*sample[i];
451 sumW2 += sqr(weights[i]);
452 sumWX2 += weights[i]* sqr(sample[i]);
454 return variance(sumWX, sumW, sumWX2, sumW2);
458 inline double stdDev( const double sumWX, const double sumW,
459 const double sumWX2, const double sumW2) {
460 return std::sqrt( variance(sumWX, sumW, sumWX2, sumW2));
464 inline double stdDev( const std::vector<double>& sample,
465 const std::vector<double>& weights) {
466 return std::sqrt( variance(sample, weights));
470 inline double stdErr( const double sumWX, const double sumW,
471 const double sumWX2, const double sumW2) {
473 if (effN == 0) return std::numeric_limits<double>::quiet_NaN();
474 const double var = variance(sumWX, sumW, sumWX2, sumW2);
475 return std::sqrt(var / effN);
479 inline double stdErr( const std::vector<double>& sample,
480 const std::vector<double>& weights) {
481 if (sample.size() != weights.size()) throw RangeError( "Inputs should have equal length!");
483 if (effN == 0) return std::numeric_limits<double>::quiet_NaN();
484 const double var = variance(sample, weights);
485 return std::sqrt(var / effN);
489 inline double RMS( const double sumWX2, const double sumW, const double sumW2) {
493 if (effN == 0) return std::numeric_limits<double>::quiet_NaN();
494 const double meanSq = sumWX2 / sumW;
495 return std::sqrt(meanSq);
499 inline double RMS( const std::vector<double>& sample,
500 const std::vector<double>& weights) {
501 if (sample.size() != weights.size()) throw RangeError( "Inputs should have equal length!");
502 double sumWX2 = 0., sumW = 0., sumW2 = 0.;
503 for ( size_t i = 0; i < sample.size(); ++i) {
505 sumW2 += sqr(weights[i]);
506 sumWX2 += weights[i]* sqr(sample[i]);
508 return RMS(sumWX2, sumW, sumW2);
512 inline double covariance( const std::vector<int>& sample1, const std::vector<int>& sample2) {
513 const double mean1 = mean(sample1);
514 const double mean2 = mean(sample2);
515 const size_t N = sample1.size();
517 for ( size_t i = 0; i < N; i++) {
518 const double cov_i = (sample1[i] - mean1)*(sample2[i] - mean2);
521 if (N > 1) return cov/(N-1);
527 inline double correlation( const std::vector<int>& sample1, const std::vector<int>& sample2) {
528 const double cov = covariance(sample1, sample2);
529 const double var1 = covariance(sample1, sample1);
530 const double var2 = covariance(sample2, sample2);
532 const double corr_strength = correlation*sqrt(var2/var1);
533 return corr_strength;
543 inline double naiveChi2( const std::vector<double>& sample1, const std::vector<double>& sample2,
544 const std::vector<double>& s1errors = std::vector<double>{},
545 const std::vector<double>& s2errors = std::vector<double>{}) {
546 if (sample1.size() != sample2.size()) {
547 throw RangeError( "Inputs should have equal length!");
549 if (s1errors.size() && sample1.size() != s1errors.size()) {
550 throw RangeError( "Inputs should have equal length!");
552 if (s2errors.size() && sample2.size() != s2errors.size()) {
553 throw RangeError( "Inputs should have equal length!");
555 const size_t N = sample1.size();
557 for ( size_t i = 0; i < N; ++i) {
558 double temp = sqr(sample1[i] - sample2[i]);
559 if (s1errors.size()) {
560 temp /= sqr(s1errors[i]) + sqr(s2errors[i]);
573 inline double naiveChi2reduced( const std::vector<double>& sample1, const std::vector<double>& sample2,
574 const std::vector<double>& s1errors = std::vector<double>{},
575 const std::vector<double>& s2errors = std::vector<double>{}) {
576 if (sample1.empty()) throw RangeError( "Inputs should not have 0 length!");
577 return naiveChi2(sample1, sample2, s1errors, s2errors)/sample1.size();
Error for e.g. use of invalid bin ranges.
Anonymous namespace to limit visibility.
double stdErr(const double sumWX, const double sumW, const double sumWX2, const double sumW2) Calculate the weighted standard error of a sample.
static const double HALFPI A pre-defined value of .
double RMS(const double sumWX2, const double sumW, const double sumW2) Calculate the weighted RMS of a sample.
std::enable_if_t< std::is_arithmetic_v< N1 > &&std::is_arithmetic_v< N2 >, bool > fuzzyLessEquals(N1 a, N2 b, double tolerance=1e-5) Compare two floating point numbers for <= with a degree of fuzziness.
std::vector< double > logspace(size_t nbins, double xmin, double xmax, bool include_end=true) Make a list of nbins + 1 values uniformly spaced in log(x) between xmin and xmax inclusive.
std::enable_if_t< std::is_arithmetic_v< N1 > &&std::is_arithmetic_v< N2 >, bool > fuzzyGtrEquals(N1 a, N2 b, double tolerance=1e-5) Compare two numbers for >= with a degree of fuzziness.
int index_between(const NUM &val, const std::vector< NUM > &binedges) Return the bin index of the given value, val, given a vector of bin edges.
std::enable_if_t< std::is_floating_point_v< NUM >, bool > isNaN(NUM val) Check if a number is NaN.
std::vector< double > linspace(size_t nbins, double xmin, double xmax, bool include_end=true) Make a list of nbins + 1 values uniformly spaced between xmin and xmax inclusive.
double covariance(const std::vector< int > &sample1, const std::vector< int > &sample2) Calculate the covariance (variance) between two samples.
double naiveChi2(const std::vector< double > &sample1, const std::vector< double > &sample2, const std::vector< double > &s1errors=std::vector< double >{}, const std::vector< double > &s2errors=std::vector< double >{}) Calculate the error-weighted chi2 statistic between two samples.
double approx(double a, int n=5) Returns a number floored at the nth decimal place.
double correlation(const std::vector< int > &sample1, const std::vector< int > &sample2) Calculate the correlation strength between two samples.
static const double TWOPI A pre-defined value of .
NUM sqr(NUM a) Named number-type squaring operation.
static const double PI A pre-defined value of .
double stdDev(const double sumWX, const double sumW, const double sumWX2, const double sumW2) Calculate the weighted standard deviation of a sample.
Num add_quad(Num a, Num b) Named number-type addition in quadrature operation.
std::enable_if_t< std::is_arithmetic_v< N1 > &&std::is_arithmetic_v< N2 > &&(std::is_floating_point_v< N1 >||std::is_floating_point_v< N2 >), bool > fuzzyEquals(N1 a, N2 b, double tolerance=1e-5) Compare two numbers for equality with a degree of fuzziness.
std::enable_if_t< std::is_floating_point_v< NUM >, bool > notNaN(NUM val) Check if a number is non-NaN.
double naiveChi2reduced(const std::vector< double > &sample1, const std::vector< double > &sample2, const std::vector< double > &s1errors=std::vector< double >{}, const std::vector< double > &s2errors=std::vector< double >{}) Calculate the error-weighted reduced chi2 statistic between two samples.
int sign(double val) Find the sign of a number.
static std::function< bool(const double, const double)> fuzzyEqComp Comparator wrapper to use with STL algorithms, e.g. std::equal etc.
static const double MAXDOUBLE
std::enable_if_t< std::is_floating_point_v< NUM >, bool > isZero(NUM val, double tolerance=1e-8) Compare a number to zero.
static const double MAXINT
double variance(const double sumWX, const double sumW, const double sumWX2, const double sumW2) Calculate the weighted variance of a sample.
std::vector< double > pdfspace(size_t nbins, double xmin, double xmax, std::function< double(double)> &fn, size_t nsample=10000) Make a list of nbins + 1 values spaced with density ~ f(x) between xmin and end inclusive.
double mean(const std::vector< int > &sample) Calculate the mean of a sample.
double effNumEntries(const double sumW, const double sumW2) Calculate the effective number of entries of a sample.
Sign Enum for signs of numbers.
bool inRange(NUM value, NUM low, NUM high, RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) Determine if value is in the range low to high, for floating point numbers.
|