7 , m_adapt_rate(adapt_rate)
8 , m_window(window == 0 ? 1 : window)
59 return (std::abs(step) > 0.0) ? 1.0 : 0.0;
61 const double ratio = std::abs(step) / f;
62 return std::clamp(ratio / 3.0, 0.0, 1.0);
71 const double mean = std::accumulate(view.begin(), view.end(), 0.0) /
static_cast<double>(view.size());
108 std::vector<double> sorted(view.begin(), view.end());
109 std::ranges::sort(sorted);
110 const double median = sorted[sorted.size() / 2];
130 std::vector<double> chronological(view.rbegin(), view.rend());
131 std::span<const double> ordered(chronological);
133 const double mean = std::accumulate(chronological.begin(), chronological.end(), 0.0) /
static_cast<double>(chronological.size());
134 const double window_stddev =
stddev(view);
137 const bool looks_quiet = trend_ratio < 0.5;
165 std::vector<double> chronological(view.rbegin(), view.rend());
166 std::span<const double> ordered(chronological);
172 const double mean = std::accumulate(chronological.begin(), chronological.end(), 0.0) /
static_cast<double>(chronological.size());
175 const auto n =
static_cast<double>(chronological.size());
177 for (
size_t i = 0; i < chronological.size(); ++i)
178 sum_x +=
static_cast<double>(i);
179 const double mean_x = sum_x / n;
180 const double intercept =
mean - slope * mean_x;
181 const auto newest_x =
static_cast<double>(chronological.size() - 1);
184 const double first = chronological.front();
185 const double last = chronological.back();
186 const auto span_n =
static_cast<double>(chronological.size() - 1);
188 double residual_sq_sum = 0.0;
189 for (
size_t i = 0; i < chronological.size(); ++i) {
190 const double t =
static_cast<double>(i) / span_n;
191 const double trend_value = first + t * (last - first);
192 const double residual = chronological[i] - trend_value;
193 residual_sq_sum += residual * residual;
196 const double residual_var = (chronological.size() > 2)
197 ? residual_sq_sum /
static_cast<double>(chronological.size() - 2)
212 if (samples.size() < 2)
215 const double mean = std::accumulate(samples.begin(), samples.end(), 0.0) /
static_cast<double>(samples.size());
218 for (
double v : samples)
221 return sq_sum /
static_cast<double>(samples.size() - 1);
226 return std::sqrt(variance(samples));
234 std::vector<double> sorted(samples.begin(), samples.end());
235 std::ranges::sort(sorted);
236 const double median = sorted[sorted.size() / 2];
238 std::vector<double> deviations;
239 deviations.reserve(sorted.size());
240 for (
double v : sorted)
241 deviations.push_back(std::abs(v - median));
242 std::ranges::sort(deviations);
244 return deviations[deviations.size() / 2] * 1.4826;
249 std::vector<size_t> result;
250 if (samples.size() < 2)
253 std::vector<double> sorted(samples.begin(), samples.end());
254 std::ranges::sort(sorted);
255 const double median = sorted[sorted.size() / 2];
256 const double scaled_mad = median_absolute_deviation(samples);
258 if (scaled_mad < 1e-12)
261 for (
size_t i = 0; i < samples.size(); ++i) {
262 const double dev = std::abs(samples[i] - median) / scaled_mad;
263 if (dev > threshold_mad)
272 if (samples.size() < 2)
275 const double first = samples.front();
276 const double last = samples.back();
277 const auto n =
static_cast<double>(samples.size() - 1);
279 const double total_mean = std::accumulate(samples.begin(), samples.end(), 0.0) /
static_cast<double>(samples.size());
281 double total_var = 0.0;
282 double residual_var = 0.0;
284 for (
size_t i = 0; i < samples.size(); ++i) {
285 const double t =
static_cast<double>(i) / n;
286 const double trend_value = first + t * (last - first);
288 const double total_dev = samples[i] - total_mean;
289 total_var += total_dev * total_dev;
291 const double residual = samples[i] - trend_value;
292 residual_var += residual * residual;
295 if (total_var < 1e-12)
298 return std::clamp(1.0 - (residual_var / total_var), 0.0, 1.0);
303 if (samples.size() < 2)
306 const auto n =
static_cast<double>(samples.size());
313 for (
size_t i = 0; i < samples.size(); ++i) {
314 const auto x =
static_cast<double>(i);
315 const double y = samples[i];
322 const double denom = (n * sum_xx) - (sum_x * sum_x);
323 if (std::abs(denom) < 1e-12)
326 return ((n * sum_xy) - (sum_x * sum_y)) / denom;
void reset()
Resets internal state.
static double trend_slope(std::span< const double > samples) noexcept
Linear trend slope across a span.
double update_rolling_variance(double sample)
double update_trend(double sample)
Estimate(EstimateModel model=EstimateModel::EWM_VARIANCE, double adapt_rate=0.05, size_t window=32)
Constructs an estimator with the specified model.
double update(double sample)
Feed one new sample, updating the running estimate.
static std::vector< size_t > flag_outliers(std::span< const double > samples, double threshold_mad=3.0) noexcept
Flags samples whose deviation from the median exceeds a threshold.
static double variance(std::span< const double > samples) noexcept
Sample variance of a span.
void set_window(size_t window)
Sets the window size used by windowed models.
double update_ewm_variance(double sample)
double confidence(double step) const
Confidence that a step is signal, not floor.
static double trend_explained_ratio(std::span< const double > samples) noexcept
Fraction of a span's variance attributable to its linear trend.
double update_quiet_period(double sample)
double update_mad(double sample)
Memory::HistoryBuffer< double > m_history
void set_model(EstimateModel model)
Changes active model.
static double median_absolute_deviation(std::span< const double > samples) noexcept
Median absolute deviation of a span.
static double stddev(std::span< const double > samples) noexcept
Standard deviation of a span.
void resize(size_t new_capacity)
Resize buffer capacity.
std::span< T > linearized_view()
Get mutable linearized view of entire history.
void push(const T &value)
Push new value to front of history.
void reset()
Reset buffer to initial state (all zeros)
EstimateModel
Strategies for characterizing an evolving stream's statistical behavior.
@ MEDIAN_ABSOLUTE_DEVIATION
double mean(const std::vector< double > &data)
Calculate mean of single-channel data.
double trend_explained_ratio