codexpareto-alpha-quantiles-and-top-share.py
pareto-alpha-quantiles-and-top-share.py
"""Compare exact Pareto quantiles and top shares as alpha varies.
Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7. Run from _media.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import PercentFormatter, ScalarFormatter
def main():
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False})
fig, axes = plt.subplots(1, 2, figsize=(10, 5.5), layout="constrained",
facecolor="white")
alpha = np.linspace(0.75, 4, 700)
ax = axes[0]
for p, color in zip([0.5, 0.9, 0.99], ["#007487", "#a74900", "#6743ad"]):
ax.semilogy(alpha, (1 - p) ** (-1 / alpha), color=color, linewidth=2.3,
label=rf"$p={p:g}$")
ax.set(xlabel=r"Shape $\alpha$", ylabel=r"Quantile / minimum, $Q(p)/\beta$",
title="Quantiles remain finite at every shown shape", ylim=(1, 600))
ax.yaxis.set_major_formatter(ScalarFormatter())
ax.legend(title="Cumulative probability", fontsize=10, loc="upper right")
ax = axes[1]
finite = alpha[alpha > 1]
share = 0.2 ** (1 - 1 / finite)
ax.plot(finite, share, color="#007487", linewidth=2.6)
ax.axvspan(0.75, 1, color="#eeeeee")
ax.text(0.88, 0.66, "Infinite mean\nshare undefined", ha="center", va="center",
rotation=90, fontsize=10)
chosen = np.log(5) / np.log(4)
ax.plot([chosen, chosen], [0, 0.8], color="#a74900", linestyle=":")
ax.plot([0.75, chosen], [0.8, 0.8], color="#a74900", linestyle=":")
ax.scatter([chosen, 2], [0.8, np.sqrt(0.2)], c=["#a74900", "#007487"],
s=38, zorder=4)
ax.annotate(r"80/20: $\alpha=\log 5/\log 4$" + "\n" + r"$\alpha\approx1.160964$",
xy=(chosen, 0.8), xytext=(1.9, 0.91), fontsize=10,
arrowprops={"arrowstyle": "->", "color": "#a74900"})
ax.annotate(r"$\alpha=2$: 44.72%", xy=(2, np.sqrt(0.2)),
xytext=(2.25, 0.56), fontsize=10,
arrowprops={"arrowstyle": "->", "color": "#007487"})
ax.set(xlabel=r"Shape $\alpha$", ylabel="Share of amount in largest 20%",
title=r"$T(0.2)=0.2^{1-1/\alpha}$ requires $\alpha>1$", ylim=(0, 1))
ax.yaxis.set_major_formatter(PercentFormatter(1))
for ax in axes:
for boundary in [1, 2]:
ax.axvline(boundary, color="#777777", linewidth=1, linestyle="--", zorder=0)
ax.set_xlim(0.75, 4)
ax.grid(True, which="major", alpha=0.2)
fig.supxlabel("Dashed boundaries: finite mean for α > 1; finite variance for α > 2.\n"
"Exact theoretical curves with chosen parameters; no observed wealth data.",
fontsize=10)
fig.savefig(Path(__file__).stem + ".png", dpi=140, facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()