|
| 1 | +"""Generate four-panel Gaussian/CLT/chi-squared demonstration figure. |
| 2 | +
|
| 3 | +Reproduces the logic of gauss.png (Chapter 11): |
| 4 | + a) Standard normal probability density function with square markers |
| 5 | + b) Histogram of N=1000 draws from N(mu=10, sigma=3) with PDF overlay |
| 6 | + c) Histogram of sample means from 100 repeated trials (CLT demonstration) |
| 7 | + d) Histogram of sample variances from the same trials (chi-squared shape) |
| 8 | +""" |
| 9 | + |
| 10 | +import sys |
| 11 | +from pathlib import Path |
| 12 | + |
| 13 | +import matplotlib.pyplot as plt |
| 14 | +import numpy as np |
| 15 | +from scipy.stats import norm |
| 16 | + |
| 17 | +# Use project figure style |
| 18 | +sys.path.insert(0, str(Path(__file__).parent)) |
| 19 | +from figure_style import apply_mpl_style |
| 20 | + |
| 21 | +apply_mpl_style() |
| 22 | + |
| 23 | + |
| 24 | +# Reproducible random state |
| 25 | +rng = np.random.default_rng(42) |
| 26 | + |
| 27 | +# Parameters matching the original figure description |
| 28 | +mu = 15.0 |
| 29 | +sigma = 3.0 |
| 30 | +n_single = 1000 |
| 31 | +n_trials = 100 |
| 32 | +n_per_trial = 1000 |
| 33 | + |
| 34 | +# --- Generate data --- |
| 35 | +# Panel b: one set of 1000 bed-thickness measurements |
| 36 | +bed_thickness = rng.normal(loc=mu, scale=sigma, size=n_single) |
| 37 | + |
| 38 | +# Panels c & d: 100 repeated trials of 1000 measurements each |
| 39 | +repeated_trials = rng.normal(loc=mu, scale=sigma, size=(n_trials, n_per_trial)) |
| 40 | +trial_means = repeated_trials.mean(axis=1) |
| 41 | +trial_variances = repeated_trials.var(axis=1, ddof=1) |
| 42 | + |
| 43 | +# --- Create figure --- |
| 44 | +fig, axes = plt.subplots(2, 2, figsize=(8, 7)) |
| 45 | + |
| 46 | +# --- Panel a: Standard normal PDF --- |
| 47 | +ax = axes[0, 0] |
| 48 | +z = np.linspace(-4, 4, 500) |
| 49 | +pdf_z = norm.pdf(z) |
| 50 | + |
| 51 | +# Red dashed line with red square markers (matching original) |
| 52 | +z_markers = np.linspace(-3.5, 3.5, 50) |
| 53 | +ax.plot(z, pdf_z, 'r--', lw=1.5) |
| 54 | +ax.plot(z_markers, norm.pdf(z_markers), 's', color='red', |
| 55 | + markersize=4, markeredgecolor='black', markeredgewidth=0.5) |
| 56 | +ax.axvline(0.0, color='grey', lw=0.8, alpha=0.5) |
| 57 | +ax.set_xlim(-4, 4) |
| 58 | +ax.set_ylim(0, 0.42) |
| 59 | +ax.set_xlabel('z') |
| 60 | +ax.set_ylabel('f(z)') |
| 61 | +ax.text(0.05, 0.90, 'a)', transform=ax.transAxes, fontsize=14, |
| 62 | + fontweight='bold') |
| 63 | + |
| 64 | +# --- Panel b: Histogram of bed thickness with normal PDF overlay --- |
| 65 | +ax = axes[0, 1] |
| 66 | +bin_width_b = 0.5 |
| 67 | +bins_b = np.arange(mu - 5 * sigma, mu + 5 * sigma, bin_width_b) |
| 68 | +ax.hist(bed_thickness, bins=bins_b, |
| 69 | + histtype='step', color='black', linewidth=0.8) |
| 70 | + |
| 71 | +x_fit = np.linspace(mu - 4 * sigma, mu + 4 * sigma, 400) |
| 72 | +ax.plot(x_fit, n_single * bin_width_b * norm.pdf(x_fit, loc=mu, scale=sigma), |
| 73 | + 'r--', lw=2) |
| 74 | +ax.set_xlabel('Bed thickness (cm)') |
| 75 | +ax.set_ylabel('Count') |
| 76 | +ax.set_xlim(mu - 4 * sigma, mu + 4 * sigma) |
| 77 | +ax.text(0.05, 0.90, 'b)', transform=ax.transAxes, fontsize=14, |
| 78 | + fontweight='bold') |
| 79 | +ax.text(0.65, 0.90, f'N = {n_single}', transform=ax.transAxes, |
| 80 | + fontsize=11) |
| 81 | + |
| 82 | +# --- Panel c: Histogram of sample means --- |
| 83 | +ax = axes[1, 0] |
| 84 | +sigma_mean = sigma / np.sqrt(n_per_trial) |
| 85 | +bins_c = np.linspace(trial_means.min() - 0.05, trial_means.max() + 0.05, 25) |
| 86 | +bin_width_c = bins_c[1] - bins_c[0] |
| 87 | +ax.hist(trial_means, bins=bins_c, |
| 88 | + histtype='step', color='black', linewidth=0.8) |
| 89 | + |
| 90 | +x_fit_c = np.linspace(trial_means.min() - 3 * sigma_mean, |
| 91 | + trial_means.max() + 3 * sigma_mean, 400) |
| 92 | +ax.plot(x_fit_c, n_trials * bin_width_c * norm.pdf(x_fit_c, loc=mu, scale=sigma_mean), |
| 93 | + 'r--', lw=2) |
| 94 | +ax.set_xlabel('Means of repeat trials') |
| 95 | +ax.set_ylabel('Count') |
| 96 | +ax.text(0.05, 0.90, 'c)', transform=ax.transAxes, fontsize=14, |
| 97 | + fontweight='bold') |
| 98 | +ax.text(0.60, 0.90, f'N = {n_trials}', transform=ax.transAxes, |
| 99 | + fontsize=11) |
| 100 | + |
| 101 | +# --- Panel d: Histogram of sample variances --- |
| 102 | +ax = axes[1, 1] |
| 103 | +bins_d = np.linspace(trial_variances.min(), trial_variances.max(), 25) |
| 104 | +ax.hist(trial_variances, bins=bins_d, |
| 105 | + histtype='step', color='black', linewidth=0.8) |
| 106 | +ax.set_xlabel('Variance') |
| 107 | +ax.set_ylabel('Count') |
| 108 | +ax.text(0.05, 0.90, 'd)', transform=ax.transAxes, fontsize=14, |
| 109 | + fontweight='bold') |
| 110 | +ax.text(0.60, 0.90, f'N = {n_trials}', transform=ax.transAxes, |
| 111 | + fontsize=11) |
| 112 | + |
| 113 | +fig.tight_layout() |
| 114 | + |
| 115 | +outpath = Path(__file__).parent.parent / 'book' / 'figures' / 'chapter11' / 'gauss_code.png' |
| 116 | +fig.savefig(outpath, dpi=300, bbox_inches='tight', facecolor='white') |
| 117 | +print(f'Saved to {outpath}') |
| 118 | +plt.close(fig) |
0 commit comments