Project
Loading...
Searching...
No Matches
RobustAverage.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
13#include "Framework/Logger.h"
14#include <numeric>
15
16void o2::tpc::RobustAverage::reserve(const unsigned int maxValues)
17{
18 mValues.reserve(maxValues);
19 mWeights.reserve(maxValues);
20}
21
22std::pair<float, float> o2::tpc::RobustAverage::getFilteredAverage(const float sigma, const float interQuartileRange)
23{
24 if (mValues.empty()) {
25 return std::pair<float, float>(0, 0);
26 }
27
28 /*
29 1. Sort the values
30 2. Use only the values in the Interquartile Range
31 3. Get the median
32 4. Calculate the std dev of the selected values with the median as reference
33 5. Get mean of the selected points which are in the range of the std dev
34 */
35
36 // 1. Sort the values
37 sort();
38
39 // 2. Use only the values in the Interquartile Range (inner n%)
40 const auto upper = mValues.begin() + mValues.size() * interQuartileRange;
41 const auto lower = mValues.begin() + mValues.size() * (1 - interQuartileRange);
42
43 // 3. Get the median
44 const float median = mValues[mValues.size() / 2];
45
46 if (upper == lower) {
47 return std::pair<float, float>(median, 0);
48 }
49
50 // 4. Calculate the std dev of the selected values with the median
51 const float stdev = getStdDev(median, lower, upper);
52
53 // 5. Get mean of the selected points which are in the range of the std dev
54 return std::pair<float, float>(getFilteredMean(median, stdev, sigma), stdev);
55}
56
57std::tuple<float, float, float, unsigned int> o2::tpc::RobustAverage::filterPointsMedian(const float maxAbsMedian, const float sigma)
58{
59 if (mValues.empty()) {
60 return {0, 0, 0, 0};
61 }
62
63 // 1. Sort the values
64 sort();
65
66 // 2. median
67 const float median = mValues[mValues.size() / 2];
68
69 // 3. select points larger and smaller than specified max value
70 const auto upperV0 = std::upper_bound(mValues.begin(), mValues.end(), median + maxAbsMedian);
71 const auto lowerV0 = std::lower_bound(mValues.begin(), mValues.end(), median - maxAbsMedian);
72
73 if (upperV0 == lowerV0) {
74 return {0, 0, 0, 0};
75 }
76
77 // 4. get RMS of selected values
78 const float stdev = getStdDev(median, lowerV0, upperV0);
79
80 // 5. define RMS cut
81 const float sigmastddev = sigma * stdev;
82 const float minVal = median - sigmastddev;
83 const float maxVal = median + sigmastddev;
84 const auto upper = std::upper_bound(mValues.begin(), mValues.end(), maxVal);
85 const auto lower = std::lower_bound(mValues.begin(), mValues.end(), minVal);
86
87 if (upper == lower) {
88 return {0, 0, 0, 0};
89 }
90
91 const int indexUp = upper - mValues.begin();
92 const int indexLow = lower - mValues.begin();
93
94 // 7. get filtered median
95 const float medianFilterd = mValues[(indexUp - indexLow) / 2 + indexLow];
96
97 // 8. weighted mean
98 const auto upperW = mWeights.begin() + indexUp;
99 const auto lowerW = mWeights.begin() + indexLow;
100 const float wMean = mUseWeights ? getWeightedMean(lower, upper, lowerW, upperW) : getMean(lower, upper);
101
102 // 9. number of points passing the cuts
103 const int nEntries = indexUp - indexLow;
104
105 return {medianFilterd, wMean, stdev, nEntries};
106}
107
109{
110 if (mUseWeights) {
111 const size_t nVals = mValues.size();
112 if (mValues.size() != mWeights.size()) {
113 LOGP(warning, "values and errors haave different size");
114 return;
115 }
116 std::vector<std::size_t> tmpIdx(nVals);
117 std::iota(tmpIdx.begin(), tmpIdx.end(), 0);
118 std::sort(tmpIdx.begin(), tmpIdx.end(), [&](std::size_t i, std::size_t j) { return (mValues[i] < mValues[j]); });
119
120 std::vector<float> mValues_tmp;
121 std::vector<float> mWeights_tmp;
122 mValues_tmp.reserve(nVals);
123 mWeights_tmp.reserve(nVals);
124 for (int i = 0; i < nVals; ++i) {
125 const int idx = tmpIdx[i];
126 mValues_tmp.emplace_back(mValues[idx]);
127 mWeights_tmp.emplace_back(mWeights[idx]);
128 }
129 mValues.swap(mValues_tmp);
130 mWeights.swap(mWeights_tmp);
131 } else {
132 std::sort(mValues.begin(), mValues.end());
133 }
134}
135
136float o2::tpc::RobustAverage::getStdDev(const float mean, std::vector<float>::const_iterator begin, std::vector<float>::const_iterator end)
137{
138 const auto size = std::distance(begin, end);
139 if (size == 0) {
140 return 0;
141 }
142 mTmpValues.resize(size);
143 std::transform(begin, end, mTmpValues.begin(), [mean](const float val) { return val - mean; });
144 const float sqsum = std::inner_product(mTmpValues.begin(), mTmpValues.end(), mTmpValues.begin(), decltype(mTmpValues)::value_type(0));
145 const float stdev = std::sqrt(sqsum / size);
146 return stdev;
147}
148
149float o2::tpc::RobustAverage::getFilteredMean(const float mean, const float stdev, const float sigma) const
150{
151 const float sigmastddev = sigma * stdev;
152 const float minVal = mean - sigmastddev;
153 const float maxVal = mean + sigmastddev;
154 const auto upper = std::upper_bound(mValues.begin(), mValues.end(), maxVal);
155 const auto lower = std::lower_bound(mValues.begin(), mValues.end(), minVal);
156 return getMean(lower, upper);
157}
158
160{
161 LOGP(info, "PRINTING STORED VALUES");
162 for (auto val : mValues) {
163 LOGP(info, "{}", val);
164 }
165}
166
167float o2::tpc::RobustAverage::getMean(std::vector<float>::const_iterator begin, std::vector<float>::const_iterator end) const
168{
169 return std::accumulate(begin, end, decltype(mValues)::value_type(0)) / std::distance(begin, end);
170}
171
173{
174 if (mValues.empty()) {
175 return 0;
176 }
177 size_t n = mValues.size() / 2;
178 std::nth_element(mValues.begin(), mValues.begin() + n, mValues.end());
179 return mValues[n];
180}
181
182float o2::tpc::RobustAverage::getWeightedMean(std::vector<float>::const_iterator beginValues, std::vector<float>::const_iterator endValues, std::vector<float>::const_iterator beginWeight, std::vector<float>::const_iterator endWeight) const
183{
184 return std::inner_product(beginValues, endValues, beginWeight, decltype(mValues)::value_type(0)) / std::accumulate(beginWeight, endWeight, decltype(mWeights)::value_type(0));
185}
186
188{
189 mValues.clear();
190 mWeights.clear();
191}
192
193void o2::tpc::RobustAverage::addValue(const float value, const float weight)
194{
195 mValues.emplace_back(value);
196 if (mUseWeights) {
197 mWeights.emplace_back(weight);
198 }
199}
200
201float o2::tpc::RobustAverage::getTrunctedMean(float low, float high)
202{
203 if (low >= high) {
204 LOGP(warning, "low {} should be higher than high {}", low, high);
205 return 0;
206 }
207 const int startInd = static_cast<int>(low * mValues.size());
208 const int endInd = static_cast<int>(high * mValues.size());
209 if ((endInd <= startInd) || (startInd < 0) || (endInd >= mValues.size())) {
210 return 0;
211 }
212
213 if (!mUseWeights) {
214 // fast truncated mean without sorting
215 std::nth_element(mValues.begin(), mValues.begin() + startInd, mValues.end());
216 const float valMin = mValues[startInd];
217
218 std::nth_element(mValues.begin(), mValues.begin() + endInd, mValues.end());
219 const float valMax = mValues[endInd];
220
221 double sum = 0;
222 for (auto val : mValues) {
223 if (val >= valMin && val < valMax) {
224 sum += val;
225 }
226 }
227 const int count = endInd - startInd;
228 const float mean = sum / count;
229 return mean;
230 } else {
231 sort();
232 return getMean(mValues.begin() + startInd, mValues.begin() + endInd);
233 }
234}
235
237{
238 // see: https://numpy.org/doc/stable/reference/generated/numpy.quantile.html
239 if (mValues.empty() || (quantile > 1) || (quantile < 0)) {
240 return -1;
241 }
242 sort();
243 // calculate index
244 const int n = mValues.size();
245 const float vIdx = type ? (quantile * (n + 1 / 3.) - 2 / 3.) : (quantile * (n - 1));
246 const int idxL = vIdx;
247 const float frac = vIdx - idxL;
248 // no interpolation required in case index is matched or left index equals to last entry in values
249 if ((frac == 0) || (idxL >= mValues.size() - 1)) {
250 return mValues[idxL];
251 }
252 // right index
253 const int idxR = idxL + 1;
254
255 // linear interpolation between left and right index
256 const float val = mValues[idxL] + (mValues[idxR] - mValues[idxL]) * frac;
257 return val;
258}
int32_t i
uint32_t j
Definition RawData.h:0
class for performing robust averaging and outlier filtering
void addValue(const float value, const float weight=1)
float getTrunctedMean(float low, float high)
float getQuantile(float quantile, int type)
float getWeightedMean() const
void reserve(const unsigned int maxValues)
void print() const
values which will be averaged and filtered
std::pair< float, float > getFilteredAverage(const float sigma=3, const float interQuartileRange=0.9)
void clear()
clear the stored values
std::tuple< float, float, float, unsigned int > filterPointsMedian(const float maxAbsMedian, const float sigma=5)
remove all the point which are abs(val - val_median)>maxAbsMedian
GLdouble n
Definition glcorearb.h:1982
GLint GLsizei count
Definition glcorearb.h:399
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLuint end
Definition glcorearb.h:469
GLuint GLuint GLfloat weight
Definition glcorearb.h:5477
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
GLuint GLfloat * val
Definition glcorearb.h:1582
Enum< T >::Iterator begin(Enum< T >)
Definition Defs.h:173
DataT sum(const Vector< DataT, N > &a)
Definition Vector.h:107