13#include <boost/algorithm/string.hpp>
24 virtual bool operator()(
double,
double,
double)
const = 0;
25 virtual ~EdgeCheck() =
default;
29struct StandardEdgeCheck : EdgeCheck {
30 StandardEdgeCheck(
double lastBinCoef) : m_lastBinCoef(lastBinCoef) {}
31 bool operator()(
double xcurr,
double xs,
double rightEdge)
const override {
32 return xcurr + (1. + m_lastBinCoef) * xs <= rightEdge;
39struct ReverseLogEdgeCheck : EdgeCheck {
40 bool operator()(
double xcurr,
double xs,
double leftEdge)
const override {
return xcurr + 2. * xs >= leftEdge; }
47 virtual double operator()(
double,
double)
const = 0;
48 virtual ~BinWidth() =
default;
52struct LinearBinWidth : BinWidth {
53 double operator()([[maybe_unused]]
double xcurr,
double alpha)
const override {
return alpha; }
56struct LogBinWidth : BinWidth {
57 double operator()(
double xcurr,
double alpha)
const override {
return xcurr * alpha; }
63struct ReverseLogBinWidth : BinWidth {
64 ReverseLogBinWidth(
double leftEdge,
double rightEdge) : m_leftEdge(leftEdge), m_rightEdge(rightEdge) {}
65 double operator()(
double xcurr,
double alpha)
const override {
return (xcurr - m_rightEdge - m_leftEdge) * alpha; }
72struct PowerBinWidth : BinWidth {
73 PowerBinWidth(std::size_t &currDiv,
double power) : m_currDiv(&currDiv), m_power(power) {}
74 double operator()([[maybe_unused]]
double xcurr,
double alpha)
const override {
75 return alpha * std::pow((*m_currDiv)++, -m_power);
79 std::size_t *
const m_currDiv;
88 size_t numParams = params.size();
89 if (numParams < 3 || numParams % 2 != 1) {
90 throw std::runtime_error(
"validateRebinParameters: Invalid number of rebin parameters (" +
91 std::to_string(numParams) +
")! Must be odd number of parameters, 3 or more.");
93 double previousEdge = params[0];
94 for (
size_t i = 1; i < numParams - 1; i += 2) {
95 double const binWidth = params[i];
96 double const nextEdge = params[i + 1];
97 if (previousEdge >= nextEdge) {
98 throw std::runtime_error(
99 "validateRebinParameters: Bin boundary values must be given in order of increasing value! " +
101 }
else if (binWidth == 0) {
102 throw std::runtime_error(
"validateRebinParameters: Bin width cannot be zero! Found in range " +
104 }
else if (binWidth < 0 && previousEdge <= 0) {
105 throw std::runtime_error(
"Bin boundaries must be positive for logarithmic binning. Got " +
107 }
else if (binWidth < 0 && isPower) {
108 throw std::runtime_error(
109 "validateRebinParameters: Cannot use power binning with negative bin width! Bin width was " +
112 previousEdge = nextEdge;
121 bool const isPower = power > 0 && power <= 1;
124 double previousEdge = params[0];
125 for (
size_t i = 1; i < params.size() - 1; i += 2) {
126 double const binWidth = params[i];
127 double const nextEdge = params[i + 1];
131 numBins += std::ceil(std::log(nextEdge / previousEdge) / std::log(1. - binWidth));
132 }
else if (isPower) {
136 double constexpr eulerMascheroni = 0.5772156649;
137 numBins += std::exp((nextEdge - previousEdge) / binWidth - eulerMascheroni);
139 numBins += std::pow(((nextEdge - previousEdge) / binWidth) * (1 - power) + 1, 1 / (1 - power));
141 }
else if (binWidth > 0) {
143 numBins += std::ceil((nextEdge - previousEdge) / binWidth);
145 previousEdge = nextEdge;
147 if (numBins >=
static_cast<double>(std::numeric_limits<std::size_t>::max())) {
148 throw std::runtime_error(
149 "estimateNumberOfBins: The number of bins requested exceeds the maximum storable by size_t.");
151 return static_cast<size_t>(numBins);
169 const bool resize_xnew,
const bool full_bins_only,
const double xMinHint,
170 const double xMaxHint,
const bool useReverseLogarithmic,
const double power) {
173 std::vector<double>
tmp;
174 if (params.size() == 1) {
175 if (std::isnan(xMinHint) || std::isnan(xMaxHint)) {
176 throw std::runtime_error(
177 "createAxisFromRebinParams: xMinHint and xMaxHint must be supplied if params contains only the bin width.");
179 tmp = {xMinHint, params.front(), xMaxHint};
181 std::vector<double>
const &fullParams = params.size() == 1 ?
tmp : params;
186 double const lastBinCoef = full_bins_only ? 1.0 : 0.25;
188 bool isPower = power > 0 && power <= 1;
191 double xcurr = fullParams[0];
200 if (neededSize >= xnew.max_size()) {
201 throw std::runtime_error(
202 "createAxisFromRebinParams: The number of bins requested exceeds the maximum storable by the output vector.");
204 xnew.reserve(neededSize);
205 xnew.push_back(xcurr);
211 std::size_t currDiv = 1;
214 std::size_t inew = 1;
215 double leftEdge = fullParams[0];
216 for (std::size_t i = 1; i <= fullParams.size() - 1; i += 2) {
217 double binParam = fullParams[i];
218 double rightEdge = fullParams[i + 1];
219 double alpha = std::fabs(binParam);
222 std::unique_ptr<BinWidth> binWidth(
nullptr);
223 std::unique_ptr<EdgeCheck> edgeCheck(
nullptr);
224 if (binParam < 0.0 && useReverseLogarithmic) {
226 binWidth = std::make_unique<ReverseLogBinWidth>(leftEdge, rightEdge);
227 edgeCheck = std::make_unique<ReverseLogEdgeCheck>();
231 std::swap(leftEdge, rightEdge);
232 }
else if (binParam < 0.0) {
234 binWidth = std::make_unique<LogBinWidth>();
235 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
236 }
else if (isPower) {
238 binWidth = std::make_unique<PowerBinWidth>(currDiv, power);
239 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
242 binWidth = std::make_unique<LinearBinWidth>();
243 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
246 double xs = (*binWidth)(xcurr, alpha);
247 while ((*edgeCheck)(xcurr, xs, rightEdge) || !std::isfinite(xs)) {
250 throw std::runtime_error(
"Invalid binning step provided! Can't create binning axis.");
251 }
else if (!std::isfinite(xs)) {
252 throw std::runtime_error(
"An infinite or NaN value was found in the binning parameters.");
256 xnew.push_back(xcurr);
260 xs = (*binWidth)(xcurr, alpha);
264 if (!useReverseLogarithmic && full_bins_only) {
270 xcurr = fullParams[i + 1];
273 xnew.push_back(xcurr);
280 if (useReverseLogarithmic) {
281 std::sort(xnew.begin(), xnew.end());
305void rebin(
const std::vector<double> &xold,
const std::vector<double> &yold,
const std::vector<double> &eold,
306 const std::vector<double> &xnew, std::vector<double> &ynew, std::vector<double> &enew,
bool distribution,
309 const size_t size_xold = xold.size();
310 if (size_xold != (yold.size() + 1) || size_xold != (eold.size() + 1))
311 throw std::runtime_error(
"rebin: old y and error vectors should be of same "
312 "size & 1 shorter than x");
313 const size_t size_xnew = xnew.size();
314 if (size_xnew != (ynew.size() + 1) || size_xnew != (enew.size() + 1))
315 throw std::runtime_error(
"rebin: new y and error vectors should be of same "
316 "size & 1 shorter than x");
318 size_t size_yold = yold.size();
319 size_t size_ynew = ynew.size();
323 std::fill(ynew.begin(), ynew.end(), 0.0);
324 std::fill(enew.begin(), enew.end(), 0.0);
327 size_t iold = 0, inew = 0;
330 while ((inew < size_ynew) && (iold < size_yold)) {
331 double xo_low = xold[iold];
332 double xo_high = xold[iold + 1];
333 double xn_low = xnew[inew];
334 double xn_high = xnew[inew + 1];
335 if (xn_high <= xo_low)
337 else if (xo_high <= xn_low)
342 double delta = xo_high < xn_high ? xo_high : xn_high;
343 delta -= xo_low > xn_low ? xo_low : xn_low;
344 width = xo_high - xo_low;
345 if ((
delta <= 0.0) || (width <= 0.0)) {
357 ynew[inew] += yold[iold] *
delta;
359 enew[inew] += eold[iold] * eold[iold] *
delta * width;
365 ynew[inew] += yold[iold] *
delta / width;
368 enew[inew] += eold[iold] * eold[iold] *
delta / width;
370 if (xn_high > xo_high) {
385 for (
size_t i = 0; i < size_ynew; ++i) {
387 width = xnew[i + 1] - xnew[i];
390 enew[i] = sqrt(enew[i]) / width;
392 throw std::invalid_argument(
"rebin: Invalid output X array, contains consecutive X values");
398 using pf = double (*)(double);
400 std::transform(enew.begin(), enew.end(), enew.begin(), uf);
422void rebinHistogram(
const std::vector<double> &xold,
const std::vector<double> &yold,
const std::vector<double> &eold,
423 const std::vector<double> &xnew, std::vector<double> &ynew, std::vector<double> &enew,
426 const size_t size_yold = yold.size();
427 if (xold.size() != (size_yold + 1) || size_yold != eold.size())
428 throw std::runtime_error(
"rebin: y and error vectors should be of same size & 1 shorter than x");
429 const size_t size_ynew = ynew.size();
430 if (xnew.size() != (size_ynew + 1) || size_ynew != enew.size())
431 throw std::runtime_error(
"rebin: y and error vectors should be of same size & 1 shorter than x");
435 ynew.assign(size_ynew, 0.0);
436 enew.assign(size_ynew, 0.0);
440 size_t iold = 0, inew = 0;
442 if (xnew.front() > xold.front()) {
443 auto it = std::upper_bound(xold.cbegin(), xold.cend(), xnew.front());
444 if (it == xold.end())
447 iold = std::distance(xold.begin(), it) - 1;
449 auto it = std::upper_bound(xnew.cbegin(), xnew.cend(), xold.front());
450 if (it == xnew.cend())
453 inew = std::distance(xnew.cbegin(), it) - 1;
457 double oneOverWidth, overlap;
461 for (; iold < size_yold; ++iold) {
462 double xold_of_iold_p_1 = xold[iold + 1];
464 if (xold_of_iold_p_1 <= xnew[inew + 1]) {
465 ynew[inew] += yold[iold];
467 enew[inew] += temp * temp;
469 if (xold_of_iold_p_1 == xnew[inew + 1])
472 double xold_of_iold = xold[iold];
474 oneOverWidth = 1. / (xold_of_iold_p_1 - xold_of_iold);
475 frac = yold[iold] * oneOverWidth;
477 fracE = temp * temp * oneOverWidth;
480 while (inew < size_ynew && xnew[inew + 1] <= xold_of_iold_p_1) {
481 overlap = xnew[inew + 1] - std::max(xnew[inew], xold_of_iold);
482 ynew[inew] += frac * overlap;
483 enew[inew] += fracE * overlap;
488 if (inew == size_ynew)
492 overlap = xold_of_iold_p_1 - xnew[inew];
493 ynew[inew] += frac * overlap;
494 enew[inew] += fracE * overlap;
502 using pf = double (*)(double);
504 std::transform(enew.begin(), enew.end(), enew.begin(), uf);
515 const std::vector<double>::size_type npoints = bin_edges.size();
516 if (bin_centres.size() != npoints) {
517 bin_centres.resize(npoints);
523 std::adjacent_difference(bin_edges.begin(), bin_edges.end(), bin_centres.begin(),
SimpleAverage<double>());
527 bin_centres.erase(bin_centres.begin());
547 const auto n = bin_centers.size();
555 bin_edges.resize(
n + 1);
560 bin_edges[0] = bin_centers[0] - 0.5;
561 bin_edges[1] = bin_centers[0] + 0.5;
565 for (
size_t i = 0; i <
n - 1; ++i) {
566 bin_edges[i + 1] = 0.5 * (bin_centers[i] + bin_centers[i + 1]);
569 bin_edges[0] = bin_centers[0] - (bin_edges[1] - bin_centers[0]);
571 bin_edges[
n] = bin_centers[
n - 1] + (bin_centers[
n - 1] - bin_edges[
n - 1]);
587 return static_cast<size_t>(
index);
589 throw std::out_of_range(
"indexOfValue - value out of range");
593 if (bin_centers.empty()) {
594 throw std::out_of_range(
"indexOfValue - vector is empty");
596 if (bin_centers.size() == 1) {
598 if (
value < bin_centers[0] - 0.5 ||
value > bin_centers[0] + 0.5) {
604 const size_t n = bin_centers.size();
605 const double firstBinLowEdge = bin_centers[0] - 0.5 * (bin_centers[1] - bin_centers[0]);
606 const double lastBinHighEdge = bin_centers[
n - 1] + 0.5 * (bin_centers[
n - 1] - bin_centers[
n - 2]);
607 if (value < firstBinLowEdge || value > lastBinHighEdge) {
610 const auto it = std::lower_bound(bin_centers.begin(), bin_centers.end(),
value);
611 if (it == bin_centers.end()) {
612 return static_cast<int>(
n - 1);
614 size_t binIndex = std::distance(bin_centers.begin(), it);
616 value < bin_centers[binIndex - 1] + 0.5 * (bin_centers[binIndex] - bin_centers[binIndex - 1])) {
619 return static_cast<int>(binIndex);
633 if (bin_edges.empty()) {
634 throw std::out_of_range(
"indexOfValue - vector is empty");
636 if (bin_edges.size() == 1) {
637 throw std::out_of_range(
"indexOfValue - requires at least two bin edges");
639 if (
value < bin_edges.front()) {
640 throw std::out_of_range(
"indexOfValue - value out of range");
642 const auto it = std::lower_bound(bin_edges.begin(), bin_edges.end(),
value);
643 if (it == bin_edges.end()) {
644 throw std::out_of_range(
"indexOfValue - value out of range");
647 size_t edgeIndex = std::distance(bin_edges.begin(), it);
662 auto i = arra.cbegin();
664 if (i == arra.cend()) {
671 for (; val != val;) {
673 if (i == arra.cend()) {
680 for (; i != arra.cend(); ++i) {
698template <
typename NumT>
701 std::vector<NumT> values;
702 std::vector<std::string> strs;
704 boost::split(strs, listString, boost::is_any_of(separators));
705 for (
auto &str : strs) {
708 std::stringstream oneNumber(str);
711 values.emplace_back(num);
727 assert(bins.size() >= 2);
729 assert(bins.size() <
static_cast<size_t>(std::numeric_limits<int>::max()));
731 if (
value < bins.front())
740 auto it = std::upper_bound(bins.begin(), bins.end() - 1,
value) - 1;
741 assert(it >= bins.begin());
743 return static_cast<int>(it - bins.begin());
761double runAverage(
size_t index,
size_t startIndex,
size_t endIndex,
const double halfWidth,
762 const std::vector<double> &input, std::vector<double>
const *
const binBndrs) {
765 double weight0(0), weight1(0), start(0.0), end(0.0);
773 auto &rBndrs = *binBndrs;
776 double binC = 0.5 * (rBndrs[
index + 1] + rBndrs[
index]);
777 start = binC - halfWidth;
778 end = binC + halfWidth;
779 if (start <= rBndrs[startIndex]) {
787 if (end >= rBndrs[endIndex]) {
789 end = rBndrs[endIndex];
792 weight1 = (end - rBndrs[
iEnd]) / (rBndrs[
iEnd + 1] - rBndrs[
iEnd]);
796 weight0 = (end - start) / (rBndrs[
iStart] - rBndrs[
iStart - 1]);
799 auto iHalfWidth =
static_cast<size_t>(halfWidth);
801 if (startIndex + iHalfWidth >
index)
816 avrg += input[
iStart - 1] * weight0;
817 if (
iEnd != endIndex)
818 avrg += input[
iEnd] * weight1;
820 double div = end - start;
824 return avrg / (end - start);
829 return avrg / double(ic);
859void smoothInRange(
const std::vector<double> &input, std::vector<double> &output,
const double avrgInterval,
860 std::vector<double>
const *
const binBndrs,
size_t startIndex,
size_t endIndex,
861 std::vector<double> *
const outBins) {
864 endIndex = input.size();
865 if (endIndex > input.size())
866 endIndex = input.size();
868 if (endIndex <= startIndex) {
873 size_t max_size = input.size();
875 if (binBndrs->size() != max_size + 1) {
876 throw std::invalid_argument(
"Array of bin boundaries, "
877 "if present, have to be one bigger then the input array");
881 size_t length = endIndex - startIndex;
882 output.resize(length);
884 double halfWidth = avrgInterval / 2;
886 if (std::floor(halfWidth) * 2 - avrgInterval > 1.e-6) {
887 halfWidth = std::floor(halfWidth) + 1;
892 outBins->resize(length + 1);
896 for (
size_t i = startIndex; i < endIndex; i++) {
898 binSize = binBndrs->operator[](i + 1) - binBndrs->operator[](i);
900 output[i - startIndex] = runAverage(i, startIndex, endIndex, halfWidth, input, binBndrs) * binSize;
901 if (outBins && binBndrs) {
902 outBins->operator[](i - startIndex) = binBndrs->operator[](i);
905 if (outBins && binBndrs) {
906 outBins->operator[](endIndex - startIndex) = binBndrs->operator[](endIndex);
912 const std::string &separator);
914 const std::string &separator);
916 const std::string &separator);
918 const std::string &separator);
920 const std::string &separator);
922 const std::string &separator);
double value
The value of the point.
std::map< DeltaEMode::Type, std::string > index
#define DLLExport
Definitions of the DLLImport compiler directives for MSVC.
MANTID_KERNEL_DLL void smoothInRange(const std::vector< double > &input, std::vector< double > &output, double avrgInterval, std::vector< double > const *const binBndrs=nullptr, size_t startIndex=0, size_t endIndex=0, std::vector< double > *const outBins=nullptr)
Basic running average of input vector within specified range, considering variable bin-boundaries if ...
template DLLExport std::vector< int64_t > splitStringIntoVector< int64_t >(std::string listString, const std::string &separator)
size_t MANTID_KERNEL_DLL indexOfValueFromCenters(const std::vector< double > &bin_centers, const double value)
Gets the bin of a value from a vector of bin centers and throws exception if out of range.
void MANTID_KERNEL_DLL convertToBinCentre(const std::vector< double > &bin_edges, std::vector< double > &bin_centres)
Convert an array of bin boundaries to bin center values.
template DLLExport std::vector< std::string > splitStringIntoVector< std::string >(std::string listString, const std::string &separator)
template DLLExport std::vector< size_t > splitStringIntoVector< size_t >(std::string listString, const std::string &separator)
std::size_t MANTID_KERNEL_DLL estimateNumberOfBins(std::vector< double > const ¶ms, double const power=-1)
Returns a size_t with the estimated number of bins that would be needed for a rebinning operation.
bool MANTID_KERNEL_DLL isConstantValue(const std::vector< double > &arra)
Assess if all the values in the vector are equal or if there are some different values.
template DLLExport std::vector< float > splitStringIntoVector< float >(std::string listString, const std::string &separator)
MANTID_KERNEL_DLL int getBinIndex(const std::vector< double > &bins, const double value)
Return the index into a vector of bin boundaries for a particular X value.
template DLLExport std::vector< double > splitStringIntoVector< double >(std::string listString, const std::string &separator)
size_t MANTID_KERNEL_DLL indexOfValueFromEdges(const std::vector< double > &bin_edges, const double value)
Gets the bin of a value from a vector of bin edges.
template DLLExport std::vector< int32_t > splitStringIntoVector< int32_t >(std::string listString, const std::string &separator)
Declare all version of this.
void MANTID_KERNEL_DLL rebin(const std::vector< double > &xold, const std::vector< double > &yold, const std::vector< double > &eold, const std::vector< double > &xnew, std::vector< double > &ynew, std::vector< double > &enew, bool distribution, bool addition=false)
Rebins data according to a new output X array.
void MANTID_KERNEL_DLL rebinHistogram(const std::vector< double > &xold, const std::vector< double > &yold, const std::vector< double > &eold, const std::vector< double > &xnew, std::vector< double > &ynew, std::vector< double > &enew, bool addition)
Rebins histogram data according to a new output X array.
void MANTID_KERNEL_DLL convertToBinBoundary(const std::vector< double > &bin_centers, std::vector< double > &bin_edges)
Convert an array of bin centers to bin boundary values.
MANTID_KERNEL_DLL std::vector< NumT > splitStringIntoVector(std::string listString, const std::string &separators=", ")
Take a string of comma or space-separated values, and splits it into a vector of doubles.
int MANTID_KERNEL_DLL indexOfValueFromCentersNoThrow(const std::vector< double > &bin_centers, const double value)
Gets the bin of a value from a vector of bin centers and returns -1 if out of range.
void MANTID_KERNEL_DLL validateRebinParameters(std::vector< double > const &, bool const =false)
Validate rebinning parameters, throwing an error if any assumptions are invalidated.
std::size_t MANTID_KERNEL_DLL createAxisFromRebinParams(const std::vector< double > ¶ms, std::vector< double > &xnew, const bool resize_xnew=true, const bool full_bins_only=false, const double xMinHint=std::nan(""), const double xMaxHint=std::nan(""), const bool useReverseLogarithmic=false, const double power=-1)
Creates a new output X array given a 'standard' set of rebinning parameters.
std::string to_string(const wide_integer< Bits, Signed > &n)
A binary functor to compute the simple average of 2 numbers.