codexgumbel-limit-exponential-maxima.py
gumbel-limit-exponential-maxima.py
"""Plot normalized exponential maxima and their Gumbel limit; PNG basename to CWD.
Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def normalized_cdf(z, n):
"""Use the shifted support, avoiding powers of a negative CDF base."""
z = np.asarray(z, dtype=float)
shifted = np.maximum(z + np.log(n), 0.0)
return (-np.expm1(-shifted)) ** n
def gumbel_cdf(z):
return np.exp(-np.exp(-np.asarray(z, dtype=float)))
def main():
plt.rcParams.update({"font.size": 12, "figure.facecolor": "white"})
fig, ax = plt.subplots(figsize=(9.2, 5.6), dpi=100)
z = np.linspace(-4.5, 6, 2801)
for n, color, style in zip((1, 5, 20, 100),
("#bf5b20", "#2674af", "#8862ad", "#34804a"),
("-", "--", "-.", "-")):
ax.plot(z, normalized_cdf(z, n), color=color, linestyle=style,
linewidth=2.2, label=rf"Exact, $n={n}$")
ax.plot(z, gumbel_cdf(z), color="#222222", linestyle=(0, (4, 2)),
linewidth=2.8, label=r"Gumbel limit $\exp(-e^{-z})$")
median = -np.log(np.log(2))
ax.scatter([median], [0.5], color="#222222", s=43, zorder=6)
ax.annotate("Limit median\n0.366513", xy=(median, 0.5), xytext=(2.2, 0.43),
fontsize=11, ha="left", va="center",
arrowprops={"arrowstyle": "->", "color": "#444444"})
ax.set(xlim=(-4.5, 6), ylim=(0, 1.025),
xlabel=r"Normalized threshold $z$ (dimensionless)",
ylabel=r"Cumulative probability $P(Z_n\leq z)$")
ax.legend(loc="upper left", frameon=True, framealpha=0.96, fontsize=10.5)
ax.grid(alpha=0.18)
ax.spines[["top", "right"]].set_visible(False)
fig.suptitle("Subtract the logarithmic growth: one limiting shape remains",
fontsize=15, weight="bold", y=0.96)
ax.set_title(r"$Z_n=\lambda M_n-\log n$ for independent exponential observations",
fontsize=12, pad=13)
fig.text(0.5, 0.025, r"Finite-sample CDFs are zero below $-\log n$; the limit has no finite lower boundary.",
ha="center", fontsize=11)
fig.subplots_adjust(left=0.105, right=0.97, bottom=0.15, top=0.80)
fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()