Practical Statistical Scenarios
SciPy - Statistical Testing
2 min read
Published Nov 17 2025, updated Aug 17 2026
Guide Sections
Guide Comments
This section shows how to pick the right statistical test and perform full workflows for common real-world situations:
- A/B testing (web experiments)
- Before/after tests (clinical & performance improvements)
- Survey & Likert-scale analysis
- Group comparisons (treatment groups, product versions)
- Check assumptions → run test → compute effect size → interpret
- Full “test selection workflow”
The focus is practical: what tests to run, how to run them, and how to interpret the results.
Scenario 1 — A/B Test (Conversion Rates)
Binary outcome → Proportion test
Question:
Is version B’s conversion rate higher than A’s?
Data:
- Group A: 200 conversions out of 2500 visits
- Group B: 260 conversions out of 2480 visits
Step 1: Extract counts
import numpy as npfrom statsmodels.stats.proportion import proportions_ztestcount = np.array([200, 260])nobs = np.array([2500, 2480])Step 2: Run the test
stat, p = proportions_ztest(count, nobs)print(stat, p)Step 3: Interpret
p < 0.05→ conversion rates differ- Check proportions:
p_A = 200/2500p_B = 260/2480Step 4: Effect size (Cohen’s h)
def cohens_h(p1, p2): import numpy as np return 2*np.arcsin(np.sqrt(p1)) - 2*np.arcsin(np.sqrt(p2))print(cohens_h(p_A, p_B))Step 5: Confidence intervals
from statsmodels.stats.proportion import proportion_confintprint(proportion_confint(200, 2500))print(proportion_confint(260, 2480))Scenario 2 — Before/After Measurements (Paired Data)
Continuous → Paired t-test or Wilcoxon
Question:
Did a new process improve response time?
Data:
before = [105, 110, 120, 100, 115]after = [98, 100, 109, 95, 102]Step 1: Check normality of differences
from scipy import statsdiff = np.array(after) - np.array(before)stats.shapiro(diff)If:
p ≥ 0.05→ use paired t-testp < 0.05→ use Wilcoxon signed-rank
Step 2 (Option A): Paired t-test
t, p = stats.ttest_rel(before, after)print(t, p)Step 2 (Option B): Wilcoxon (non-parametric)
w, p = stats.wilcoxon(before, after)print(w, p)Step 3: Effect size
def cohens_d_paired(x, y): d = np.array(y) - np.array(x) return np.mean(d) / np.std(d, ddof=1)print(cohens_d_paired(before, after))Scenario 3 — Comparing 3+ Groups (ANOVA or Kruskal–Wallis)
Independent groups → ANOVA or Kruskal-Wallis
Question:
Do three marketing channels differ in average revenue per user?
Data:
group_A = [12, 15, 14, 13]group_B = [18, 17, 19, 16]group_C = [10, 11, 12, 9]groups = [group_A, group_B, group_C]Step 1: Check normality of each group
for g in groups: print(stats.shapiro(g))Step 2: Check equal variances
stats.levene(group_A, group_B, group_C)Choose:
- ANOVA if normal & equal variances
- Kruskal–Wallis if not
ANOVA
f, p = stats.f_oneway(group_A, group_B, group_C)print(f, p)Kruskal–Wallis
h, p = stats.kruskal(group_A, group_B, group_C)print(h, p)Effect size (eta-squared)
def eta_squared_anova(groups): all_data = np.concatenate(groups) grand_mean = np.mean(all_data) ss_between = sum(len(g)*(np.mean(g)-grand_mean)**2 for g in groups) ss_total = sum((x-grand_mean)**2 for x in all_data) return ss_between / ss_totaleta_squared_anova(groups)If significant → run post-hoc tests
Example: Tukey’s HSD (via Statsmodels)
Scenario 4 — Survey / Likert-Scale Analysis
Ordinal data → non-parametric tests
Likert data (1–5 ratings) is ordinal, not interval.
Group example:
satisfaction_A = [4, 5, 4, 3, 5]satisfaction_B = [3, 4, 3, 2, 4]Recommended tests
- 2 groups → Mann–Whitney U
- 3+ groups → Kruskal–Wallis
- Paired → Wilcoxon
Example
stats.mannwhitneyu(satisfaction_A, satisfaction_B)Effect size
# rank-biserial correlationu, p = stats.mannwhitneyu(satisfaction_A, satisfaction_B)rbc = 1 - (2*u)/(len(satisfaction_A)*len(satisfaction_B))print(rbc)Scenario 5 — Categorical Comparisons (Chi-Square)
Two categorical variables → Chi-square or Fisher's
Example:
Does product preference differ by age group?
Data:
table = np.array([ # Young: Prefer A / B [30, 10], # Older: Prefer A / B [20, 40] ])Chi-square test
chi2, p, dof, expected = stats.chi2_contingency(table)print(chi2, p)If expected counts < 5 → Fisher’s Exact
stats.fisher_exact(table)Effect size (Cramér’s V)
def cramers_v(table): chi2, p, dof, expected = stats.chi2_contingency(table) n = table.sum() k = min(table.shape) - 1 return np.sqrt(chi2 / (n * k))print(cramers_v(table))Scenario 6 — Correlation Testing
Question:
Is time spent on site related to revenue?
time_spent = [1,2,3,4,5]revenue = [10,20,25,24,28]Step 1: Check linearity
Plot or eyeball.
Step 2: Choose test
- Pearson → linear
- Spearman → monotonic
Test
corr, p = stats.pearsonr(time_spent, revenue)print(corr, p)Plot
import matplotlib.pyplot as pltplt.scatter(time_spent, revenue)plt.show()