Skip to content

Commit d6a5cb3

Browse files
authored
Merge pull request #94 from snowch/claude/geometric-distribution-visual-vchU8
Add visual formula breakdown for geometric distribution
2 parents 2f5b9fb + 313d23d commit d6a5cb3

1 file changed

Lines changed: 155 additions & 0 deletions

File tree

chapter_07.md

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,161 @@ The formula $(1-p)^{k-1} p$ has an intuitive structure:
877877
This is why the formula captures "trials until first success" - it requires all previous trials to fail and the final trial to succeed.
878878
:::
879879

880+
**Visual example:** Here's how the geometric distribution works with $p=0.4$ (our free throw example):
881+
882+
```{code-cell} ipython3
883+
:tags: [remove-input, remove-output]
884+
885+
import matplotlib.pyplot as plt
886+
from matplotlib.patches import FancyBboxPatch
887+
888+
# --- Parameters ---
889+
p = 0.4
890+
q = 1 - p # 0.6
891+
892+
fig, ax = plt.subplots(figsize=(14, 10), constrained_layout=True)
893+
ax.set_axis_off()
894+
ax.set_xlim(0, 1)
895+
ax.set_ylim(0, 1)
896+
897+
# ---------------- helpers ----------------
898+
def rounded_box(ax, xy, w, h, fc, ec, lw=2, pad=0.012, r=0.02, z=3):
899+
x, y = xy
900+
patch = FancyBboxPatch(
901+
(x, y), w, h,
902+
boxstyle=f"round,pad={pad},rounding_size={r}",
903+
transform=ax.transAxes,
904+
facecolor=fc, edgecolor=ec, linewidth=lw, zorder=z
905+
)
906+
ax.add_patch(patch)
907+
return patch
908+
909+
def draw_sequence(ax, cx, cy, label, k, prob_val):
910+
w, h = 0.16, 0.07
911+
rounded_box(ax, (cx - w/2, cy - h/2), w, h,
912+
fc="lightblue", ec="steelblue", lw=2.2, r=0.02)
913+
914+
ax.text(cx, cy, label, transform=ax.transAxes,
915+
ha="center", va="center",
916+
fontsize=22, weight="bold", family="monospace", zorder=4)
917+
918+
# Formula showing the calculation
919+
if k == 1:
920+
formula_text = rf"${p:.1f}$"
921+
else:
922+
formula_text = rf"${q:.1f}^{{{k-1}}} \times {p:.1f}$"
923+
924+
ax.text(cx, cy - 0.065, formula_text,
925+
transform=ax.transAxes, ha="center", va="top",
926+
fontsize=16, zorder=4)
927+
928+
ax.text(cx, cy - 0.100, rf"$= {prob_val:.4f}$",
929+
transform=ax.transAxes, ha="center", va="top",
930+
fontsize=16, weight="bold", zorder=4)
931+
932+
return (cx - w/2, cy - h/2, cx + w/2, cy + h/2)
933+
934+
# ---------------- layout ----------------
935+
ax.text(0.5, 0.96, rf"Geometric Distribution: Trials Until First Success ($p={p}$)",
936+
transform=ax.transAxes, ha="center", va="top",
937+
fontsize=24, weight="bold", zorder=4)
938+
939+
# Draw sequences at different positions
940+
sequences = [
941+
("S", 1, 0.20, 0.84), # Success on trial 1
942+
("FS", 2, 0.38, 0.84), # Fail then Success
943+
("FFS", 3, 0.56, 0.84), # Fail Fail then Success
944+
("FFFS", 4, 0.74, 0.84), # Fail Fail Fail then Success
945+
("FFFFS", 5, 0.92, 0.84), # Fail Fail Fail Fail then Success
946+
]
947+
948+
seq_boxes = {}
949+
for label, k, x, y in sequences:
950+
prob_val = (q ** (k-1)) * p
951+
seq_boxes[label] = draw_sequence(ax, x, y, label, k, prob_val)
952+
953+
# Explanation box
954+
expl_w, expl_h = 0.70, 0.10
955+
expl_xy = (0.5 - expl_w/2, 0.64 - expl_h/2)
956+
rounded_box(ax, expl_xy, expl_w, expl_h,
957+
fc="lightyellow", ec="orange", lw=2.2, r=0.02)
958+
959+
ax.text(0.5, 0.67, "Each sequence shows trials until first success",
960+
transform=ax.transAxes, ha="center", va="center",
961+
fontsize=18, weight="bold", zorder=4)
962+
ax.text(0.5, 0.625, "Probability decreases exponentially with more failures",
963+
transform=ax.transAxes, ha="center", va="center",
964+
fontsize=16, style="italic", zorder=4)
965+
966+
# Formula block
967+
ax.text(0.5, 0.50, "General Formula:", transform=ax.transAxes,
968+
ha="center", va="center", fontsize=20, weight="bold", zorder=4)
969+
970+
ax.text(0.5, 0.445, r"$P(X=k) = (1-p)^{k-1} \cdot p$",
971+
transform=ax.transAxes, ha="center", va="center", fontsize=20, zorder=4)
972+
973+
ax.text(0.5, 0.39, r"$P(X=k) = (\text{fail})^{k-1} \times \text{succeed}$",
974+
transform=ax.transAxes, ha="center", va="center", fontsize=18, zorder=4)
975+
976+
# Key insight box
977+
key_w, key_h = 0.60, 0.12
978+
key_xy = (0.5 - key_w/2, 0.22 - key_h/2)
979+
rounded_box(ax, key_xy, key_w, key_h,
980+
fc="lightgreen", ec="green", lw=2.2, r=0.02)
981+
982+
ax.text(0.5, 0.255, "Key Insight:",
983+
transform=ax.transAxes, ha="center", va="center",
984+
fontsize=18, weight="bold", zorder=4)
985+
ax.text(0.5, 0.215, rf"All trials before the $k$-th must fail: $(1-p)^{{k-1}}$",
986+
transform=ax.transAxes, ha="center", va="center",
987+
fontsize=16, zorder=4)
988+
ax.text(0.5, 0.180, rf"The $k$-th trial must succeed: $p$",
989+
transform=ax.transAxes, ha="center", va="center",
990+
fontsize=16, zorder=4)
991+
992+
# ---- Callouts ----
993+
# Arrow pointing to decreasing probabilities
994+
x0, y0, x1, y1 = seq_boxes["S"]
995+
ax.annotate("Most likely:\nsucceed early",
996+
xy=((x0 + x1)/2, y0), xycoords=ax.transAxes,
997+
xytext=(0.08, 0.75), textcoords=ax.transAxes,
998+
arrowprops=dict(arrowstyle="->",
999+
connectionstyle="arc3,rad=-0.15",
1000+
lw=2.5, color="green",
1001+
shrinkA=6, shrinkB=8),
1002+
fontsize=15, color="green", weight="bold",
1003+
ha="center", va="center", zorder=5)
1004+
1005+
# Arrow pointing to later trials
1006+
x0, y0, x1, y1 = seq_boxes["FFFFS"]
1007+
ax.annotate("Less likely:\nmany failures",
1008+
xy=((x0 + x1)/2, y0), xycoords=ax.transAxes,
1009+
xytext=(0.92, 0.70), textcoords=ax.transAxes,
1010+
arrowprops=dict(arrowstyle="->",
1011+
connectionstyle="arc3,rad=0.15",
1012+
lw=2.5, color="red",
1013+
shrinkA=6, shrinkB=8),
1014+
fontsize=15, color="red", weight="bold",
1015+
ha="center", va="center", zorder=5)
1016+
1017+
# Bottom explanation
1018+
why = (
1019+
f"Example: P(X=3) means getting your first success on the 3rd trial. "
1020+
f"This requires exactly 2 failures followed by 1 success: "
1021+
f"P(X=3) = (0.6)² × 0.4 = 0.36 × 0.4 = 0.144"
1022+
)
1023+
ax.text(0.5, 0.06, why,
1024+
transform=ax.transAxes, ha="center", va="center",
1025+
fontsize=14, style="italic", wrap=True, zorder=4)
1026+
1027+
plt.savefig('ch07_geometric_formula_breakdown.svg', format='svg', bbox_inches='tight')
1028+
plt.show()
1029+
```
1030+
1031+
![Geometric Formula Breakdown](ch07_geometric_formula_breakdown.svg)
1032+
1033+
The diagram shows how the geometric distribution works: each additional failure before success makes the outcome less likely. The probability decreases exponentially - notice how P(X=1) = 0.4000 is much larger than P(X=5) = 0.0518.
1034+
8801035
**Key Characteristics**
8811036

8821037
- **Scenarios**: Coin flips until first Head, job applications until first offer, attempts to pass an exam, at-bats until first hit

0 commit comments

Comments
 (0)