v2.0.0
Loading...
Searching...
No Matches
sts_correction.cpp
Go to the documentation of this file.
1//=============================================================================================================
20
21//=============================================================================================================
22// INCLUDES
23//=============================================================================================================
24
25#include "sts_correction.h"
26
27#include <algorithm>
28#include <numeric>
29#include <vector>
30#include <cmath>
31
32//=============================================================================================================
33// USED NAMESPACES
34//=============================================================================================================
35
36using namespace STSLIB;
37using namespace Eigen;
38
39//=============================================================================================================
40// DEFINE METHODS
41//=============================================================================================================
42
43MatrixXd StatsMcCorrection::bonferroni(const MatrixXd& pValues, double alpha, RejectMask* reject)
44{
45 const MatrixXd corrected = (pValues * static_cast<double>(pValues.size())).cwiseMin(1.0);
46 if (reject)
47 *reject = corrected.array() < alpha;
48 return corrected;
49}
50
51//=============================================================================================================
52
53MatrixXd StatsMcCorrection::holmBonferroni(const MatrixXd& pValues)
54{
55 const int n = static_cast<int>(pValues.size());
56
57 // Flatten to a vector with indices
58 std::vector<std::pair<double, int>> indexed(n);
59 for (int i = 0; i < n; ++i) {
60 indexed[i] = {pValues.data()[i], i};
61 }
62
63 // Sort ascending by p-value
64 std::sort(indexed.begin(), indexed.end());
65
66 // Compute adjusted p-values: adjusted_p[i] = p * (n - rank_i + 1), rank is 1-based
67 std::vector<double> adjusted(n);
68 for (int i = 0; i < n; ++i) {
69 adjusted[i] = indexed[i].first * static_cast<double>(n - i);
70 }
71
72 // Enforce monotonicity: from first to last, corrected[i] = max(corrected[i], corrected[i-1])
73 for (int i = 1; i < n; ++i) {
74 adjusted[i] = std::max(adjusted[i], adjusted[i - 1]);
75 }
76
77 // Cap at 1.0 and place back in original order
78 MatrixXd corrected(pValues.rows(), pValues.cols());
79 for (int i = 0; i < n; ++i) {
80 corrected.data()[indexed[i].second] = std::min(adjusted[i], 1.0);
81 }
82
83 return corrected;
84}
85
86//=============================================================================================================
87
88MatrixXd StatsMcCorrection::fdr(const MatrixXd& pValues, double alpha, FdrMethod method, RejectMask* reject)
89{
90 const int n = static_cast<int>(pValues.size());
91
92 std::vector<std::pair<double, int>> indexed(n);
93 for (int i = 0; i < n; ++i)
94 indexed[i] = {pValues.data()[i], i};
95 std::sort(indexed.begin(), indexed.end());
96
97 // Benjamini-Yekutieli divides the rank factor by the harmonic number of n
98 double harmonic = 1.0;
99 if (method == FdrMethod::NegativelyCorrelated) {
100 harmonic = 0.0;
101 for (int k = 1; k <= n; ++k)
102 harmonic += 1.0 / k;
103 }
104
105 std::vector<double> adjusted(n);
106 int lastRejected = -1;
107 for (int i = 0; i < n; ++i) {
108 const double factor = (i + 1) / (static_cast<double>(n) * harmonic);
109 adjusted[i] = indexed[i].first / factor;
110 if (indexed[i].first < factor * alpha)
111 lastRejected = i;
112 }
113 for (int i = n - 2; i >= 0; --i)
114 adjusted[i] = std::min(adjusted[i], adjusted[i + 1]);
115
116 MatrixXd corrected(pValues.rows(), pValues.cols());
117 if (reject)
118 *reject = RejectMask::Constant(pValues.rows(), pValues.cols(), false);
119 for (int i = 0; i < n; ++i) {
120 corrected.data()[indexed[i].second] = std::min(adjusted[i], 1.0);
121 if (reject)
122 reject->data()[indexed[i].second] = i <= lastRejected;
123 }
124 return corrected;
125}
Family-wise error and false-discovery-rate corrections for mass-univariate p-value maps.
Statistical testing (t-tests, F-tests, cluster permutation, multiple comparison correction).
static Eigen::MatrixXd holmBonferroni(const Eigen::MatrixXd &pValues)
static Eigen::MatrixXd bonferroni(const Eigen::MatrixXd &pValues, double alpha=0.05, RejectMask *reject=nullptr)
static Eigen::MatrixXd fdr(const Eigen::MatrixXd &pValues, double alpha=0.05, FdrMethod method=FdrMethod::Independent, RejectMask *reject=nullptr)
Eigen::Array< bool, Eigen::Dynamic, Eigen::Dynamic > RejectMask