{
"cells": [
{
"cell_type": "markdown",
"id": "81d76ce2",
"metadata": {},
"source": [
"# Histogram\n",
"\n",
"This example uses the `morph()` animation with `sequential=True` to animate bins appearing gradually, and then reveal an approximate gaussian curve of the distribution. And it uses the XKCD style because why not."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "eebf5819",
"metadata": {},
"outputs": [],
"source": [
"import diplotocus as dpl\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"N = 5#Number of dice\n",
"l = 10#Number of faces per die\n",
"m = 10_000#Number of throws for each die\n",
"\n",
"plt.xkcd()\n",
"tl = dpl.Timeline(xlim=(0,50),ylim=(0,0.075),bbox_inches='tight')\n",
"tl.ax.set_xlabel(f'Sum of {N}, {l}-faced dice averaged over {m} throws')\n",
"tl.ax.set_ylabel('Probability density')\n",
"\n",
"#We generate dice throws and add up their result to get our distribution\n",
"throws = np.random.randint(1,l,size=(m,N))\n",
"sum = np.sum(throws,axis=1)\n",
"\n",
"#We use numpy's histogram() function and plot the result with dpl.bar(), as it lets us animate\n",
"#the height of bars independently.\n",
"hist,edges = np.histogram(sum,bins=l*N,range=(1,N*l),density=True)\n",
"x = (edges[1:]+edges[:-1])/2\n",
"\n",
"h = dpl.bar(x=x,height=0,width=(edges[1]-edges[0])/2)\n",
"h.morph(new_x=x,new_height=hist,duration=60,sequential=True,easing=dpl.easeInOutCubic())\n",
"\n",
"#We then compute and plot a gaussian curve approximating our distribution.\n",
"x = np.linspace(0,50,50)\n",
"mu = N*l/2\n",
"sigma = np.sqrt(N*(l-1)**2/12)\n",
"y = 1/(sigma*np.sqrt(2*np.pi))*np.exp(-(x-mu)**2/(2*sigma**2))\n",
"\n",
"p = dpl.plot(x,y,c='C1')\n",
"p.draw(duration=60,delay=60)\n",
"\n",
"tl.animate((h,p))\n",
"tl.save_video('../../_static/examples/hist.mp4')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "577bd7d9",
"metadata": {
"tags": [
"remove-input"
]
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from IPython.display import HTML, display\n",
"display(HTML(\"\"\"\n",
" \n",
"\"\"\"))"
]
},
{
"cell_type": "markdown",
"id": "bc5d3f66",
"metadata": {},
"source": [
"For those curious, here's the math for the gaussian curve approximation:\n",
"\n",
"We throw $N$ dice that each have $l$ labeled from 1 to $l$.\n",
"\n",
"For one die, the expected distribution is:\n",
"\n",
"$$p = \\lceil\\mathcal{U(1,l)}\\rceil$$\n",
"\n",
"For $N$ dice, we convolve each distribution $p$:\n",
"\n",
"$$P_N = \\underbrace{p \\ast p \\ast \\ldots \\ast p}_{N}$$\n",
"\n",
"By the Central Limit Theorem, we have $\\lim_{N\\rightarrow\\infty}P_N = \\mathcal{N}(\\mu,\\sigma)$\n",
"\n",
"Trivially, the mean of this distribution is $\\mu = \\frac{N\\cdot l}{2}$.\n",
"\n",
"For $\\sigma$, we know that the standard deviation of $\\mathcal{U(1,l)}$ is $\\sigma(\\mathcal{U}(1,l))=\\sqrt{\\frac{(l-1)^2}{12}}$.\n",
"\n",
"For $N$ dice, we add the individual standard deviations quadratically, so we find $\\sigma^2$ = $\\Sigma_{i=0}^{N}\\sigma(\\mathcal{U}(1,l))^2 = N\\sigma(\\mathcal{U}(1,l))^2$, thus:\n",
"\n",
"$$\\sigma = \\sqrt{N}\\sigma(\\mathcal{U}(1,l)) = \\sqrt{\\frac{N(l-1)^2}{12}}$$\n",
"\n",
"Here, we have $N$=5, big enough that our distribution can be well approximated by the following gaussian:\n",
"\n",
"$$\\mathcal{N}(\\frac{N\\cdot l}{2},\\sqrt{\\frac{N(l-1)^2}{12}})$$\n",
"\n",
"Try lowering the number of dice $N$, you will notice our approximation hits its limits!"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}