statcpp
C++17 Header-Only Statistics Library
Loading...
Searching...
No Matches
effect_size.hpp
Go to the documentation of this file.
1
6#pragma once
7
10
11#include <cmath>
12#include <stdexcept>
13
14namespace statcpp {
15
16// ============================================================================
17// Cohen's d (Standardized Mean Difference)
18// ============================================================================
19
34template <typename Iterator>
35double cohens_d(Iterator first, Iterator last, double mu0, double sigma)
36{
37 if (sigma <= 0.0) {
38 throw std::invalid_argument("statcpp::cohens_d: sigma must be positive");
39 }
40
41 auto n = statcpp::count(first, last);
42 if (n == 0) {
43 throw std::invalid_argument("statcpp::cohens_d: empty range");
44 }
45
46 double mean_val = statcpp::mean(first, last);
47 return (mean_val - mu0) / sigma;
48}
49
62template <typename Iterator>
63double cohens_d(Iterator first, Iterator last, double mu0)
64{
65 auto n = statcpp::count(first, last);
66 if (n < 2) {
67 throw std::invalid_argument("statcpp::cohens_d: need at least 2 elements");
68 }
69
70 double mean_val = statcpp::mean(first, last);
71 double s = statcpp::sample_stddev(first, last);
72
73 if (s == 0.0) {
74 throw std::invalid_argument("statcpp::cohens_d: zero variance");
75 }
76
77 return (mean_val - mu0) / s;
78}
79
95template <typename Iterator1, typename Iterator2>
96double cohens_d_two_sample(Iterator1 first1, Iterator1 last1,
97 Iterator2 first2, Iterator2 last2)
98{
99 auto n1 = statcpp::count(first1, last1);
100 auto n2 = statcpp::count(first2, last2);
101
102 if (n1 < 2 || n2 < 2) {
103 throw std::invalid_argument("statcpp::cohens_d_two_sample: need at least 2 elements in each sample");
104 }
105
106 double mean1 = statcpp::mean(first1, last1);
107 double mean2 = statcpp::mean(first2, last2);
108 double var1 = statcpp::sample_variance(first1, last1);
109 double var2 = statcpp::sample_variance(first2, last2);
110
111 // Pooled standard deviation
112 double sp = std::sqrt(((n1 - 1) * var1 + (n2 - 1) * var2) / (n1 + n2 - 2));
113
114 if (sp == 0.0) {
115 throw std::invalid_argument("statcpp::cohens_d_two_sample: zero pooled variance");
116 }
117
118 return (mean1 - mean2) / sp;
119}
120
121// ============================================================================
122// Hedges' g (Bias-corrected Cohen's d)
123// ============================================================================
124
133inline double hedges_correction_factor(double df)
134{
135 // J ≈ 1 - 3 / (4 * df - 1)
136 return 1.0 - 3.0 / (4.0 * df - 1.0);
137}
138
151template <typename Iterator>
152double hedges_g(Iterator first, Iterator last, double mu0)
153{
154 auto n = statcpp::count(first, last);
155 if (n < 2) {
156 throw std::invalid_argument("statcpp::hedges_g: need at least 2 elements");
157 }
158
159 double d = cohens_d(first, last, mu0);
160 double df = static_cast<double>(n - 1);
161 return d * hedges_correction_factor(df);
162}
163
178template <typename Iterator1, typename Iterator2>
179double hedges_g_two_sample(Iterator1 first1, Iterator1 last1,
180 Iterator2 first2, Iterator2 last2)
181{
182 auto n1 = statcpp::count(first1, last1);
183 auto n2 = statcpp::count(first2, last2);
184
185 if (n1 < 2 || n2 < 2) {
186 throw std::invalid_argument("statcpp::hedges_g_two_sample: need at least 2 elements in each sample");
187 }
188
189 double d = cohens_d_two_sample(first1, last1, first2, last2);
190 double df = static_cast<double>(n1 + n2 - 2);
191 return d * hedges_correction_factor(df);
192}
193
194// ============================================================================
195// Glass's Delta (using control group's SD)
196// ============================================================================
197
213template <typename Iterator1, typename Iterator2>
214double glass_delta(Iterator1 control_first, Iterator1 control_last,
215 Iterator2 treatment_first, Iterator2 treatment_last)
216{
217 auto n1 = statcpp::count(control_first, control_last);
218 auto n2 = statcpp::count(treatment_first, treatment_last);
219
220 if (n1 < 2) {
221 throw std::invalid_argument("statcpp::glass_delta: control group needs at least 2 elements");
222 }
223 if (n2 == 0) {
224 throw std::invalid_argument("statcpp::glass_delta: treatment group is empty");
225 }
226
227 double mean1 = statcpp::mean(control_first, control_last);
228 double mean2 = statcpp::mean(treatment_first, treatment_last);
229 double s1 = statcpp::sample_stddev(control_first, control_last);
230
231 if (s1 == 0.0) {
232 throw std::invalid_argument("statcpp::glass_delta: control group has zero variance");
233 }
234
235 return (mean2 - mean1) / s1;
236}
237
238// ============================================================================
239// Correlation-based Effect Size (r)
240// ============================================================================
241
249inline double t_to_r(double t, double df)
250{
251 return t / std::sqrt(t * t + df);
252}
253
260inline double d_to_r(double d)
261{
262 // r = d / sqrt(d^2 + 4)
263 return d / std::sqrt(d * d + 4.0);
264}
265
273inline double r_to_d(double r)
274{
275 // d = 2r / sqrt(1 - r^2)
276 if (std::abs(r) >= 1.0) {
277 throw std::invalid_argument("statcpp::r_to_d: |r| must be less than 1");
278 }
279 return 2.0 * r / std::sqrt(1.0 - r * r);
280}
281
282// ============================================================================
283// Eta-squared and Partial Eta-squared
284// ============================================================================
285
296inline double eta_squared(double ss_effect, double ss_total)
297{
298 if (ss_total <= 0.0) {
299 throw std::invalid_argument("statcpp::eta_squared: ss_total must be positive");
300 }
301 return ss_effect / ss_total;
302}
303
312inline double partial_eta_squared(double f, double df1, double df2)
313{
314 return (f * df1) / (f * df1 + df2);
315}
316
317// ============================================================================
318// Omega-squared (less biased than eta-squared)
319// ============================================================================
320
333inline double omega_squared(double ss_effect, double ss_total, double ms_error, double df_effect)
334{
335 if (ss_total <= 0.0) {
336 throw std::invalid_argument("statcpp::omega_squared: ss_total must be positive");
337 }
338 return (ss_effect - df_effect * ms_error) / (ss_total + ms_error);
339}
340
341// ============================================================================
342// Cohen's h (Effect Size for Proportions)
343// ============================================================================
344
355inline double cohens_h(double p1, double p2)
356{
357 if (p1 < 0.0 || p1 > 1.0 || p2 < 0.0 || p2 > 1.0) {
358 throw std::invalid_argument("statcpp::cohens_h: proportions must be in [0, 1]");
359 }
360
361 // h = 2 * (arcsin(sqrt(p1)) - arcsin(sqrt(p2)))
362 return 2.0 * (std::asin(std::sqrt(p1)) - std::asin(std::sqrt(p2)));
363}
364
365// ============================================================================
366// Odds Ratio and Risk Ratio
367// ============================================================================
368
381inline double odds_ratio(double a, double b, double c, double d)
382{
383 // 2x2 table: a, b, c, d
384 if (b == 0.0 || c == 0.0) {
385 throw std::invalid_argument("statcpp::odds_ratio: cell b or c is zero");
386 }
387 return (a * d) / (b * c);
388}
389
402inline double risk_ratio(double a, double b, double c, double d)
403{
404 // Risk in group 1: a / (a + b)
405 // Risk in group 2: c / (c + d)
406 if (a + b == 0.0 || c + d == 0.0) {
407 throw std::invalid_argument("statcpp::risk_ratio: row total is zero");
408 }
409 double risk1 = a / (a + b);
410 double risk2 = c / (c + d);
411
412 if (risk2 == 0.0) {
413 throw std::invalid_argument("statcpp::risk_ratio: risk in group 2 is zero");
414 }
415
416 return risk1 / risk2;
417}
418
419// ============================================================================
420// Effect Size Interpretation
421// ============================================================================
422
427 negligible,
428 small,
429 medium,
430 large
431};
432
442{
443 double abs_d = std::abs(d);
444 if (abs_d < 0.2) return effect_size_magnitude::negligible;
445 if (abs_d < 0.5) return effect_size_magnitude::small;
446 if (abs_d < 0.8) return effect_size_magnitude::medium;
448}
449
459{
460 double abs_r = std::abs(r);
461 if (abs_r < 0.1) return effect_size_magnitude::negligible;
462 if (abs_r < 0.3) return effect_size_magnitude::small;
463 if (abs_r < 0.5) return effect_size_magnitude::medium;
465}
466
476{
477 if (eta2 < 0.01) return effect_size_magnitude::negligible;
478 if (eta2 < 0.06) return effect_size_magnitude::small;
479 if (eta2 < 0.14) return effect_size_magnitude::medium;
481}
482
483} // namespace statcpp
Basic statistical computation functions.
Dispersion and variance calculation functions.
double risk_ratio(double a, double b, double c, double d)
Relative risk (risk ratio)
effect_size_magnitude interpret_cohens_d(double d)
Interpret Cohen's d.
double omega_squared(const one_way_anova_result &result)
Calculate Omega-squared for one-way ANOVA.
Definition anova.hpp:899
double cohens_d_two_sample(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
Cohen's d (two-sample, pooled standard deviation)
double sample_stddev(Iterator first, Iterator last)
Sample standard deviation.
double sample_variance(Iterator first, Iterator last)
Sample variance (unbiased variance)
odds_ratio_result odds_ratio(const std::vector< std::vector< std::size_t > > &table)
Calculate odds ratio from a 2x2 contingency table.
double d_to_r(double d)
Convert Cohen's d to correlation coefficient.
double t_to_r(double t, double df)
Convert t-value to correlation coefficient.
double r_to_d(double r)
Convert correlation coefficient to Cohen's d.
double glass_delta(Iterator1 control_first, Iterator1 control_last, Iterator2 treatment_first, Iterator2 treatment_last)
Glass's Delta (using control group's standard deviation)
double hedges_g_two_sample(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
Hedges' g (two-sample)
double eta_squared(const one_way_anova_result &result)
Calculate Eta-squared for one-way ANOVA.
Definition anova.hpp:828
double partial_eta_squared(double f, double df1, double df2)
Calculate partial eta-squared from F-test.
double mean(Iterator first, Iterator last)
Arithmetic mean.
double cohens_h(double p1, double p2)
Cohen's h (effect size for difference between two proportions)
effect_size_magnitude
Enumeration for effect size magnitude.
effect_size_magnitude interpret_eta_squared(double eta2)
Interpret eta-squared.
double cohens_d(Iterator first, Iterator last, double mu0, double sigma)
Cohen's d (one-sample, known population standard deviation)
effect_size_magnitude interpret_correlation(double r)
Interpret correlation coefficient.
double hedges_g(Iterator first, Iterator last, double mu0)
Hedges' g (one-sample)
std::size_t count(Iterator first, Iterator last)
Data count.
double hedges_correction_factor(double df)
Hedges' bias correction factor J.