{ "cells": [ { "cell_type": "markdown", "id": "6eb2c139", "metadata": {}, "source": [ "# Week 7: Hypothesis Testing and Statistical Tests\n", "\n", "*Kmex Consult — Data Science Fundamentals Course*\n", "\n", "Work through each code cell in order. Edit and re-run to experiment." ] }, { "cell_type": "markdown", "id": "88561668", "metadata": {}, "source": [ "*Data Science Fundamentals Course*" ] }, { "cell_type": "markdown", "id": "0495951e", "metadata": {}, "source": [ "## Week 7 Overview\n", "Hypothesis testing is one of the most important statistical tools in data science. It provides a formal framework for making decisions about populations based on sample data. This week you'll learn how to:\n", "\n", "- Formulate proper hypotheses\n", "- Understand Type I and Type II errors\n", "- Interpret p-values correctly (and avoid common misinterpretations)\n", "- Conduct various types of statistical tests\n", "- Calculate effect sizes\n", "- Make data-driven conclusions with proper evidence\n", "\n", "Hypothesis testing is used everywhere in data science and business:\n", "- A/B testing for website optimization\n", "- Quality control in manufacturing\n", "- Clinical trials for new drugs\n", "- Evaluating marketing campaign effectiveness\n", "- Detecting fraud patterns\n", "\n", "By the end of Week 7, you will be able to:\n", "- Formulate null and alternative hypotheses correctly\n", "- Choose appropriate statistical tests\n", "- Calculate test statistics and p-values\n", "- Interpret results in business context\n", "- Understand Type I/II errors and statistical power\n", "- Calculate and interpret effect sizes\n", "- Report findings professionally\n", "\n", "Week 7 is divided into three 2-hour sessions:\n", "- Session 1: Hypothesis Testing Fundamentals and T-tests\n", "- Session 2: ANOVA and Chi-Square Tests\n", "- Session 3: Non-parametric Tests and Effect Sizes" ] }, { "cell_type": "markdown", "id": "3fcf1079", "metadata": {}, "source": [ "## SESSION 1: Hypothesis Testing Fundamentals and T-tests" ] }, { "cell_type": "markdown", "id": "7217aaed", "metadata": {}, "source": [ "### Duration: 2 hours" ] }, { "cell_type": "markdown", "id": "afcc269f", "metadata": {}, "source": [ "### 1.1 Hypothesis Testing Framework" ] }, { "cell_type": "code", "execution_count": null, "id": "14702ab4", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from scipy import stats\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "print(\"=\"*50)\n", "print(\"HYPOTHESIS TESTING FRAMEWORK\")\n", "print(\"=\"*50)\n", "\n", "print(\"\"\"\n", "THE BASIC IDEA:\n", "We make a claim about a population and test it using sample data.\n", "\n", "STEPS:\n", "1. State hypotheses\n", "2. Choose significance level (α)\n", "3. Calculate test statistic\n", "4. Find p-value\n", "5. Make decision and interpret\n", "\n", "KEY CONCEPTS:\n", "\"\"\")\n", "\n", "print(\"\"\"\n", "NULL HYPOTHESIS (H₀):\n", "- Status quo or no effect\n", "- What we assume is true initially\n", "- We try to find evidence AGAINST it\n", "\n", "ALTERNATIVE HYPOTHESIS (H₁ or Hₐ):\n", "- What we're testing for\n", "- Different from null hypothesis\n", "\n", "SIGNIFICANCE LEVEL (α):\n", "- Probability of rejecting H₀ when it's actually true\n", "- Common values: 0.05, 0.01, 0.10\n", "- 0.05 means 5% chance of Type I error\n", "\n", "P-VALUE:\n", "- Probability of observing data as extreme (or more extreme) if H₀ is true\n", "- Small p-value: strong evidence against H₀\n", "- NOT the probability that H₀ is true!\n", "\n", "DECISION RULE:\n", "- If p-value < α: Reject H₀ (significant result)\n", "- If p-value ≥ α: Fail to reject H₀ (not significant)\n", "\"\"\")\n", "\n", "# Type I and Type II errors\n", "print(\"\\n\" + \"=\"*50)\n", "print(\"TYPE I AND TYPE II ERRORS\")\n", "print(\"=\"*50)\n", "\n", "print(\"\"\"\n", "TYPE I ERROR (α - False Positive):\n", "- Reject H₀ when H₀ is actually true\n", "- \"Crying wolf\" - finding effect that doesn't exist\n", "- Probability = α (significance level)\n", "\n", "TYPE II ERROR (β - False Negative):\n", "- Fail to reject H₀ when H₀ is actually false\n", "- Missing real effect\n", "- Probability = β\n", "\n", "STATISTICAL POWER:\n", "- Power = 1 - β\n", "- Probability of correctly rejecting false H₀\n", "- Typical target: 0.80 (80% power)\n", "\n", "TRUTH TABLE:\n", "H₀ True H₀ False\n", "Reject H₀ Type I Error Correct ✓\n", "Fail to Reject Correct ✓ Type II Error\n", "\"\"\")\n", "\n", "# Example visualization\n", "fig, ax = plt.subplots(figsize=(10, 6))\n", "\n", "# Two normal distributions\n", "x = np.linspace(-4, 4, 1000)\n", "null_dist = stats.norm.pdf(x, 0, 1)\n", "alt_dist = stats.norm.pdf(x, 2, 1)\n", "\n", "ax.plot(x, null_dist, 'b-', linewidth=2, label='H₀ Distribution (μ=0)')\n", "ax.plot(x, alt_dist, 'r-', linewidth=2, label='H₁ Distribution (μ=2)')\n", "\n", "# Critical value at α=0.05\n", "critical_value = stats.norm.ppf(0.95)\n", "ax.axvline(critical_value, color='black', linestyle='--', linewidth=2, label=f'Critical value (α=0.05)')\n", "\n", "# Shade Type I error (α)\n", "x_alpha = x[x >= critical_value]\n", "alpha_area = stats.norm.pdf(x_alpha, 0, 1)\n", "ax.fill_between(x_alpha, alpha_area, alpha=0.3, color='blue', label='Type I Error (α)')\n", "\n", "# Shade Type II error (β)\n", "x_beta = x[x < critical_value]\n", "beta_area = stats.norm.pdf(x_beta, 2, 1)\n", "ax.fill_between(x_beta, beta_area, alpha=0.3, color='red', label='Type II Error (β)')\n", "\n", "ax.set_xlabel('Test Statistic', fontsize=12)\n", "ax.set_ylabel('Probability Density', fontsize=12)\n", "ax.set_title('Type I and Type II Errors', fontsize=14, fontweight='bold')\n", "ax.legend(fontsize=10)\n", "ax.grid(True, alpha=0.3)\n", "plt.tight_layout()\n", "plt.show()\n", "\n", "print(f\"Critical value: {critical_value:.3f}\")\n", "print(f\"Type I error (α): 0.05 (5%)\")\n", "print(f\"Type II error (β): {stats.norm.cdf(critical_value, 2, 1):.3f} (~27%)\")\n", "print(f\"Power (1-β): {1 - stats.norm.cdf(critical_value, 2, 1):.3f} (~73%)\")" ] }, { "cell_type": "markdown", "id": "23e0117c", "metadata": {}, "source": [ "### 1.2 One-Sample T-Test" ] }, { "cell_type": "code", "execution_count": null, "id": "ed5f824e", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from scipy import stats\n", "import pandas as pd\n", "\n", "print(\"=\"*50)\n", "print(\"ONE-SAMPLE T-TEST\")\n", "print(\"=\"*50)\n", "\n", "print(\"\"\"\n", "WHEN TO USE:\n", "- Testing if population mean equals specific value\n", "- Sample size is small (n < 30) or population SD unknown\n", "- Data approximately normally distributed\n", "\n", "HYPOTHESES:\n", "- H₀: μ = μ₀ (mean equals specified value)\n", "- H₁: μ ≠ μ₀ (two-tailed, most common)\n", "- Or: μ > μ₀ (one-tailed, right)\n", "- Or: μ < μ₀ (one-tailed, left)\n", "\"\"\")\n", "\n", "# Example: Factory production\n", "print(\"\\nEXAMPLE: FACTORY PRODUCTION\")\n", "print(\"-\"*50)\n", "\n", "# Production line target: 500g per package\n", "target_weight = 500\n", "\n", "# Sample weights from production\n", "weights = np.array([498, 502, 499, 501, 497, 503, 498, 500, 499, 502,\n", "501, 498, 497, 503, 499, 500, 502, 498, 501, 499])\n", "\n", "print(f\"Target weight: {target_weight}g\")\n", "print(f\"Sample: {weights}\")\n", "print(f\"Sample size: {len(weights)}\")\n", "print(f\"Sample mean: {weights.mean():.2f}g\")\n", "print(f\"Sample std dev: {weights.std(ddof=1):.2f}g\")\n", "\n", "# Conduct one-sample t-test\n", "t_stat, p_value = stats.ttest_1samp(weights, target_weight)\n", "\n", "print(f\"\\nT-Test Results:\")\n", "print(f\"T-statistic: {t_stat:.4f}\")\n", "print(f\"P-value: {p_value:.4f}\")\n", "\n", "# Interpretation\n", "alpha = 0.05\n", "print(f\"\\nSignificance level (α): {alpha}\")\n", "\n", "if p_value < alpha:\n", "print(f\"Result: REJECT H₀ (p={p_value:.4f} < {alpha})\")\n", "print(\"Conclusion: The mean weight is significantly different from 500g\")\n", "else:\n", "print(f\"Result: FAIL TO REJECT H₀ (p={p_value:.4f} ≥ {alpha})\")\n", "print(\"Conclusion: No significant difference from target weight\")\n", "\n", "# Calculate 95% confidence interval\n", "n = len(weights)\n", "se = weights.std(ddof=1) / np.sqrt(n)\n", "t_crit = stats.t.ppf(0.975, df=n-1)\n", "ci_lower = weights.mean() - t_crit * se\n", "ci_upper = weights.mean() + t_crit * se\n", "\n", "print(f\"\\n95% Confidence Interval: [{ci_lower:.2f}, {ci_upper:.2f}]\")\n", "print(f\"Target {target_weight}g is {'INSIDE' if ci_lower <= target_weight <= ci_upper else 'OUTSIDE'} the CI\")\n", "\n", "# Example 2: Test score hypothesis\n", "print(\"\\n\" + \"=\"*50)\n", "print(\"EXAMPLE 2: TEST SCORE ANALYSIS\")\n", "print(\"-\"*50)\n", "\n", "# Hypothesis: Class average is 75\n", "hypothesized_mean = 75\n", "test_scores = np.array([78, 82, 76, 74, 88, 91, 73, 71, 85, 79,\n", "81, 75, 84, 77, 80, 72, 86, 79, 83, 75])\n", "\n", "t_stat, p_value = stats.ttest_1samp(test_scores, hypothesized_mean)\n", "\n", "print(f\"Null hypothesis: μ = {hypothesized_mean}\")\n", "print(f\"Sample mean: {test_scores.mean():.2f}\")\n", "print(f\"Sample size: {len(test_scores)}\")\n", "print(f\"\\nT-statistic: {t_stat:.4f}\")\n", "print(f\"P-value: {p_value:.4f}\")\n", "\n", "if p_value < 0.05:\n", "print(f\"Result: Class average is SIGNIFICANTLY DIFFERENT from {hypothesized_mean}\")\n", "else:\n", "print(f\"Result: Class average is NOT significantly different from {hypothesized_mean}\")\n", "\n", "# Effect size (Cohen's d)\n", "cohens_d = (test_scores.mean() - hypothesized_mean) / test_scores.std(ddof=1)\n", "print(f\"\\nEffect size (Cohen's d): {cohens_d:.3f}\")\n", "print(f\"Interpretation: {'Small' if abs(cohens_d) < 0.5 else 'Medium' if abs(cohens_d) < 0.8 else 'Large'} effect\")" ] }, { "cell_type": "markdown", "id": "32d1ce0e", "metadata": {}, "source": [ "### 1.3 Two-Sample T-Test" ] }, { "cell_type": "code", "execution_count": null, "id": "c6293da4", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from scipy import stats\n", "import pandas as pd\n", "\n", "print(\"=\"*50)\n", "print(\"TWO-SAMPLE T-TEST\")\n", "print(\"=\"*50)\n", "\n", "print(\"\"\"\n", "WHEN TO USE:\n", "- Comparing means of two independent groups\n", "- Testing if two population means are equal\n", "- Small sample sizes or population SD unknown\n", "\n", "HYPOTHESES:\n", "- H₀: μ₁ = μ₂ (means are equal)\n", "- H₁: μ₁ ≠ μ₂ (means are different - two-tailed)\n", "\n", "ASSUMPTIONS:\n", "- Both samples approximately normal\n", "- Variances equal (can test with Levene's test)\n", "- Samples are independent\n", "\"\"\")\n", "\n", "# Example: Treatment effectiveness\n", "print(\"\\nEXAMPLE: DRUG TREATMENT EFFECTIVENESS\")\n", "print(\"-\"*50)\n", "\n", "# Control group (no treatment)\n", "control = np.array([5.2, 4.8, 5.1, 4.9, 5.0, 4.7, 5.3, 4.9, 5.1, 4.8])\n", "\n", "# Treatment group (received drug)\n", "treatment = np.array([3.2, 3.5, 3.1, 3.4, 3.0, 3.6, 3.3, 3.2, 3.4, 3.5])\n", "\n", "print(f\"Control group (n={len(control)}): {control}\")\n", "print(f\"Treatment group (n={len(treatment)}): {treatment}\")\n", "\n", "print(f\"\\nDescriptive Statistics:\")\n", "print(f\"Control mean: {control.mean():.2f}, SD: {control.std(ddof=1):.2f}\")\n", "print(f\"Treatment mean: {treatment.mean():.2f}, SD: {treatment.std(ddof=1):.2f}\")\n", "print(f\"Difference in means: {control.mean() - treatment.mean():.2f}\")\n", "\n", "# Test for equal variances (Levene's test)\n", "stat_levene, p_levene = stats.levene(control, treatment)\n", "print(f\"\\nLevene's Test for Equal Variances:\")\n", "print(f\"P-value: {p_levene:.4f}\")\n", "\n", "equal_var = p_levene > 0.05\n", "print(f\"Assumption: Variances are {'EQUAL' if equal_var else 'NOT EQUAL'}\")\n", "\n", "# Conduct two-sample t-test\n", "t_stat, p_value = stats.ttest_ind(control, treatment, equal_var=equal_var)\n", "\n", "print(f\"\\nTwo-Sample T-Test:\")\n", "print(f\"T-statistic: {t_stat:.4f}\")\n", "print(f\"P-value: {p_value:.6f}\")\n", "\n", "alpha = 0.05\n", "if p_value < alpha:\n", "print(f\"Result: REJECT H₀ (p={p_value:.6f} < {alpha})\")\n", "print(\"Conclusion: Treatment significantly reduces the outcome\")\n", "else:\n", "print(f\"Result: FAIL TO REJECT H₀ (p={p_value:.6f} ≥ {alpha})\")\n", "print(\"Conclusion: No significant difference between groups\")\n", "\n", "# Cohen's d\n", "pooled_std = np.sqrt(((len(control)-1)*control.std(ddof=1)**2 +\n", "(len(treatment)-1)*treatment.std(ddof=1)**2) /\n", "(len(control) + len(treatment) - 2))\n", "cohens_d = (control.mean() - treatment.mean()) / pooled_std\n", "\n", "print(f\"\\nEffect size (Cohen's d): {abs(cohens_d):.3f}\")\n", "print(f\"Interpretation: {'Small' if abs(cohens_d) < 0.5 else 'Medium' if abs(cohens_d) < 0.8 else 'Large'} effect\")\n", "\n", "# Paired t-test example\n", "print(\"\\n\" + \"=\"*50)\n", "print(\"PAIRED T-TEST (Within-subject design)\")\n", "print(\"-\"*50)\n", "\n", "print(\"\"\"\n", "WHEN TO USE:\n", "- Same subjects measured twice (before/after)\n", "- Dependent/correlated samples\n", "- Examples: weight before/after diet, test scores before/after training\n", "\"\"\")\n", "\n", "# Before and after training\n", "before = np.array([65, 72, 68, 71, 70, 73, 69, 74, 66, 68])\n", "after = np.array([72, 78, 74, 76, 77, 79, 75, 81, 72, 75])\n", "\n", "print(f\"\\nBefore training: {before}\")\n", "print(f\"After training: {after}\")\n", "print(f\"Improvements: {after - before}\")\n", "\n", "# Paired t-test\n", "t_stat_paired, p_value_paired = stats.ttest_rel(before, after)\n", "\n", "print(f\"\\nPaired T-Test Results:\")\n", "print(f\"Mean before: {before.mean():.2f}\")\n", "print(f\"Mean after: {after.mean():.2f}\")\n", "print(f\"Mean difference: {(after-before).mean():.2f}\")\n", "print(f\"T-statistic: {t_stat_paired:.4f}\")\n", "print(f\"P-value: {p_value_paired:.6f}\")\n", "\n", "if p_value_paired < 0.05:\n", "print(\"Result: Training SIGNIFICANTLY improved performance\")\n", "else:\n", "print(\"Result: Training did NOT significantly improve performance\")" ] }, { "cell_type": "markdown", "id": "d789b3e9", "metadata": {}, "source": [ "## SESSION 2: ANOVA and Chi-Square Tests" ] }, { "cell_type": "markdown", "id": "efb201b2", "metadata": {}, "source": [ "### Duration: 2 hours" ] }, { "cell_type": "markdown", "id": "68bd2e44", "metadata": {}, "source": [ "### 2.1 Analysis of Variance (ANOVA)" ] }, { "cell_type": "code", "execution_count": null, "id": "a29d964a", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from scipy import stats\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "print(\"=\"*50)\n", "print(\"ONE-WAY ANOVA\")\n", "print(\"=\"*50)\n", "\n", "print(\"\"\"\n", "WHEN TO USE:\n", "- Comparing means across 3+ groups\n", "- Extension of two-sample t-test\n", "- Testing if at least one group mean differs from others\n", "\n", "HYPOTHESES:\n", "- H₀: μ₁ = μ₂ = μ₃ = ... (all means equal)\n", "- H₁: At least one mean differs\n", "\n", "ASSUMPTIONS:\n", "- Each group approximately normal\n", "- Variances equal across groups (test with Levene's)\n", "- Independent observations\n", "\"\"\")\n", "\n", "# Example: Store performance\n", "print(\"\\nEXAMPLE: STORE PERFORMANCE\")\n", "print(\"-\"*50)\n", "\n", "# Sales from three store locations\n", "store_A = np.array([45, 48, 42, 46, 44, 47, 43, 45, 46, 44])\n", "store_B = np.array([52, 55, 53, 54, 51, 56, 52, 54, 53, 55])\n", "store_C = np.array([40, 42, 39, 41, 38, 43, 40, 39, 42, 41])\n", "\n", "print(f\"Store A sales: {store_A}\")\n", "print(f\"Store B sales: {store_B}\")\n", "print(f\"Store C sales: {store_C}\")\n", "\n", "print(f\"\\nDescriptive Statistics:\")\n", "print(f\"Store A: mean={store_A.mean():.2f}, sd={store_A.std(ddof=1):.2f}\")\n", "print(f\"Store B: mean={store_B.mean():.2f}, sd={store_B.std(ddof=1):.2f}\")\n", "print(f\"Store C: mean={store_C.mean():.2f}, sd={store_C.std(ddof=1):.2f}\")\n", "\n", "# Test homogeneity of variance\n", "stat_levene, p_levene = stats.levene(store_A, store_B, store_C)\n", "print(f\"\\nLevene's Test for Equal Variances: p={p_levene:.4f}\")\n", "print(f\"Variances are {'EQUAL' if p_levene > 0.05 else 'NOT EQUAL'}\")\n", "\n", "# Conduct one-way ANOVA\n", "f_stat, p_value = stats.f_oneway(store_A, store_B, store_C)\n", "\n", "print(f\"\\nOne-Way ANOVA Results:\")\n", "print(f\"F-statistic: {f_stat:.4f}\")\n", "print(f\"P-value: {p_value:.6f}\")\n", "\n", "if p_value < 0.05:\n", "print(f\"Result: REJECT H₀\")\n", "print(\"Conclusion: Store performance differs significantly\")\n", "else:\n", "print(f\"Result: FAIL TO REJECT H₀\")\n", "print(\"Conclusion: No significant difference in store performance\")\n", "\n", "# Post-hoc test (pairwise comparisons)\n", "if p_value < 0.05:\n", "print(\"\\n\" + \"-\"*50)\n", "print(\"POST-HOC: PAIRWISE COMPARISONS (T-tests)\")\n", "print(\"-\"*50)\n", "\n", "# Bonferroni correction\n", "alpha_corrected = 0.05 / 3 # 3 pairwise comparisons\n", "\n", "comparisons = [\n", "(\"A vs B\", store_A, store_B),\n", "(\"A vs C\", store_A, store_C),\n", "(\"B vs C\", store_B, store_C)\n", "]\n", "\n", "for name, group1, group2 in comparisons:\n", "t_stat_post, p_post = stats.ttest_ind(group1, group2)\n", "sig = \"***\" if p_post < alpha_corrected else \"\"\n", "print(f\"{name}: t={t_stat_post:.3f}, p={p_post:.4f} {sig}\")\n", "\n", "print(f\"Note: Bonferroni corrected α = {alpha_corrected:.4f}\")\n", "\n", "# Chi-Square Test\n", "print(\"\\n\" + \"=\"*50)\n", "print(\"CHI-SQUARE TEST\")\n", "print(\"=\"*50)\n", "\n", "print(\"\"\"\n", "WHEN TO USE:\n", "- Testing relationships between categorical variables\n", "- Goodness of fit for categorical data\n", "- Independence of two categorical variables\n", "\n", "HYPOTHESES (Independence):\n", "- H₀: Variables are independent\n", "- H₁: Variables are associated\n", "\"\"\")\n", "\n", "# Example: Product preference by gender\n", "print(\"\\nEXAMPLE: PRODUCT PREFERENCE BY GENDER\")\n", "print(\"-\"*50)\n", "\n", "# Contingency table\n", "observed = np.array([\n", "[45, 30], # Male: Product A (45), Product B (30)\n", "[25, 60] # Female: Product A (25), Product B (60)\n", "])\n", "\n", "df_contingency = pd.DataFrame(\n", "observed,\n", "index=['Male', 'Female'],\n", "columns=['Product A', 'Product B']\n", ")\n", "\n", "print(\"Observed frequencies:\")\n", "print(df_contingency)\n", "\n", "# Chi-square test\n", "chi2_stat, p_chi2, dof, expected = stats.chi2_contingency(observed)\n", "\n", "print(f\"\\nExpected frequencies:\")\n", "print(expected)\n", "\n", "print(f\"\\nChi-Square Test Results:\")\n", "print(f\"Chi-square statistic: {chi2_stat:.4f}\")\n", "print(f\"P-value: {p_chi2:.6f}\")\n", "print(f\"Degrees of freedom: {dof}\")\n", "\n", "if p_chi2 < 0.05:\n", "print(f\"Result: REJECT H₀\")\n", "print(\"Conclusion: Product preference is associated with gender\")\n", "else:\n", "print(f\"Result: FAIL TO REJECT H₀\")\n", "print(\"Conclusion: No association between gender and product preference\")\n", "\n", "# Cramér's V (effect size)\n", "n = observed.sum()\n", "min_dim = min(observed.shape) - 1\n", "cramers_v = np.sqrt(chi2_stat / (n * min_dim))\n", "print(f\"\\nEffect size (Cramér's V): {cramers_v:.3f}\")\n", "print(f\"Interpretation: {'Negligible' if cramers_v < 0.1 else 'Small' if cramers_v < 0.3 else 'Medium' if cramers_v < 0.5 else 'Large'} effect\")" ] }, { "cell_type": "markdown", "id": "135acf36", "metadata": {}, "source": [ "## SESSION 3: Non-parametric Tests and Effect Sizes" ] }, { "cell_type": "markdown", "id": "47fb67cd", "metadata": {}, "source": [ "### Duration: 2 hours" ] }, { "cell_type": "markdown", "id": "89181bab", "metadata": {}, "source": [ "### 3.1 Non-Parametric Tests" ] }, { "cell_type": "code", "execution_count": null, "id": "c807da42", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from scipy import stats\n", "import pandas as pd\n", "\n", "print(\"=\"*50)\n", "print(\"NON-PARAMETRIC TESTS\")\n", "print(\"=\"*50)\n", "\n", "print(\"\"\"\n", "WHEN TO USE:\n", "- Data not normally distributed\n", "- Small sample sizes\n", "- Ordinal or ranked data\n", "- Violate parametric test assumptions\n", "\n", "NON-PARAMETRIC ALTERNATIVES:\n", "- t-test → Mann-Whitney U test (Wilcoxon rank-sum)\n", "- Paired t-test → Wilcoxon signed-rank test\n", "- ANOVA → Kruskal-Wallis test\n", "\"\"\")\n", "\n", "# Mann-Whitney U Test\n", "print(\"\\nMANN-WHITNEY U TEST (Non-parametric alternative to two-sample t-test)\")\n", "print(\"-\"*50)\n", "\n", "print(\"\"\"\n", "WHEN TO USE:\n", "- Two independent groups\n", "- Data not normally distributed or unknown distribution\n", "- Ordinal data or small samples\n", "\"\"\")\n", "\n", "# Example: Customer satisfaction (non-normal data)\n", "old_method = np.array([3, 2, 4, 2, 3, 1, 3, 2, 4, 1]) # Ranks 1-5\n", "new_method = np.array([5, 4, 5, 4, 4, 5, 3, 4, 5, 4])\n", "\n", "print(f\"\\nOld method satisfaction: {old_method}\")\n", "print(f\"New method satisfaction: {new_method}\")\n", "\n", "# Mann-Whitney U test\n", "u_stat, p_mann_whitney = stats.mannwhitneyu(old_method, new_method, alternative='two-sided')\n", "\n", "print(f\"\\nMann-Whitney U Test:\")\n", "print(f\"U-statistic: {u_stat:.4f}\")\n", "print(f\"P-value: {p_mann_whitney:.6f}\")\n", "\n", "if p_mann_whitney < 0.05:\n", "print(\"Result: Methods have SIGNIFICANTLY different satisfaction ratings\")\n", "else:\n", "print(\"Result: No significant difference in satisfaction ratings\")\n", "\n", "# Wilcoxon Signed-Rank Test\n", "print(\"\\n\" + \"=\"*50)\n", "print(\"WILCOXON SIGNED-RANK TEST (Non-parametric paired t-test)\")\n", "print(\"-\"*50)\n", "\n", "print(\"\"\"\n", "WHEN TO USE:\n", "- Paired/dependent samples\n", "- Data not normally distributed\n", "- Ordinal or ranked data\n", "\"\"\")\n", "\n", "# Before and after (ordinal data)\n", "before = np.array([2, 3, 2, 1, 3, 2, 1, 2, 3, 2])\n", "after = np.array([4, 5, 4, 3, 5, 4, 3, 4, 5, 4])\n", "\n", "print(f\"\\nBefore: {before}\")\n", "print(f\"After: {after}\")\n", "print(f\"Differences: {after - before}\")\n", "\n", "# Wilcoxon signed-rank test\n", "w_stat, p_wilcoxon = stats.wilcoxon(before, after)\n", "\n", "print(f\"\\nWilcoxon Signed-Rank Test:\")\n", "print(f\"W-statistic: {w_stat:.4f}\")\n", "print(f\"P-value: {p_wilcoxon:.6f}\")\n", "\n", "if p_wilcoxon < 0.05:\n", "print(\"Result: Significant improvement after intervention\")\n", "else:\n", "print(\"Result: No significant improvement\")\n", "\n", "# Kruskal-Wallis Test\n", "print(\"\\n\" + \"=\"*50)\n", "print(\"KRUSKAL-WALLIS TEST (Non-parametric ANOVA)\")\n", "print(\"-\"*50)\n", "\n", "print(\"\"\"\n", "WHEN TO USE:\n", "- Three or more groups\n", "- Data not normally distributed\n", "- Ordinal data or unknown distribution\n", "\"\"\")\n", "\n", "# Example: Customer ratings for three products\n", "product_A = np.array([2, 3, 2, 1, 3, 2, 4, 2, 3, 1])\n", "product_B = np.array([4, 5, 4, 3, 5, 4, 5, 4, 5, 3])\n", "product_C = np.array([3, 4, 3, 2, 4, 3, 4, 3, 4, 2])\n", "\n", "print(f\"\\nProduct A ratings: {product_A}\")\n", "print(f\"Product B ratings: {product_B}\")\n", "print(f\"Product C ratings: {product_C}\")\n", "\n", "# Kruskal-Wallis test\n", "h_stat, p_kruskal = stats.kruskal(product_A, product_B, product_C)\n", "\n", "print(f\"\\nKruskal-Wallis Test:\")\n", "print(f\"H-statistic: {h_stat:.4f}\")\n", "print(f\"P-value: {p_kruskal:.6f}\")\n", "\n", "if p_kruskal < 0.05:\n", "print(\"Result: Products have SIGNIFICANTLY different ratings\")\n", "else:\n", "print(\"Result: No significant difference in product ratings\")" ] }, { "cell_type": "markdown", "id": "9562292c", "metadata": {}, "source": [ "### 3.2 Effect Sizes and Practical Significance" ] }, { "cell_type": "code", "execution_count": null, "id": "3c10e411", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from scipy import stats\n", "import pandas as pd\n", "\n", "print(\"=\"*50)\n", "print(\"EFFECT SIZES\")\n", "print(\"=\"*50)\n", "\n", "print(\"\"\"\n", "WHY EFFECT SIZE MATTERS:\n", "- P-value tells if effect exists (significant)\n", "- Effect size tells how LARGE the effect is\n", "- Large sample can detect tiny (insignificant) effects\n", "- Small sample might miss large (important) effects\n", "\n", "PRACTICAL vs STATISTICAL SIGNIFICANCE:\n", "- Statistical significance: Effect exists (p < 0.05)\n", "- Practical significance: Effect is large enough to matter\n", "\"\"\")\n", "\n", "# Cohen's d\n", "print(\"\\n\" + \"=\"*50)\n", "print(\"COHEN'S D (for comparing means)\")\n", "print(\"=\"*50)\n", "\n", "print(\"\"\"\n", "INTERPRETATION:\n", "- d = 0.2: Small effect\n", "- d = 0.5: Medium effect\n", "- d = 0.8: Large effect\n", "\n", "CALCULATION:\n", "Cohen's d = (Mean₁ - Mean₂) / Pooled SD\n", "\"\"\")\n", "\n", "# Example\n", "group1 = np.array([85, 88, 82, 90, 86, 84, 87, 89, 83, 85])\n", "group2 = np.array([78, 80, 75, 82, 79, 77, 81, 83, 76, 79])\n", "\n", "mean1, mean2 = group1.mean(), group2.mean()\n", "sd1, sd2 = group1.std(ddof=1), group2.std(ddof=1)\n", "n1, n2 = len(group1), len(group2)\n", "\n", "pooled_sd = np.sqrt(((n1-1)*sd1**2 + (n2-1)*sd2**2) / (n1+n2-2))\n", "cohens_d = (mean1 - mean2) / pooled_sd\n", "\n", "print(f\"\\nGroup 1: mean={mean1:.2f}, sd={sd1:.2f}\")\n", "print(f\"Group 2: mean={mean2:.2f}, sd={sd2:.2f}\")\n", "print(f\"\\nCohen's d = ({mean1:.2f} - {mean2:.2f}) / {pooled_sd:.2f}\")\n", "print(f\"Cohen's d = {cohens_d:.3f}\")\n", "\n", "effect_interpretation = \"Small\" if abs(cohens_d) < 0.5 else \"Medium\" if abs(cohens_d) < 0.8 else \"Large\"\n", "print(f\"\\nInterpretation: {effect_interpretation} effect\")\n", "\n", "# Other effect sizes\n", "print(\"\\n\" + \"=\"*50)\n", "print(\"OTHER EFFECT SIZES\")\n", "print(\"=\"*50)\n", "\n", "print(\"\"\"\n", "R² (Coefficient of Determination):\n", "- Proportion of variance explained\n", "- 0 to 1 (0% to 100%)\n", "- Small: 0.01, Medium: 0.06, Large: 0.14\n", "\n", "Cramér's V (for chi-square):\n", "- Small: 0.1, Medium: 0.3, Large: 0.5\n", "\n", "Eta² (for ANOVA):\n", "- Proportion of variance between groups\n", "- Similar scale to R²\n", "\n", "Odds Ratio:\n", "- For categorical data\n", "- Ratio of odds in two groups\n", "\"\"\")\n", "\n", "# Example: R-squared\n", "print(\"\\nEXAMPLE: R-SQUARED FOR CORRELATION\")\n", "print(\"-\"*50)\n", "\n", "x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\n", "y = 2*x + np.random.normal(0, 2, 10)\n", "\n", "correlation = np.corrcoef(x, y)[0, 1]\n", "r_squared = correlation ** 2\n", "\n", "print(f\"Correlation coefficient (r): {correlation:.3f}\")\n", "print(f\"R-squared: {r_squared:.3f}\")\n", "print(f\"Interpretation: {r_squared*100:.1f}% of variance in y explained by x\")\n", "\n", "# Reporting results\n", "print(\"\\n\" + \"=\"*50)\n", "print(\"HOW TO REPORT RESULTS\")\n", "print(\"=\"*50)\n", "\n", "print(\"\"\"\n", "COMPLETE RESULT REPORT SHOULD INCLUDE:\n", "\n", "1. Test name and type (one-tailed vs two-tailed)\n", "2. Sample descriptive statistics\n", "3. Test statistic and value\n", "4. Degrees of freedom (if applicable)\n", "5. P-value (exact if p > 0.001, otherwise p < 0.001)\n", "6. Effect size\n", "7. Confidence interval\n", "8. Conclusion\n", "\n", "EXAMPLE REPORT:\n", "\"A two-sample t-test comparing treatment group (M=85.2, SD=4.3, n=10)\n", "with control group (M=79.8, SD=5.1, n=10) revealed a significant difference,\n", "t(18)=2.84, p=0.011, d=1.01. The 95% CI [3.12, 12.64] does not include zero,\n", "indicating treatment significantly improved outcomes with a large effect size.\"\n", "\"\"\")\n", "\n", "# Real-world reporting\n", "print(\"\\n\" + \"-\"*50)\n", "print(\"REAL-WORLD EXAMPLE\")\n", "print(\"-\"*50)\n", "\n", "data1 = np.array([45, 48, 42, 46, 44, 47, 43, 45])\n", "data2 = np.array([52, 55, 53, 54, 51, 56, 52, 54])\n", "\n", "t_stat, p_value = stats.ttest_ind(data1, data2)\n", "n1, n2 = len(data1), len(data2)\n", "mean1, mean2 = data1.mean(), data2.mean()\n", "sd1, sd2 = data1.std(ddof=1), data2.std(ddof=1)\n", "\n", "pooled_sd = np.sqrt(((n1-1)*sd1**2 + (n2-1)*sd2**2) / (n1+n2-2))\n", "cohens_d = (mean1 - mean2) / pooled_sd\n", "\n", "se = pooled_sd * np.sqrt(1/n1 + 1/n2)\n", "df = n1 + n2 - 2\n", "t_crit = stats.t.ppf(0.975, df)\n", "ci_lower = (mean1 - mean2) - t_crit * se\n", "ci_upper = (mean1 - mean2) + t_crit * se\n", "\n", "report = f\"\"\"\n", "PROFESSIONAL REPORT:\n", "A two-sample t-test comparing Method A (M={mean1:.1f}, SD={sd1:.2f}, n={n1})\n", "with Method B (M={mean2:.1f}, SD={sd2:.2f}, n={n2}) revealed a significant\n", "difference, t({df})={t_stat:.2f}, p={p_value:.4f}, d={cohens_d:.2f}.\n", "The 95% CI [{ci_lower:.1f}, {ci_upper:.1f}] does not include zero.\n", "Method B shows significantly better performance with a large effect size.\n", "\"\"\"\n", "\n", "print(report)" ] }, { "cell_type": "markdown", "id": "3992ca32", "metadata": {}, "source": [ "#### Hypothesis Testing Checklist\n", "- Define H₀ and H₁ clearly before analyzing data\n", "- Choose significance level (α) in advance\n", "- Check test assumptions before conducting test\n", "- Select appropriate test based on:\n", "- Number of groups (1, 2, or 3+)\n", "- Independence of samples\n", "- Data distribution normality\n", "- Data type (continuous vs categorical)\n", "- Report: test statistic, p-value, effect size, CI\n", "- Interpret p-value correctly (NOT probability H₀ is true)\n", "- Consider practical significance, not just statistical\n", "- Report effect size even if not significant\n", "- Use confidence intervals alongside p-values" ] }, { "cell_type": "markdown", "id": "30214be5", "metadata": {}, "source": [ "## Week 7 Summary\n", "By completing Week 7, you have learned:\n", "- Hypothesis testing framework: H₀, H₁, α, p-value\n", "- Type I error (α): False positive rate\n", "- Type II error (β): False negative rate\n", "- Statistical power: 1 - β\n", "- Interpreting p-values correctly\n", "- One-sample t-test: testing against a hypothesized mean\n", "- Two-sample t-test: comparing two independent groups\n", "- Paired t-test: comparing dependent/related samples\n", "- Checking assumptions: normality, equal variances\n", "- One-way ANOVA: comparing 3+ groups\n", "- Post-hoc tests: pairwise comparisons after ANOVA\n", "- Chi-square test: independence of categorical variables\n", "- Contingency tables and frequency analysis\n", "- Mann-Whitney U test: non-parametric alternative to t-test\n", "- Wilcoxon signed-rank test: non-parametric paired test\n", "- Kruskal-Wallis test: non-parametric ANOVA\n", "- Effect sizes: Cohen's d, R², Cramér's V\n", "- Practical vs statistical significance\n", "- Professional reporting of results" ] }, { "cell_type": "markdown", "id": "24fe766b", "metadata": {}, "source": [ "## Week 7 Assignments" ] }, { "cell_type": "markdown", "id": "d1a724f0", "metadata": {}, "source": [ "### Assignment 1: Hypothesis Testing Scenarios\n", "Conduct hypothesis tests on provided scenarios:\n", "- Formulate appropriate null and alternative hypotheses\n", "- Choose correct statistical test with justification\n", "- Check test assumptions\n", "- Conduct test and report results\n", "- Interpret p-value and effect size\n", "- Make business recommendations based on findings\n", "- Report: 5 different hypothesis tests with full analysis" ] }, { "cell_type": "markdown", "id": "c0d55d8a", "metadata": {}, "source": [ "### Assignment 2: A/B Testing Analysis\n", "Analyze an A/B test (real or simulated):\n", "- Define control and treatment groups\n", "- Calculate descriptive statistics for each group\n", "- Conduct appropriate statistical test\n", "- Calculate effect size and confidence interval\n", "- Determine statistical and practical significance\n", "- Create visualizations of results\n", "- Write business report with recommendations" ] }, { "cell_type": "markdown", "id": "cdcd8df0", "metadata": {}, "source": [ "### Assignment 3: Comprehensive Statistical Analysis\n", "Perform complete statistical analysis on real dataset:\n", "- Test assumptions for different groups\n", "- Conduct multiple hypothesis tests:\n", "- At least 1 parametric test (t-test or ANOVA)\n", "- At least 1 categorical test (chi-square)\n", "- At least 1 non-parametric test if needed\n", "- Calculate all effect sizes\n", "- Create professional report with:\n", "- Summary of findings\n", "- Statistical evidence\n", "- Practical implications\n", "- Business recommendations\n", "- Appropriate visualizations" ] }, { "cell_type": "markdown", "id": "4b474eb9", "metadata": {}, "source": [ "## Recommended Practice Problems\n", "- Conduct t-tests with different sample sizes and observe p-value changes\n", "- Calculate statistical power for different effect sizes\n", "- Compare parametric vs non-parametric test results on same data\n", "- Perform post-hoc tests after ANOVA with different correction methods\n", "- Analyze contingency tables and interpret associations\n", "- Design A/B tests with specified power and effect size\n", "- Report results using proper APA format\n", "- Create visualizations for different test results\n", "- Calculate sample size needed for desired power" ] }, { "cell_type": "markdown", "id": "8c4195bf", "metadata": {}, "source": [ "## Additional Resources" ] }, { "cell_type": "markdown", "id": "7c25d4a7", "metadata": {}, "source": [ "### Books\n", "- Chapter 7 (continued): Statistical Inference - "Python for Data Analysis" by Wes McKinney\n", "- Statistics Done Wrong by Alex Reinhart\n", "- The Book of Why by Judea Pearl - Understanding causality" ] }, { "cell_type": "markdown", "id": "3d8d2d79", "metadata": {}, "source": [ "### Online Resources\n", "- scipy.stats documentation: https://docs.scipy.org/doc/scipy/reference/stats.html\n", "- StatQuest with Josh Starmer (YouTube): Excellent visual explanations\n", "- P-value explained: https://www.nature.com/articles/506150a\n", "- Effect size guide: https://en.wikipedia.org/wiki/Effect_size" ] }, { "cell_type": "markdown", "id": "1c6b9ede", "metadata": {}, "source": [ "### Tools and Calculators\n", "- G*Power: Statistical power analysis calculator\n", "- Stat Trek: Online statistics calculators\n", "- Real Statistics Resource Pack for Excel" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.11" } }, "nbformat": 4, "nbformat_minor": 5 }