41template <
typename Iterator>
42double mad(Iterator first, Iterator last)
44 auto n =
static_cast<std::size_t
>(std::distance(first, last));
46 throw std::invalid_argument(
"statcpp::mad: empty range");
50 std::vector<double> sorted_data;
51 sorted_data.reserve(n);
52 for (
auto it = first; it != last; ++it) {
53 sorted_data.push_back(
static_cast<double>(*it));
55 std::sort(sorted_data.begin(), sorted_data.end());
60 std::vector<double> abs_deviations;
61 abs_deviations.reserve(n);
62 for (
double val : sorted_data) {
63 abs_deviations.push_back(std::abs(val - med));
67 std::sort(abs_deviations.begin(), abs_deviations.end());
92template <
typename Iterator>
95 return 1.4826 *
mad(first, last);
129template <
typename Iterator>
132 auto n =
static_cast<std::size_t
>(std::distance(first, last));
134 throw std::invalid_argument(
"statcpp::detect_outliers_iqr: empty range");
138 std::vector<double> sorted_data;
139 sorted_data.reserve(n);
140 for (
auto it = first; it != last; ++it) {
141 sorted_data.push_back(
static_cast<double>(*it));
143 std::sort(sorted_data.begin(), sorted_data.end());
146 double iqr_val = q.q3 - q.q1;
148 double lower_fence = q.q1 - k * iqr_val;
149 double upper_fence = q.q3 + k * iqr_val;
160 for (
auto it = first; it != last; ++it, ++idx) {
161 double val =
static_cast<double>(*it);
162 if (val < lower_fence || val > upper_fence) {
189template <
typename Iterator>
192 auto n =
static_cast<std::size_t
>(std::distance(first, last));
194 throw std::invalid_argument(
"statcpp::detect_outliers_zscore: need at least 2 elements");
201 throw std::invalid_argument(
"statcpp::detect_outliers_zscore: zero standard deviation");
212 for (
auto it = first; it != last; ++it, ++idx) {
213 double val =
static_cast<double>(*it);
214 double z = (val - m) / s;
215 if (std::abs(z) > threshold) {
238template <
typename Iterator>
241 auto n =
static_cast<std::size_t
>(std::distance(first, last));
243 throw std::invalid_argument(
"statcpp::detect_outliers_modified_zscore: empty range");
247 std::vector<double> sorted_data;
248 sorted_data.reserve(n);
249 for (
auto it = first; it != last; ++it) {
250 sorted_data.push_back(
static_cast<double>(*it));
252 std::sort(sorted_data.begin(), sorted_data.end());
255 double mad_val =
mad(first, last);
257 if (mad_val == 0.0) {
258 throw std::invalid_argument(
"statcpp::detect_outliers_modified_zscore: zero MAD");
262 double scale = 0.6745;
265 result.
lower_fence = med - threshold * mad_val / scale;
266 result.
upper_fence = med + threshold * mad_val / scale;
272 for (
auto it = first; it != last; ++it, ++idx) {
273 double val =
static_cast<double>(*it);
274 double modified_z = scale * (val - med) / mad_val;
275 if (std::abs(modified_z) > threshold) {
302template <
typename Iterator>
303std::vector<double>
winsorize(Iterator first, Iterator last,
double limits = 0.05)
305 auto n =
static_cast<std::size_t
>(std::distance(first, last));
307 throw std::invalid_argument(
"statcpp::winsorize: empty range");
309 if (limits < 0.0 || limits >= 0.5) {
310 throw std::invalid_argument(
"statcpp::winsorize: limits must be in [0, 0.5)");
314 std::vector<double> sorted_data;
315 sorted_data.reserve(n);
316 for (
auto it = first; it != last; ++it) {
317 sorted_data.push_back(
static_cast<double>(*it));
319 std::sort(sorted_data.begin(), sorted_data.end());
322 double lower_percentile = limits;
323 double upper_percentile = 1.0 - limits;
324 double lower_value =
statcpp::percentile(sorted_data.begin(), sorted_data.end(), lower_percentile);
325 double upper_value =
statcpp::percentile(sorted_data.begin(), sorted_data.end(), upper_percentile);
328 std::vector<double> result;
330 for (
auto it = first; it != last; ++it) {
331 double val =
static_cast<double>(*it);
332 if (val < lower_value) {
333 result.push_back(lower_value);
334 }
else if (val > upper_value) {
335 result.push_back(upper_value);
337 result.push_back(val);
364 const std::vector<double>& residuals,
365 const std::vector<double>& hat_values,
369 if (residuals.size() != hat_values.size()) {
370 throw std::invalid_argument(
"statcpp::cooks_distance: residuals and hat_values must have same length");
372 if (residuals.empty()) {
373 throw std::invalid_argument(
"statcpp::cooks_distance: empty data");
376 throw std::invalid_argument(
"statcpp::cooks_distance: mse must be positive");
379 throw std::invalid_argument(
"statcpp::cooks_distance: p must be positive");
382 std::vector<double> result;
383 result.reserve(residuals.size());
385 for (std::size_t i = 0; i < residuals.size(); ++i) {
386 double h = hat_values[i];
388 result.push_back(std::numeric_limits<double>::infinity());
392 double e = residuals[i];
393 double d = (e * e / (
static_cast<double>(p) *
mse)) *
394 (h / ((1.0 - h) * (1.0 - h)));
419 const std::vector<double>& residuals,
420 const std::vector<double>& hat_values,
423 if (residuals.size() != hat_values.size()) {
424 throw std::invalid_argument(
"statcpp::dffits: residuals and hat_values must have same length");
426 if (residuals.empty()) {
427 throw std::invalid_argument(
"statcpp::dffits: empty data");
430 std::vector<double> result;
431 result.reserve(residuals.size());
433 for (std::size_t i = 0; i < residuals.size(); ++i) {
434 double h = hat_values[i];
436 result.push_back(std::numeric_limits<double>::infinity());
440 double e = residuals[i];
442 double se = std::sqrt(
mse * (1.0 - h));
444 result.push_back(std::numeric_limits<double>::infinity());
450 double dffits_val = t * std::sqrt(h / (1.0 - h));
451 result.push_back(dffits_val);
474template <
typename Iterator>
477 auto n =
static_cast<std::size_t
>(std::distance(first, last));
479 throw std::invalid_argument(
"statcpp::hodges_lehmann: empty range");
482 std::vector<double> data;
484 for (
auto it = first; it != last; ++it) {
485 data.push_back(
static_cast<double>(*it));
489 std::vector<double> pairwise_means;
490 pairwise_means.reserve(n * (n + 1) / 2);
492 for (std::size_t i = 0; i < n; ++i) {
493 for (std::size_t j = i; j < n; ++j) {
494 pairwise_means.push_back((data[i] + data[j]) / 2.0);
498 std::sort(pairwise_means.begin(), pairwise_means.end());
516template <
typename Iterator>
519 auto n =
static_cast<std::size_t
>(std::distance(first, last));
521 throw std::invalid_argument(
"statcpp::biweight_midvariance: need at least 2 elements");
525 std::vector<double> data;
527 for (
auto it = first; it != last; ++it) {
528 data.push_back(
static_cast<double>(*it));
530 std::sort(data.begin(), data.end());
533 double mad_val =
mad(first, last);
535 if (mad_val == 0.0) {
541 std::size_t
count = 0;
543 for (
double x : data) {
544 double u = (x - med) / (c * mad_val);
545 if (std::abs(u) < 1.0) {
547 double w = (1.0 - u2);
550 num += (x - med) * (x - med) * w4;
551 den += w * (1.0 - 5.0 * u2);
556 if (den == 0.0 ||
count < 2) {
560 return static_cast<double>(n) * num / (den * den);
Basic statistical computation functions.
Dispersion and variance calculation functions.
std::vector< double > dffits(const std::vector< double > &residuals, const std::vector< double > &hat_values, double mse)
Calculate DFFITS.
double biweight_midvariance(Iterator first, Iterator last, double c=9.0)
Biweight Midvariance.
double sample_stddev(Iterator first, Iterator last)
Sample standard deviation.
double mad(Iterator first, Iterator last)
Median Absolute Deviation (MAD)
std::vector< double > cooks_distance(const std::vector< double > &residuals, const std::vector< double > &hat_values, double mse, std::size_t p)
Calculate Cook's Distance.
outlier_detection_result detect_outliers_zscore(Iterator first, Iterator last, double threshold=3.0)
Outlier detection using Z-score.
std::vector< double > winsorize(Iterator first, Iterator last, double limits=0.05)
Winsorization.
double percentile(Iterator first, Iterator last, double p)
Return percentile.
double mean(Iterator first, Iterator last)
Arithmetic mean.
double hodges_lehmann(Iterator first, Iterator last)
Hodges-Lehmann estimator.
quartile_result quartiles(Iterator first, Iterator last)
Return quartiles.
double mad_scaled(Iterator first, Iterator last)
Scaled MAD for normal distribution.
double median(Iterator first, Iterator last)
Median (accepts a sorted range)
outlier_detection_result detect_outliers_iqr(Iterator first, Iterator last, double k=1.5)
Outlier detection using IQR method (Tukey's Fences)
double mse(Iterator1 first1, Iterator1 last1, Iterator2 first2)
Mean Squared Error (MSE)
outlier_detection_result detect_outliers_modified_zscore(Iterator first, Iterator last, double threshold=3.5)
Outlier detection using Modified Z-score.
std::size_t count(Iterator first, Iterator last)
Data count.
Order statistics implementation.
Outlier detection result.
double iqr_value
Interquartile range.
std::vector< std::size_t > outlier_indices
Outlier indices.
double upper_fence
Upper fence.
std::vector< double > outliers
Outliers.
double lower_fence
Lower fence.