codexgeometric-distribution-conditional-residual.py
geometric-distribution-conditional-residual.py
"""Show conditioning and resetting a geometric waiting count; PNG in CWD.
Tested with Python 3.14, matplotlib 3.10.7, numpy 2.3.5 (pyproject.toml).
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
p = 0.3
q = 1 - p
m = 5
k = np.arange(1, 16)
r = np.arange(1, 11)
original = p * q ** (k - 1)
residual = p * q ** (m + r - 1) / q ** m
fresh = p * q ** (r - 1)
assert np.allclose(residual, fresh)
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False})
fig, axes = plt.subplots(1, 2, figsize=(10, 4.9), dpi=110,
sharey=True, facecolor="white")
axes[0].bar(k[:m], original[:m], color="#d0d0d0", width=0.72,
label="Excluded by T > 5")
axes[0].bar(k[m:], original[m:], color="#256393", width=0.72,
label="Original mass at k > 5")
axes[0].axvline(m + 0.5, color="#555555", linestyle="--", linewidth=1)
axes[0].set_xticks([1, 3, 5, 6, 8, 10, 12, 15])
axes[0].set_xlabel("Original first-success attempt k")
axes[0].set_ylabel("Probability mass")
axes[0].set_title("Keep only outcomes after five failures", pad=12)
axes[0].legend(loc="upper right", frameon=False, fontsize=9)
axes[1].bar(r, residual, color="#ba6021", width=0.7,
label="P(T − 5 = r | T > 5)")
axes[1].plot(r, fresh, "o", markerfacecolor="white", markeredgecolor="#256393",
markeredgewidth=1.6, markersize=6, linestyle="none",
label="Fresh P(T = r)")
axes[1].set_xticks(r)
axes[1].set_xlabel("Additional attempts r = k − 5")
axes[1].set_title("Divide by P(T > 5), then reset the count", pad=12)
axes[1].legend(loc="upper right", frameon=False, fontsize=9)
for ax in axes:
ax.set_facecolor("white")
ax.set_ylim(0, 0.40)
ax.set_yticks(np.arange(0, 0.41, 0.1))
ax.grid(axis="y", alpha=0.22)
ax.set_axisbelow(True)
fig.suptitle("Five failures leave the same remaining-count law (p = 0.3)",
fontsize=15, y=0.97)
fig.text(0.5, 0.12,
"Conditioning event: P(T > 5) = 0.7⁵ = 0.16807 (includes all k > 5).",
ha="center", fontsize=11)
fig.text(0.5, 0.065,
"At r = 1: 0.050421 ÷ 0.16807 = 0.30. At r = 2: the mass is 0.21.",
ha="center", fontsize=11)
fig.text(0.5, 0.017,
"Panels truncate the infinite tails; the conditional law is normalized using the full tail.",
ha="center", fontsize=10, color="#444444")
fig.subplots_adjust(left=0.075, right=0.98, bottom=0.28, top=0.79, wspace=0.15)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()