"""Two original normalized material-age kernels with the same centroid. Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7. Writes PNG into cwd. The plotted kernels are hypothetical reference-crossing ages, not exposure data. """ from pathlib import Path import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt from matplotlib.patches import Rectangle def moments(windows): """Exact mean and variance for (lower age, upper age, mass fraction).""" assert abs(sum(weight for _, _, weight in windows) - 1.0) < 1e-12 assert all(upper > lower and weight >= 0 for lower, upper, weight in windows) mean = sum(weight * (lower + upper) / 2 for lower, upper, weight in windows) second = sum(weight * (lower**2 + lower * upper + upper**2) / 3 for lower, upper, weight in windows) return mean, second - mean**2 def main(): cases = [([(0, 20, 1.0)], "Uniform material ages", "#3979a8", 0.08), ([(0, 2, 0.5), (18, 20, 0.5)], "Two pooled, separated segments", "#438354", 0.32)] fig, axes = plt.subplots(2, 1, figsize=(7.2, 4.8), dpi=100, sharex=True, facecolor="white") for ax, (windows, label, color, ymax) in zip(axes, cases): ax.set_facecolor("white") mean, variance = moments(windows) for lower, upper, weight in windows: ax.add_patch(Rectangle((lower, 0), upper - lower, weight / (upper - lower), facecolor=color, edgecolor=color, alpha=0.65)) ax.axvline(mean, color="#9a4a35", ls="--", lw=1.7) ax.set(xlim=(-0.5, 20.5), ylim=(0, ymax), ylabel="Mass fraction / day") ax.set_title(f"{label} — centroid 10 days; variance {variance:.2f} days²", fontsize=10) ax.grid(axis="y", alpha=0.18) axes[1].text(10, 0.17, "No sampled material\nat the centroid age", ha="center", fontsize=9, bbox={"facecolor": "white", "edgecolor": "none", "alpha": 0.95}) axes[1].set_xlabel("Material reference-crossing age before collection (days)") axes[1].set_xticks([0, 2, 5, 10, 15, 18, 20]) fig.suptitle("The same centroid can summarize very different material histories", fontsize=11) fig.text(0.5, 0.015, "Original normalized kernels: each shaded distribution has total mass fraction 1.\n" "These are material-age models, not measured exposure histories or clinical dating rules.", ha="center", fontsize=8) fig.tight_layout(rect=(0, 0.07, 1, 0.96)) fig.savefig(Path("nail-kernel-centroid-ambiguity.png"), facecolor="white") plt.close(fig) if __name__ == "__main__": main()