v2.0.0
Loading...
Searching...
No Matches
sts_ttest.cpp
Go to the documentation of this file.
1//=============================================================================================================
34
35//=============================================================================================================
36// INCLUDES
37//=============================================================================================================
38
39#include "sts_ttest.h"
40
41#include <cmath>
42#include <algorithm>
43
44//=============================================================================================================
45// USED NAMESPACES
46//=============================================================================================================
47
48using namespace STSLIB;
49using namespace Eigen;
50
51//=============================================================================================================
52// DEFINE METHODS
53//=============================================================================================================
54
55StatsTtestResult StatsTtest::oneSample(const MatrixXd& data, double mu, StatsTailType tail)
56{
57 const int n = static_cast<int>(data.rows());
58 const int nVars = static_cast<int>(data.cols());
59 const int df = n - 1;
60
61 // Column means
62 RowVectorXd means = data.colwise().mean();
63
64 // Column standard deviations
65 MatrixXd centered = data.rowwise() - means;
66 RowVectorXd stddev = (centered.colwise().squaredNorm() / static_cast<double>(df)).array().sqrt();
67
68 // t-statistics: (mean - mu) / (std / sqrt(n))
69 double sqrtN = std::sqrt(static_cast<double>(n));
70 RowVectorXd tstat = (means.array() - mu) / (stddev.array() / sqrtN);
71
72 // p-values
73 MatrixXd matPval(1, nVars);
74 for (int j = 0; j < nVars; ++j) {
75 matPval(0, j) = tToPval(tstat(j), df, tail);
76 }
77
78 StatsTtestResult result;
79 result.matTstat = tstat;
80 result.matPval = matPval;
81 result.degreesOfFreedom = df;
82 return result;
83}
84
85//=============================================================================================================
86
87StatsTtestResult StatsTtest::paired(const MatrixXd& dataA, const MatrixXd& dataB, StatsTailType tail)
88{
89 return oneSample(dataA - dataB, 0.0, tail);
90}
91
92//=============================================================================================================
93
94StatsTtestResult StatsTtest::independent(const MatrixXd& dataA, const MatrixXd& dataB, StatsTailType tail)
95{
96 const int nA = static_cast<int>(dataA.rows());
97 const int nB = static_cast<int>(dataB.rows());
98 const int nVars = static_cast<int>(dataA.cols());
99 const int df = nA + nB - 2;
100
101 RowVectorXd meanA = dataA.colwise().mean();
102 RowVectorXd meanB = dataB.colwise().mean();
103
104 // Pooled variance
105 MatrixXd centA = dataA.rowwise() - meanA;
106 MatrixXd centB = dataB.rowwise() - meanB;
107 RowVectorXd ssA = centA.colwise().squaredNorm();
108 RowVectorXd ssB = centB.colwise().squaredNorm();
109 RowVectorXd pooledVar = (ssA + ssB) / static_cast<double>(df);
110
111 // t-statistic
112 double invN = 1.0 / static_cast<double>(nA) + 1.0 / static_cast<double>(nB);
113 RowVectorXd tstat = (meanA - meanB).array() / (pooledVar.array() * invN).sqrt();
114
115 // p-values
116 MatrixXd matPval(1, nVars);
117 for (int j = 0; j < nVars; ++j) {
118 matPval(0, j) = tToPval(tstat(j), df, tail);
119 }
120
121 StatsTtestResult result;
122 result.matTstat = tstat;
123 result.matPval = matPval;
124 result.degreesOfFreedom = df;
125 return result;
126}
127
128//=============================================================================================================
129
130double StatsTtest::tCdf(double t, int df)
131{
132 // P(T <= t) for t-distribution with df degrees of freedom.
133 // Using: P(T <= t) = 1 - 0.5 * I_x(df/2, 1/2) where x = df/(df + t^2) [for t >= 0]
134 // For t < 0: P(T <= t) = 0.5 * I_x(df/2, 1/2) where x = df/(df + t^2)
135 double x = static_cast<double>(df) / (static_cast<double>(df) + t * t);
136 double iBeta = regularizedBeta(x, static_cast<double>(df) / 2.0, 0.5);
137 if (t >= 0.0) {
138 return 1.0 - 0.5 * iBeta;
139 } else {
140 return 0.5 * iBeta;
141 }
142}
143
144//=============================================================================================================
145
146double StatsTtest::tToPval(double t, int df, StatsTailType tail)
147{
148 double cdf = tCdf(t, df);
149 switch (tail) {
151 return cdf;
153 return 1.0 - cdf;
155 default:
156 return 2.0 * std::min(cdf, 1.0 - cdf);
157 }
158}
159
160//=============================================================================================================
161
162double StatsTtest::regularizedBeta(double x, double a, double b)
163{
164 // I_x(a,b) = x^a * (1-x)^b / (a * B(a,b)) * CF(x,a,b)
165 // where CF is the continued fraction expansion.
166 // For x > (a+1)/(a+b+2), use the identity I_x(a,b) = 1 - I_{1-x}(b,a).
167 if (x <= 0.0) return 0.0;
168 if (x >= 1.0) return 1.0;
169
170 if (x > (a + 1.0) / (a + b + 2.0)) {
171 return 1.0 - regularizedBeta(1.0 - x, b, a);
172 }
173
174 double lnPre = a * std::log(x) + b * std::log(1.0 - x) - logBeta(a, b) - std::log(a);
175 return std::exp(lnPre) * betaCf(x, a, b);
176}
177
178//=============================================================================================================
179
180double StatsTtest::betaCf(double x, double a, double b)
181{
182 // Continued fraction for I_x(a,b) using Lentz's algorithm.
183 const int maxIter = 200;
184 const double eps = 1.0e-12;
185 const double tiny = 1.0e-30;
186
187 double qab = a + b;
188 double qap = a + 1.0;
189 double qam = a - 1.0;
190
191 double c = 1.0;
192 double d = 1.0 - qab * x / qap;
193 if (std::fabs(d) < tiny) d = tiny;
194 d = 1.0 / d;
195 double h = d;
196
197 for (int m = 1; m <= maxIter; ++m) {
198 double m2 = 2.0 * m;
199
200 // Even step
201 double aa = m * (b - m) * x / ((qam + m2) * (a + m2));
202 d = 1.0 + aa * d;
203 if (std::fabs(d) < tiny) d = tiny;
204 c = 1.0 + aa / c;
205 if (std::fabs(c) < tiny) c = tiny;
206 d = 1.0 / d;
207 h *= d * c;
208
209 // Odd step
210 aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
211 d = 1.0 + aa * d;
212 if (std::fabs(d) < tiny) d = tiny;
213 c = 1.0 + aa / c;
214 if (std::fabs(c) < tiny) c = tiny;
215 d = 1.0 / d;
216 double del = d * c;
217 h *= del;
218
219 if (std::fabs(del - 1.0) < eps) break;
220 }
221 return h;
222}
223
224//=============================================================================================================
225
226double StatsTtest::logBeta(double a, double b)
227{
228 return std::lgamma(a) + std::lgamma(b) - std::lgamma(a + b);
229}
StatsTtest class declaration.
Statistical testing (t-tests, F-tests, cluster permutation, multiple comparison correction).
StatsTailType
Definition sts_types.h:49
Eigen::MatrixXd matPval
Definition sts_ttest.h:64
Eigen::MatrixXd matTstat
Definition sts_ttest.h:63
static double regularizedBeta(double x, double a, double b)
static StatsTtestResult paired(const Eigen::MatrixXd &dataA, const Eigen::MatrixXd &dataB, StatsTailType tail=StatsTailType::Both)
Definition sts_ttest.cpp:87
static double tCdf(double t, int df)
static StatsTtestResult oneSample(const Eigen::MatrixXd &data, double mu=0.0, StatsTailType tail=StatsTailType::Both)
Definition sts_ttest.cpp:55
static StatsTtestResult independent(const Eigen::MatrixXd &dataA, const Eigen::MatrixXd &dataB, StatsTailType tail=StatsTailType::Both)
Definition sts_ttest.cpp:94