codexquantile-generalized-inverse.py
quantile-generalized-inverse.py
"""Show generalized inverse CDFs with jump conventions; output a PNG to cwd."""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def quantile_x(p):
return np.array([0., 1., 3.])[np.searchsorted([.2, .7, 1.], p, side="left")]
def main():
levels = np.array([.1, .5, .9])
assert np.array_equal(quantile_x(levels), [0, 1, 3])
assert quantile_x(.2) == 0 and quantile_x(.7) == 1
# A deterministic uniform grid illustrates probability-band widths, not IID sampling.
grid = (np.arange(1000) + .5) / 1000
counts = [np.count_nonzero(quantile_x(grid) == outcome) for outcome in [0, 1, 3]]
assert counts == [200, 500, 300]
assert np.allclose(np.sqrt(levels)**2, levels)
assert np.isclose(np.sqrt(.5), 1/np.sqrt(2))
plt.rcParams.update({"font.size": 12, "axes.titlesize": 14})
fig, (discrete, continuous) = plt.subplots(1, 2, figsize=(11, 5.5), dpi=120, facecolor="white")
colors = ["#396c99", "#298268", "#b67a2e"]
bands = [(0, .2, 0), (.2, .7, 1), (.7, 1, 3)]
for (left, right, outcome), color in zip(bands, colors):
discrete.axvspan(left, right, color=color, alpha=.08)
discrete.plot([left, right], [outcome, outcome], color=color, lw=3)
discrete.scatter([left], [outcome], facecolor="white", edgecolor=color, s=55, zorder=5)
discrete.scatter([right], [outcome], facecolor=color if right < 1 else "white",
edgecolor=color, s=55, zorder=5)
discrete.text((left+right)/2, 3.55, f"Width {right-left:.1f}", ha="center", color=color, fontsize=11)
for level, output in zip(levels, quantile_x(levels)):
discrete.plot([level, level], [-.32, output], color="#a0aeb8", linestyle=":", lw=1)
discrete.scatter([level], [output], color="#283a46", s=42, zorder=6)
discrete.set(xlim=(-.025, 1.025), ylim=(-.38, 3.9), title="Quantiles of the three-value law",
xlabel="Probability level p", ylabel=r"Outcome $Q_X(p)$")
discrete.set_yticks([0, 1, 3])
p = np.linspace(0, 1, 800)
continuous.plot(p, np.sqrt(p), color="#298268", lw=3)
for level, output, color in zip(levels, np.sqrt(levels), colors):
continuous.plot([level, level], [0, output], color="#a0aeb8", linestyle=":", lw=1)
continuous.scatter([level], [output], color=color, s=60, zorder=5)
continuous.scatter([0, 1], [0, 1], facecolor="white", edgecolor="#298268", s=55, zorder=6)
continuous.annotate(r"$Q_Y(0.5)=1/\sqrt{2}$" + "\n≈ 0.70710678", xy=(.5, np.sqrt(.5)),
xytext=(.04, .87), fontsize=12, color="#20614c",
arrowprops={"arrowstyle": "->", "color": "#20614c"},
bbox={"facecolor": "white", "edgecolor": "none", "alpha": .9})
continuous.set(xlim=(-.025, 1.025), ylim=(-.04, 1.08), title=r"For $F_Y(y)=y^2$: $Q_Y(p)=\sqrt{p}$",
xlabel="Probability level p", ylabel=r"Outcome $Q_Y(p)$")
for ax in (discrete, continuous):
ax.set_xticks([0, .1, .2, .5, .7, .9, 1])
ax.grid(color="#e6eaed")
ax.spines[["top", "right"]].set_visible(False)
fig.suptitle("Quantiles choose the first outcome reaching a probability level", fontsize=16, y=.985)
fig.text(.5, .042, "A uniform draw lands in each probability band with that band's width",
ha="center", fontsize=12)
fig.subplots_adjust(left=.065, right=.98, bottom=.20, top=.82, wspace=.25)
fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()