codexbeta-mean-and-concentration.py
beta-mean-and-concentration.py
"""Render fixed-mean beta concentrations to cwd; tested with Python 3.14.4.
Uses only the project's NumPy and Matplotlib dependencies and the standard
library. The density normalization is evaluated using log-gamma arithmetic.
"""
from math import lgamma
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def beta_density(x, alpha, beta):
log_normalizer = lgamma(alpha) + lgamma(beta) - lgamma(alpha + beta)
return np.exp((alpha - 1) * np.log(x)
+ (beta - 1) * np.log1p(-x) - log_normalizer)
def main():
plt.rcParams.update({"font.size": 12, "axes.titlesize": 14})
fig, (density, variance) = plt.subplots(
1, 2, figsize=(11.5, 5.6), dpi=120, facecolor="white")
mean = 0.3
x = np.linspace(0.00001, 0.99999, 6001)
for concentration, color, style in [
(5, "#0072b2", "-"),
(20, "#d55e00", "--"),
(80, "#007c68", "-."),
]:
alpha = mean * concentration
beta = (1 - mean) * concentration
value = mean * (1 - mean) / (concentration + 1)
density.plot(np.r_[0, x, 1],
np.r_[0, beta_density(x, alpha, beta), 0],
color=color, linestyle=style, lw=2.5,
label=f"κ = {concentration}: Beta({alpha:g}, {beta:g})")
variance.scatter(concentration, value, color=color, s=65, zorder=4)
variance.annotate(f"κ = {concentration}\nv = {value:.8g}",
xy=(concentration, value),
xytext=(12 if concentration < 80 else -12, 12),
textcoords="offset points",
ha="left" if concentration < 80 else "right",
fontsize=11, color=color)
density.axvline(mean, color="#596674", lw=1.4, linestyle=":")
density.annotate("Mean = 0.3", xy=(mean, 6.4), xytext=(0.44, 6.4),
fontsize=11, va="center",
arrowprops={"arrowstyle": "->", "color": "#596674"})
density.set(title="Same mean; increasing concentration",
xlabel="Outcome x", ylabel="Probability density f(x)",
xlim=(0, 1), ylim=(0, 8.6))
density.legend(loc="upper right", fontsize=10.5, framealpha=1)
kappa = np.linspace(5, 85, 2001)
variance.plot(kappa, mean * (1 - mean) / (kappa + 1),
color="#596674", lw=2)
variance.text(0.43, 0.61, r"$v=\frac{0.3(1-0.3)}{\kappa+1}$",
transform=variance.transAxes, fontsize=19)
variance.set(title="Spread decreases as κ increases",
xlabel="Concentration κ = α + β", ylabel="Variance v",
xlim=(0, 90), ylim=(0, 0.042))
for ax in [density, variance]:
ax.grid(color="#e6eaed")
ax.spines[["top", "right"]].set_visible(False)
fig.suptitle("Beta mean and concentration: α = 0.3κ, β = 0.7κ", fontsize=16)
fig.text(0.5, 0.035,
"Each density integrates to 1. "
"A taller, narrower curve concentrates probability near the same mean.",
ha="center", fontsize=11)
fig.subplots_adjust(left=0.075, right=0.975, bottom=0.16,
top=0.85, wspace=0.3)
fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()