{ "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 }