|
| 1 | +#! /usr/bin/env python |
| 2 | + |
| 3 | +import numpy as np |
| 4 | +from scipy.stats import norm |
| 5 | +from scipy.stats import skewnorm |
| 6 | +import pandas as pd |
| 7 | +import pytest |
| 8 | + |
| 9 | +from .._api import load |
| 10 | + |
| 11 | + |
| 12 | + |
1 | 13 | def test_paired_mean_diff_ci(): |
2 | 14 | # See Altman et al., Statistics with Confidence: |
3 | 15 | # Confidence Intervals and Statistical Guidelines (Second Edition). Wiley, 2000. |
@@ -36,3 +48,126 @@ def test_paired_mean_diff_ci(): |
36 | 48 | # paired_median_diff = endorphin_marathon.median_diff.results |
37 | 49 | # |
38 | 50 | # assert pytest.approx(10.4) == paired_median_diff.bca_low[0] |
| 51 | +# assert pytest.approx(25.0) == paired_median_diff.bca_high[0] |
| 52 | + |
| 53 | + |
| 54 | +def test_unpaired_ci(reps=30, ci=95): |
| 55 | + # Dropped to 30 reps to save time. v0.2.5. |
| 56 | + POPULATION_N = 10000 |
| 57 | + SAMPLE_N = 10 |
| 58 | + |
| 59 | + # Create data for hedges g and cohens d. |
| 60 | + CONTROL_MEAN = np.random.randint(1, 1000) |
| 61 | + POP_SD = np.random.randint(1, 15) |
| 62 | + POP_D = np.round(np.random.uniform(-2, 2, 1)[0], 2) |
| 63 | + |
| 64 | + TRUE_STD_DIFFERENCE = CONTROL_MEAN + (POP_D * POP_SD) |
| 65 | + norm_sample_kwargs = dict(scale=POP_SD, size=SAMPLE_N) |
| 66 | + c1 = norm.rvs(loc=CONTROL_MEAN, **norm_sample_kwargs) |
| 67 | + t1 = norm.rvs(loc=CONTROL_MEAN+TRUE_STD_DIFFERENCE, **norm_sample_kwargs) |
| 68 | + |
| 69 | + std_diff_df = pd.DataFrame({'Control' : c1, 'Test': t1}) |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | + # Create mean_diff data |
| 74 | + CONTROL_MEAN = np.random.randint(1, 1000) |
| 75 | + POP_SD = np.random.randint(1, 15) |
| 76 | + TRUE_DIFFERENCE = np.random.randint(-POP_SD*5, POP_SD*5) |
| 77 | + |
| 78 | + c1 = norm.rvs(loc=CONTROL_MEAN, **norm_sample_kwargs) |
| 79 | + t1 = norm.rvs(loc=CONTROL_MEAN+TRUE_DIFFERENCE, **norm_sample_kwargs) |
| 80 | + |
| 81 | + mean_df = pd.DataFrame({'Control' : c1, 'Test': t1}) |
| 82 | + |
| 83 | + |
| 84 | + |
| 85 | + # Create median_diff data |
| 86 | + MEDIAN_DIFFERENCE = np.random.randint(-5, 5) |
| 87 | + A = np.random.randint(-7, 7) |
| 88 | + |
| 89 | + skew_kwargs = dict(a=A, scale=5, size=POPULATION_N) |
| 90 | + skewpop1 = skewnorm.rvs(**skew_kwargs, loc=100) |
| 91 | + skewpop2 = skewnorm.rvs(**skew_kwargs, loc=100+MEDIAN_DIFFERENCE) |
| 92 | + |
| 93 | + sample_kwargs = dict(replace=False, size=SAMPLE_N) |
| 94 | + skewsample1 = np.random.choice(skewpop1, **sample_kwargs) |
| 95 | + skewsample2 = np.random.choice(skewpop2, **sample_kwargs) |
| 96 | + |
| 97 | + median_df = pd.DataFrame({'Control' : skewsample1, 'Test': skewsample2}) |
| 98 | + |
| 99 | + |
| 100 | + |
| 101 | + # Create two populations with a 50% overlap. |
| 102 | + CD_DIFFERENCE = np.random.randint(1, 10) |
| 103 | + SD = np.abs(CD_DIFFERENCE) |
| 104 | + |
| 105 | + pop_kwargs = dict(scale=SD, size=POPULATION_N) |
| 106 | + pop1 = norm.rvs(loc=100, **pop_kwargs) |
| 107 | + pop2 = norm.rvs(loc=100+CD_DIFFERENCE, **pop_kwargs) |
| 108 | + |
| 109 | + sample_kwargs = dict(replace=False, size=SAMPLE_N) |
| 110 | + sample1 = np.random.choice(pop1, **sample_kwargs) |
| 111 | + sample2 = np.random.choice(pop2, **sample_kwargs) |
| 112 | + |
| 113 | + cd_df = pd.DataFrame({'Control' : sample1, 'Test': sample2}) |
| 114 | + |
| 115 | + |
| 116 | + |
| 117 | + # Create several CIs and see if the true population difference lies within. |
| 118 | + error_count_cohens_d = 0 |
| 119 | + error_count_hedges_g = 0 |
| 120 | + error_count_mean_diff = 0 |
| 121 | + error_count_median_diff = 0 |
| 122 | + error_count_cliffs_delta = 0 |
| 123 | + |
| 124 | + for i in range(0, reps): |
| 125 | + # pick a random seed |
| 126 | + rnd_sd = np.random.randint(0, 999999) |
| 127 | + load_kwargs = dict(ci=ci, random_seed=rnd_sd) |
| 128 | + |
| 129 | + |
| 130 | + |
| 131 | + std_diff_data = load(data=std_diff_df, idx=("Control", "Test"), **load_kwargs) |
| 132 | + |
| 133 | + cd = std_diff_data.cohens_d.results |
| 134 | + cd_low, cd_high = float(cd.bca_low), float(cd.bca_high) |
| 135 | + if cd_low < POP_D < cd_high is False: |
| 136 | + error_count_cohens_d += 1 |
| 137 | + |
| 138 | + hg = std_diff_data.hedges_g.results |
| 139 | + hg_low, hg_high = float(hg.bca_low), float(hg.bca_high) |
| 140 | + if hg_low < POP_D < hg_high is False: |
| 141 | + error_count_hedges_g += 1 |
| 142 | + |
| 143 | + |
| 144 | + |
| 145 | + mean_diff_data = load(data=mean_df, idx=("Control", "Test"), **load_kwargs) |
| 146 | + mean_d = mean_diff_data.mean_diff.results |
| 147 | + mean_d_low, mean_d_high = float(mean_d.bca_low), float(mean_d.bca_high) |
| 148 | + if mean_d_low < TRUE_DIFFERENCE < mean_d_high is False: |
| 149 | + error_count_mean_diff += 1 |
| 150 | + |
| 151 | + |
| 152 | + median_diff_data = load(data=median_df, idx=("Control", "Test"), |
| 153 | + **load_kwargs) |
| 154 | + median_d = median_diff_data.median_diff.results |
| 155 | + median_d_low, median_d_high = float(median_d.bca_low), float(median_d.bca_high) |
| 156 | + if median_d_low < MEDIAN_DIFFERENCE < median_d_high is False: |
| 157 | + error_count_median_diff += 1 |
| 158 | + |
| 159 | + |
| 160 | + cd_data = load(data=cd_df, idx=("Control", "Test"), **load_kwargs) |
| 161 | + cd = cd_data.cliffs_delta.results |
| 162 | + low, high = float(cd.bca_low), float(cd.bca_high) |
| 163 | + if low < 0.5 < high is False: |
| 164 | + error_count_cliffs_delta += 1 |
| 165 | + |
| 166 | + |
| 167 | + max_errors = int(np.ceil(reps * (100 - ci) / 100)) |
| 168 | + |
| 169 | + assert error_count_cohens_d <= max_errors |
| 170 | + assert error_count_hedges_g <= max_errors |
| 171 | + assert error_count_mean_diff <= max_errors |
| 172 | + assert error_count_median_diff <= max_errors |
| 173 | + assert error_count_cliffs_delta <= max_errors |
0 commit comments