OpenMS
Loading...
Searching...
No Matches
SignalToNoiseEstimatorMedian.h
Go to the documentation of this file.
1// Copyright (c) 2002-present, OpenMS Inc. -- EKU Tuebingen, ETH Zurich, and FU Berlin
2// SPDX-License-Identifier: BSD-3-Clause
3//
4// --------------------------------------------------------------------------
5// $Maintainer: Chris Bielow $
6// $Authors: $
7// --------------------------------------------------------------------------
8//
9
10#pragma once
11
12
18#include <algorithm> //for std::max_element
19#include <cmath>
20#include <vector>
21
22namespace OpenMS
23{
62 template <typename Container = MSSpectrum>
64 public SignalToNoiseEstimator<Container>
65 {
66
67public:
68
71
73 using SignalToNoiseEstimator<Container>::defaults_;
74 using SignalToNoiseEstimator<Container>::param_;
75
78
80
83 {
84 //set the name for DefaultParamHandler error messages
85 this->setName("SignalToNoiseEstimatorMedian");
86
87 defaults_.setValue("max_intensity", -1, "maximal intensity considered for histogram construction. By default, it will be calculated automatically (see auto_mode)." \
88 " Only provide this parameter if you know what you are doing (and change 'auto_mode' to '-1')!" \
89 " All intensities EQUAL/ABOVE 'max_intensity' will be added to the LAST histogram bin." \
90 " If you choose 'max_intensity' too small, the noise estimate might be too small as well. " \
91 " If chosen too big, the bins become quite large (which you could counter by increasing 'bin_count', which increases runtime)." \
92 " In general, the Median-S/N estimator is more robust to a manual max_intensity than the MeanIterative-S/N.", {"advanced"});
93 defaults_.setMinInt("max_intensity", -1);
94
95 defaults_.setValue("auto_max_stdev_factor", 3.0, "parameter for 'max_intensity' estimation (if 'auto_mode' == 0): mean + 'auto_max_stdev_factor' * stdev", {"advanced"});
96 defaults_.setMinFloat("auto_max_stdev_factor", 0.0);
97 defaults_.setMaxFloat("auto_max_stdev_factor", 999.0);
98
99 defaults_.setValue("auto_max_percentile", 95, "parameter for 'max_intensity' estimation (if 'auto_mode' == 1): auto_max_percentile th percentile", {"advanced"});
100 defaults_.setMinInt("auto_max_percentile", 0);
101 defaults_.setMaxInt("auto_max_percentile", 100);
102
103 defaults_.setValue("auto_mode", 0, "method to use to determine maximal intensity: -1 --> use 'max_intensity'; 0 --> 'auto_max_stdev_factor' method (default); 1 --> 'auto_max_percentile' method", {"advanced"});
104 defaults_.setMinInt("auto_mode", -1);
105 defaults_.setMaxInt("auto_mode", 1);
106
107 defaults_.setValue("win_len", 200.0, "window length in Thomson");
108 defaults_.setMinFloat("win_len", 1.0);
109
110 defaults_.setValue("bin_count", 30, "number of bins for intensity values");
111 defaults_.setMinInt("bin_count", 3);
112
113 defaults_.setValue("min_required_elements", 10, "minimum number of elements required in a window (otherwise it is considered sparse)");
114 defaults_.setMinInt("min_required_elements", 1);
115
116 defaults_.setValue("noise_for_empty_window", std::pow(10.0, 20), "noise value used for sparse windows", {"advanced"});
117
118 defaults_.setValue("write_log_messages", "true", "Write out log messages in case of sparse windows or median in rightmost histogram bin");
119 defaults_.setValidStrings("write_log_messages", {"true","false"});
120
122 }
123
126 SignalToNoiseEstimator<Container>(source)
127 {
129 }
130
136 {
137 if (&source == this) return *this;
138
141 return *this;
142 }
143
145
149
152 {
154 }
155
158 {
160 }
161
164
167 {
168 double max_intensity = 0;
171 };
172
187 static Statistics estimate(const Container& c, const Settings& settings, std::vector<double>& sn,
188 const ProgressLogger* progress = nullptr)
189 {
190 // the ranges of the parameters, which Settings do not go through
191 if (!(settings.win_len >= 1.0) || settings.bin_count < 3 || settings.min_required_elements < 1 ||
192 settings.auto_mode < MANUAL || settings.auto_mode > AUTOMAXBYPERCENT || settings.max_intensity < -1 ||
193 !(settings.auto_max_stdev_factor >= 0.0 && settings.auto_max_stdev_factor <= 999.0))
194 {
195 throw Exception::InvalidParameter(__FILE__, __LINE__, OPENMS_PRETTY_FUNCTION,
196 "SignalToNoiseEstimatorMedian: a setting is outside the range of its parameter (win_len >= 1, "
197 "bin_count >= 3, min_required_elements >= 1, auto_mode in [-1, 1], max_intensity >= -1, "
198 "auto_max_stdev_factor in [0, 999])");
199 }
200
201 Statistics stats;
202 stats.max_intensity = settings.max_intensity;
203
204 //first element in the scan
205 PeakIterator scan_first_ = c.begin();
206 //last element in the scan
207 PeakIterator scan_last_ = c.end();
208
209 // reset the results
210 sn.clear();
211 sn.resize(c.size());
212
213 // maximal range of histogram needs to be calculated first
214 if (settings.auto_mode == AUTOMAXBYSTDEV)
215 {
216 // use MEAN+auto_max_intensity_*STDEV as threshold
217 GaussianEstimate gauss_global = SignalToNoiseEstimator<Container>::estimate_(scan_first_, scan_last_);
218 stats.max_intensity = gauss_global.mean + std::sqrt(gauss_global.variance) * settings.auto_max_stdev_factor;
219 }
220 else if (settings.auto_mode == AUTOMAXBYPERCENT)
221 {
222 // get value at "auto_max_percentile"th percentile
223 // we use a histogram approach here as well.
224 if ((settings.auto_max_percentile < 0) || (settings.auto_max_percentile > 100))
225 {
226 std::string s = StringUtils::toStr(settings.auto_max_percentile);
227 throw Exception::InvalidValue(__FILE__,
228 __LINE__,
229 OPENMS_PRETTY_FUNCTION,
230 "auto_mode is on AUTOMAXBYPERCENT! auto_max_percentile is not in [0,100]. Use setAutoMaxPercentile(<value>) to change it!",
231 s);
232 }
233
234 if (c.empty())
235 {
236 return stats;
237 }
238 std::vector<int> histogram_auto(100, 0);
239
240 // find maximum of current scan
241 auto maxIt = std::max_element(c.begin(), c.end() ,[](const PeakType& a, const PeakType& b){ return a.getIntensity() < b.getIntensity();});
242 typename PeakType::IntensityType maxInt = maxIt->getIntensity();
243
244 double bin_size = maxInt > 0 ? static_cast<double>(maxInt) / 100.0 : 1.0; // no division by zero if no intensity is positive
245
246 // fill histogram
247 for(const auto& peak : c)
248 {
249 ++histogram_auto[(int) std::max(0.0, std::min(peak.getIntensity() / bin_size, 99.0))];
250 }
251
252 // add up element counts in histogram until ?th percentile is reached
253 int elements_below_percentile = std::max(1, (int) (settings.auto_max_percentile * c.size() / 100));
254 int elements_seen = 0;
255 int i = -1;
256
257 while (i < 99 && elements_seen < elements_below_percentile)
258 {
259 ++i;
260 elements_seen += histogram_auto[i];
261 }
262
263 stats.max_intensity = (((double)i) + 0.5) * bin_size;
264 }
265 else //if (settings.auto_mode == MANUAL)
266 {
267 if (stats.max_intensity <= 0)
268 {
269 std::string s = StringUtils::toStr(stats.max_intensity);
270 throw Exception::InvalidValue(__FILE__,
271 __LINE__,
272 OPENMS_PRETTY_FUNCTION,
273 "auto_mode is on MANUAL! max_intensity is <=0. Needs to be positive! Use setMaxIntensity(<value>) or enable auto_mode!",
274 s);
275 }
276 }
277
278 if (stats.max_intensity < 0)
279 {
280 OPENMS_LOG_WARN << "SignalToNoiseEstimatorMedian: the max_intensity value should be positive! " << stats.max_intensity << std::endl;
281 return stats;
282 }
283
284 PeakIterator window_pos_center = scan_first_;
285 PeakIterator window_pos_borderleft = scan_first_;
286 PeakIterator window_pos_borderright = scan_first_;
287
288 double window_half_size = settings.win_len / 2;
289 double bin_size = std::max(1.0, stats.max_intensity / settings.bin_count); // at least size of 1 for intensity bins
290 int bin_count_minus_1 = settings.bin_count - 1;
291
292 std::vector<int> histogram(settings.bin_count, 0);
293 std::vector<double> bin_value(settings.bin_count, 0);
294 // calculate average intensity that is represented by a bin
295 for (int bin = 0; bin < settings.bin_count; bin++)
296 {
297 histogram[bin] = 0;
298 bin_value[bin] = (bin + 0.5) * bin_size;
299 }
300 // bin in which a datapoint would fall
301 int to_bin = 0;
302
303 // index of bin where the median is located
304 int median_bin = 0;
305 // additive number of elements from left to x in histogram
306 int element_inc_count = 0;
307
308 // tracks elements in current window, which may vary because of unevenly spaced data
309 int elements_in_window = 0;
310 // number of windows
311 int window_count = 0;
312
313 // number of elements where we find the median
314 int element_in_window_half = 0;
315
316 double noise; // noise value of a datapoint
317
319 if (progress != nullptr) progress->startProgress(0, c.size(), "noise estimation of data");
320
321 // MAIN LOOP
322 while (window_pos_center != scan_last_)
323 {
324
325 // erase all elements from histogram that will leave the window on the LEFT side
326 while ((*window_pos_borderleft).getPos() < (*window_pos_center).getPos() - window_half_size)
327 {
328 to_bin = (int) std::max(std::min((*window_pos_borderleft).getIntensity() / bin_size, (double) bin_count_minus_1), 0.0); // clamp before converting to int
329 --histogram[to_bin];
330 --elements_in_window;
331 ++window_pos_borderleft;
332 }
333
334 // add all elements to histogram that will enter the window on the RIGHT side
335 while ((window_pos_borderright != scan_last_)
336 && ((*window_pos_borderright).getPos() <= (*window_pos_center).getPos() + window_half_size))
337 {
338 //std::cerr << (*window_pos_borderright).getIntensity() << " " << bin_size << " " << bin_count_minus_1 << std::endl;
339 to_bin = (int) std::max(std::min((*window_pos_borderright).getIntensity() / bin_size, (double) bin_count_minus_1), 0.0); // clamp before converting to int
340 ++histogram[to_bin];
341 ++elements_in_window;
342 ++window_pos_borderright;
343 }
344
345 if (elements_in_window < settings.min_required_elements)
346 {
347 noise = settings.noise_for_empty_window;
348 ++stats.sparse_window_percent;
349 }
350 else
351 {
352 // find bin i where ceil[elements_in_window/2] <= sum_c(0..i){ histogram[c] }
353 median_bin = -1;
354 element_inc_count = 0;
355 element_in_window_half = (elements_in_window + 1) / 2;
356 while (median_bin < bin_count_minus_1 && element_inc_count < element_in_window_half)
357 {
358 ++median_bin;
359 element_inc_count += histogram[median_bin];
360 }
361
362 // increase the error count
363 if (median_bin == bin_count_minus_1) {++stats.histogram_oob_percent; }
364
365 // Interpolate within the median bin instead of just reporting its center.
366 // Using the bin center alone means the noise estimate can only take one of
367 // 'bin_count' discrete values; a tiny, insignificant shift in which side of
368 // a bin boundary the cumulative-median crossing falls on then causes a full
369 // bin-width jump in the reported noise, even though the underlying data barely
370 // changed. Assuming points are uniformly distributed within the bin, we can
371 // instead estimate where inside the bin the crossing actually occurs.
372 const int elements_in_median_bin = histogram[median_bin];
373 double noise_estimate;
374 if (elements_in_median_bin > 0)
375 {
376 const int elements_before_median_bin = element_inc_count - elements_in_median_bin;
377 const double median_bin_lower_edge = median_bin * bin_size;
378 noise_estimate = median_bin_lower_edge
379 + ((double)(element_in_window_half - elements_before_median_bin) / elements_in_median_bin) * bin_size;
380 }
381 else // only possible if the rightmost bin was hit while empty (already flagged above)
382 {
383 noise_estimate = bin_value[median_bin];
384 }
385
386 // just avoid division by 0
387 noise = std::max(1.0, noise_estimate);
388 }
389
390 // store result
391 sn[window_count] = (*window_pos_center).getIntensity() / noise;
392
393
394 // advance the window center by one datapoint
395 ++window_pos_center;
396 ++window_count;
397 // update progress
398 if (progress != nullptr) progress->setProgress(window_count);
399
400 } // end while
401
402 if (progress != nullptr) progress->endProgress();
403
404 stats.sparse_window_percent = stats.sparse_window_percent * 100 / window_count;
405 stats.histogram_oob_percent = stats.histogram_oob_percent * 100 / window_count;
406
407 // warn if percentage of sparse windows is above 20%
408 if (stats.sparse_window_percent > 20 && settings.write_log_messages)
409 {
410 OPENMS_LOG_WARN << "WARNING in SignalToNoiseEstimatorMedian: "
411 << stats.sparse_window_percent
412 << "% of all windows were sparse. You should consider increasing 'win_len' or decreasing 'min_required_elements'"
413 << std::endl;
414 }
415
416 // warn if percentage of possibly wrong median estimates is above 1%
417 if (stats.histogram_oob_percent > 1 && settings.write_log_messages)
418 {
419 OPENMS_LOG_WARN << "WARNING in SignalToNoiseEstimatorMedian: "
420 << stats.histogram_oob_percent
421 << "% of all Signal-to-Noise estimates are too high, because the median was found in the rightmost histogram-bin. "
422 << "You should consider increasing 'max_intensity' (and maybe 'bin_count' with it, to keep bin width reasonable)"
423 << std::endl;
424 }
425
426 return stats;
427 }
428
429protected:
430
436 void computeSTN_(const Container& c) override
437 {
438 Settings settings;
439 settings.max_intensity = max_intensity_;
442 settings.auto_mode = auto_mode_;
443 settings.win_len = win_len_;
444 settings.bin_count = bin_count_;
448 // reset the counters, also for an estimation that throws
451 const Statistics stats = estimate(c, settings, stn_estimates_, this);
455 }
456
458 void updateMembers_() override
459 {
460 max_intensity_ = (double)param_.getValue("max_intensity");
461 auto_max_stdev_Factor_ = (double)param_.getValue("auto_max_stdev_factor");
462 auto_max_percentile_ = param_.getValue("auto_max_percentile");
463 auto_mode_ = param_.getValue("auto_mode");
464 win_len_ = (double)param_.getValue("win_len");
465 bin_count_ = param_.getValue("bin_count");
466 min_required_elements_ = param_.getValue("min_required_elements");
467 noise_for_empty_window_ = (double)param_.getValue("noise_for_empty_window");
468 write_log_messages_ = (bool)param_.getValue("write_log_messages").toBool();
469 stn_estimates_.clear();
470 }
471
481 double win_len_;
489
490 // whether to write out log messages in the case of failure
492
493 // counter for sparse windows
495 // counter for histogram overflow
497
498
499 };
500
501} // namespace OpenMS
502
#define OPENMS_LOG_WARN
Macro for warnings.
Definition LogStream.h:691
void defaultsToParam_()
Updates the parameters after the defaults have been set in the constructor.
Param param_
Container for current parameters.
Definition DefaultParamHandler.h:139
Param defaults_
Container for default parameters. This member should be filled in the constructor of derived classes!
Definition DefaultParamHandler.h:146
void setName(const std::string &name)
Mutable access to the name.
Exception indicating that an invalid parameter was handed over to an algorithm.
Definition Exception.h:317
Invalid value exception.
Definition Exception.h:306
bool toBool() const
Conversion to bool.
const ParamValue & getValue(const std::string &key) const
Returns a value of a parameter.
void setValidStrings(const std::string &key, const std::vector< std::string > &strings)
Sets the valid strings for the parameter key.
void setMaxFloat(const std::string &key, double max)
Sets the maximum value for the floating point or floating point list parameter key.
void setMaxInt(const std::string &key, int max)
Sets the maximum value for the integer or integer list parameter key.
void setMinInt(const std::string &key, int min)
Sets the minimum value for the integer or integer list parameter key.
void setValue(const std::string &key, const ParamValue &value, const std::string &description="", const std::vector< std::string > &tags=std::vector< std::string >())
Sets a value.
void setMinFloat(const std::string &key, double min)
Sets the minimum value for the floating point or floating point list parameter key.
float IntensityType
Intensity type.
Definition Peak2D.h:37
Base class for all classes that want to report their progress.
Definition ProgressLogger.h:28
Estimates the signal/noise (S/N) ratio of each data point in a scan by using the median (histogram ba...
Definition SignalToNoiseEstimatorMedian.h:65
SignalToNoiseEstimator< Container >::PeakIterator PeakIterator
Definition SignalToNoiseEstimatorMedian.h:76
double win_len_
range of data points which belong to a window in Thomson
Definition SignalToNoiseEstimatorMedian.h:481
double histogram_oob_percent
percentage of the medians found in the rightmost bin
Definition SignalToNoiseEstimatorMedian.h:170
SignalToNoiseEstimatorMedian & operator=(const SignalToNoiseEstimatorMedian &source)
Definition SignalToNoiseEstimatorMedian.h:135
static Statistics estimate(const Container &c, const Settings &settings, std::vector< double > &sn, const ProgressLogger *progress=nullptr)
Calculates the signal-to-noise values of all data points of c with a sliding window,...
Definition SignalToNoiseEstimatorMedian.h:187
double sparse_window_percent
percentage of the windows that were sparse
Definition SignalToNoiseEstimatorMedian.h:169
double noise_for_empty_window_
Definition SignalToNoiseEstimatorMedian.h:488
~SignalToNoiseEstimatorMedian() override
Destructor.
Definition SignalToNoiseEstimatorMedian.h:147
SignalToNoiseEstimatorMedian()
default constructor
Definition SignalToNoiseEstimatorMedian.h:82
double max_intensity_
maximal intensity considered during binning (values above get discarded)
Definition SignalToNoiseEstimatorMedian.h:473
double auto_max_percentile_
parameter for initial automatic estimation of "max_intensity_" percentile or a stdev
Definition SignalToNoiseEstimatorMedian.h:477
double histogram_oob_percent_
Definition SignalToNoiseEstimatorMedian.h:496
double max_intensity
maximal intensity of the histogram (estimated in the auto modes)
Definition SignalToNoiseEstimatorMedian.h:168
void computeSTN_(const Container &c) override
Definition SignalToNoiseEstimatorMedian.h:436
bool write_log_messages_
Definition SignalToNoiseEstimatorMedian.h:491
void updateMembers_() override
overridden function from DefaultParamHandler to keep members up to date, when a parameter is changed
Definition SignalToNoiseEstimatorMedian.h:458
int min_required_elements_
minimal number of elements a window needs to cover to be used
Definition SignalToNoiseEstimatorMedian.h:485
SignalToNoiseEstimatorMedian(const SignalToNoiseEstimatorMedian &source)
Copy Constructor.
Definition SignalToNoiseEstimatorMedian.h:125
double getSparseWindowPercent() const
Returns how many percent of the windows were sparse.
Definition SignalToNoiseEstimatorMedian.h:151
double sparse_window_percent_
Definition SignalToNoiseEstimatorMedian.h:494
double getHistogramRightmostPercent() const
Returns the percentage where the median was found in the rightmost bin.
Definition SignalToNoiseEstimatorMedian.h:157
SignalToNoiseEstimator< Container >::PeakType PeakType
Definition SignalToNoiseEstimatorMedian.h:77
SignalToNoiseEstimator< Container >::GaussianEstimate GaussianEstimate
Definition SignalToNoiseEstimatorMedian.h:79
int auto_mode_
determines which method shall be used for estimating "max_intensity_". valid are MANUAL=-1,...
Definition SignalToNoiseEstimatorMedian.h:479
IntensityThresholdCalculation
method to use for estimating the maximal intensity that is used for histogram calculation
Definition SignalToNoiseEstimatorMedian.h:70
@ MANUAL
Definition SignalToNoiseEstimatorMedian.h:70
@ AUTOMAXBYSTDEV
Definition SignalToNoiseEstimatorMedian.h:70
@ AUTOMAXBYPERCENT
Definition SignalToNoiseEstimatorMedian.h:70
int bin_count_
number of bins in the histogram
Definition SignalToNoiseEstimatorMedian.h:483
double auto_max_stdev_Factor_
parameter for initial automatic estimation of "max_intensity_": a stdev multiplier
Definition SignalToNoiseEstimatorMedian.h:475
What an estimation determined besides the S/N values.
Definition SignalToNoiseEstimatorMedian.h:167
This class represents the abstract base class of a signal to noise estimator.
Definition SignalToNoiseEstimator.h:33
double variance
variance of estimated Gaussian
Definition SignalToNoiseEstimator.h:108
PeakIterator::value_type PeakType
Definition SignalToNoiseEstimator.h:40
SignalToNoiseEstimator & operator=(const SignalToNoiseEstimator &source)
Assignment operator.
Definition SignalToNoiseEstimator.h:60
static GaussianEstimate estimate_(const PeakIterator &scan_first_, const PeakIterator &scan_last_)
calculate mean & stdev of intensities of a spectrum
Definition SignalToNoiseEstimator.h:113
double mean
mean of estimated Gaussian
Definition SignalToNoiseEstimator.h:107
std::vector< double > stn_estimates_
stores the noise estimate for each peak
Definition SignalToNoiseEstimator.h:146
Container::const_iterator PeakIterator
Definition SignalToNoiseEstimator.h:39
protected struct to store parameters my, sigma for a Gaussian distribution
Definition SignalToNoiseEstimator.h:106
std::string toStr(int i)
Definition StringUtils.h:101
Main OpenMS namespace.
Definition openswathalgo/include/OpenMS/OPENSWATHALGO/DATAACCESS/ISpectrumAccess.h:19
The parameters of SignalToNoiseEstimatorMedian as plain values; the defaults are those of its paramet...
Definition SignalToNoiseEstimatorMedianSettings.h:26
double auto_max_stdev_factor
Definition SignalToNoiseEstimatorMedianSettings.h:28
double auto_max_percentile
Definition SignalToNoiseEstimatorMedianSettings.h:29
int bin_count
Definition SignalToNoiseEstimatorMedianSettings.h:32
int auto_mode
-1: 'max_intensity', 0: mean + 'auto_max_stdev_factor' * stdev, 1: 'auto_max_percentile'
Definition SignalToNoiseEstimatorMedianSettings.h:30
double win_len
Definition SignalToNoiseEstimatorMedianSettings.h:31
int min_required_elements
Definition SignalToNoiseEstimatorMedianSettings.h:33
double max_intensity
Definition SignalToNoiseEstimatorMedianSettings.h:27
bool write_log_messages
Definition SignalToNoiseEstimatorMedianSettings.h:35
double noise_for_empty_window
Definition SignalToNoiseEstimatorMedianSettings.h:34