Spaces:
Running
Running
add `binomial distribution` notebook
Browse filesA notebook that explores the binomial distribution, including its definition, properties, and practical applications. It features interactive elements for parameter adjustment and visualizations of the probability mass function (PMF) and random samples.
probability/14_binomial_distribution.py
ADDED
@@ -0,0 +1,505 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# /// script
|
2 |
+
# requires-python = ">=3.10"
|
3 |
+
# dependencies = [
|
4 |
+
# "marimo",
|
5 |
+
# "matplotlib==3.10.0",
|
6 |
+
# "numpy==2.2.4",
|
7 |
+
# "scipy==1.15.2",
|
8 |
+
# "altair==5.2.0",
|
9 |
+
# "wigglystuff==0.1.10",
|
10 |
+
# "pandas==2.2.3",
|
11 |
+
# ]
|
12 |
+
# ///
|
13 |
+
|
14 |
+
import marimo
|
15 |
+
|
16 |
+
__generated_with = "0.11.23"
|
17 |
+
app = marimo.App(width="medium", app_title="Binomial Distribution")
|
18 |
+
|
19 |
+
|
20 |
+
@app.cell(hide_code=True)
|
21 |
+
def _(mo):
|
22 |
+
mo.md(
|
23 |
+
r"""
|
24 |
+
# Binomial Distribution
|
25 |
+
|
26 |
+
_This notebook is a computational companion to ["Probability for Computer Scientists"](https://chrispiech.github.io/probabilityForComputerScientists/en/part2/binomial/), by Stanford professor Chris Piech._
|
27 |
+
|
28 |
+
In this section, we will discuss the binomial distribution. To start, imagine the following example:
|
29 |
+
|
30 |
+
Consider $n$ independent trials of an experiment where each trial is a "success" with probability $p$. Let $X$ be the number of successes in $n$ trials.
|
31 |
+
|
32 |
+
This situation is truly common in the natural world, and as such, there has been a lot of research into such phenomena. Random variables like $X$ are called **binomial random variables**. If you can identify that a process fits this description, you can inherit many already proved properties such as the PMF formula, expectation, and variance!
|
33 |
+
"""
|
34 |
+
)
|
35 |
+
return
|
36 |
+
|
37 |
+
|
38 |
+
@app.cell(hide_code=True)
|
39 |
+
def _(mo):
|
40 |
+
mo.md(
|
41 |
+
r"""
|
42 |
+
## Binomial Random Variable Definition
|
43 |
+
|
44 |
+
$X \sim \text{Bin}(n, p)$ represents a binomial random variable where:
|
45 |
+
|
46 |
+
- $X$ is our random variable (number of successes)
|
47 |
+
- $\text{Bin}$ indicates it follows a binomial distribution
|
48 |
+
- $n$ is the number of trials
|
49 |
+
- $p$ is the probability of success in each trial
|
50 |
+
|
51 |
+
```
|
52 |
+
X ~ Bin(n, p)
|
53 |
+
↑ ↑ ↑
|
54 |
+
| | +-- Probability of
|
55 |
+
| | success on each
|
56 |
+
| | trial
|
57 |
+
| +-- Number of trials
|
58 |
+
|
|
59 |
+
Our random variable
|
60 |
+
is distributed
|
61 |
+
as a Binomial
|
62 |
+
```
|
63 |
+
|
64 |
+
Here are a few examples of binomial random variables:
|
65 |
+
|
66 |
+
- Number of heads in $n$ coin flips
|
67 |
+
- Number of 1's in randomly generated length $n$ bit string
|
68 |
+
- Number of disk drives crashed in 1000 computer cluster, assuming disks crash independently
|
69 |
+
"""
|
70 |
+
)
|
71 |
+
return
|
72 |
+
|
73 |
+
|
74 |
+
@app.cell(hide_code=True)
|
75 |
+
def _(mo):
|
76 |
+
mo.md(
|
77 |
+
r"""
|
78 |
+
## Properties of Binomial Distribution
|
79 |
+
|
80 |
+
| Property | Formula |
|
81 |
+
|----------|---------|
|
82 |
+
| Notation | $X \sim \text{Bin}(n, p)$ |
|
83 |
+
| Description | Number of "successes" in $n$ identical, independent experiments each with probability of success $p$ |
|
84 |
+
| Parameters | $n \in \{0, 1, \dots\}$, the number of experiments<br>$p \in [0, 1]$, the probability that a single experiment gives a "success" |
|
85 |
+
| Support | $x \in \{0, 1, \dots, n\}$ |
|
86 |
+
| PMF equation | $P(X=x) = {n \choose x}p^x(1-p)^{n-x}$ |
|
87 |
+
| Expectation | $E[X] = n \cdot p$ |
|
88 |
+
| Variance | $\text{Var}(X) = n \cdot p \cdot (1-p)$ |
|
89 |
+
|
90 |
+
Let's explore how the binomial distribution changes with different parameters.
|
91 |
+
"""
|
92 |
+
)
|
93 |
+
return
|
94 |
+
|
95 |
+
|
96 |
+
@app.cell(hide_code=True)
|
97 |
+
def _(TangleSlider, mo):
|
98 |
+
# Interactive elements using TangleSlider
|
99 |
+
n_slider = mo.ui.anywidget(TangleSlider(
|
100 |
+
amount=10,
|
101 |
+
min_value=1,
|
102 |
+
max_value=30,
|
103 |
+
step=1,
|
104 |
+
digits=0,
|
105 |
+
suffix=" trials"
|
106 |
+
))
|
107 |
+
|
108 |
+
p_slider = mo.ui.anywidget(TangleSlider(
|
109 |
+
amount=0.5,
|
110 |
+
min_value=0.01,
|
111 |
+
max_value=0.99,
|
112 |
+
step=0.01,
|
113 |
+
digits=2,
|
114 |
+
suffix=" probability"
|
115 |
+
))
|
116 |
+
|
117 |
+
# Grid layout for the interactive controls
|
118 |
+
controls = mo.vstack([
|
119 |
+
mo.md("### Adjust Parameters to See How Binomial Distribution Changes"),
|
120 |
+
mo.hstack([
|
121 |
+
mo.md("**Number of trials (n):** "),
|
122 |
+
n_slider
|
123 |
+
], justify="start"),
|
124 |
+
mo.hstack([
|
125 |
+
mo.md("**Probability of success (p):** "),
|
126 |
+
p_slider
|
127 |
+
], justify="start"),
|
128 |
+
])
|
129 |
+
return controls, n_slider, p_slider
|
130 |
+
|
131 |
+
|
132 |
+
@app.cell(hide_code=True)
|
133 |
+
def _(controls):
|
134 |
+
controls
|
135 |
+
return
|
136 |
+
|
137 |
+
|
138 |
+
@app.cell(hide_code=True)
|
139 |
+
def _(n_slider, np, p_slider, plt, stats):
|
140 |
+
# Parameters from sliders
|
141 |
+
_n = int(n_slider.amount)
|
142 |
+
_p = p_slider.amount
|
143 |
+
|
144 |
+
# Calculate PMF
|
145 |
+
_x = np.arange(0, _n + 1)
|
146 |
+
_pmf = stats.binom.pmf(_x, _n, _p)
|
147 |
+
|
148 |
+
# Relevant stats
|
149 |
+
_mean = _n * _p
|
150 |
+
_variance = _n * _p * (1 - _p)
|
151 |
+
_std_dev = np.sqrt(_variance)
|
152 |
+
|
153 |
+
_fig, _ax = plt.subplots(figsize=(10, 6))
|
154 |
+
|
155 |
+
# Plot PMF as bars
|
156 |
+
_ax.bar(_x, _pmf, color='royalblue', alpha=0.7, label=f'PMF: P(X=k)')
|
157 |
+
|
158 |
+
# Add a line
|
159 |
+
_ax.plot(_x, _pmf, 'ro-', alpha=0.6, label='PMF line')
|
160 |
+
|
161 |
+
# Add vertical lines
|
162 |
+
_ax.axvline(x=_mean, color='green', linestyle='--', linewidth=2,
|
163 |
+
label=f'Mean: {_mean:.2f}')
|
164 |
+
|
165 |
+
# Shade the stdev region
|
166 |
+
_ax.axvspan(_mean - _std_dev, _mean + _std_dev, alpha=0.2, color='green',
|
167 |
+
label=f'±1 Std Dev: {_std_dev:.2f}')
|
168 |
+
|
169 |
+
# Add labels and title
|
170 |
+
_ax.set_xlabel('Number of Successes (k)')
|
171 |
+
_ax.set_ylabel('Probability: P(X=k)')
|
172 |
+
_ax.set_title(f'Binomial Distribution with n={_n}, p={_p:.2f}')
|
173 |
+
|
174 |
+
# Annotations
|
175 |
+
_ax.annotate(f'E[X] = {_mean:.2f}',
|
176 |
+
xy=(_mean, stats.binom.pmf(int(_mean), _n, _p)),
|
177 |
+
xytext=(_mean + 1, max(_pmf) * 0.8),
|
178 |
+
arrowprops=dict(facecolor='black', shrink=0.05, width=1))
|
179 |
+
|
180 |
+
_ax.annotate(f'Var(X) = {_variance:.2f}',
|
181 |
+
xy=(_mean, stats.binom.pmf(int(_mean), _n, _p) / 2),
|
182 |
+
xytext=(_mean + 1, max(_pmf) * 0.6),
|
183 |
+
arrowprops=dict(facecolor='black', shrink=0.05, width=1))
|
184 |
+
|
185 |
+
# Grid and legend
|
186 |
+
_ax.grid(alpha=0.3)
|
187 |
+
_ax.legend()
|
188 |
+
|
189 |
+
plt.tight_layout()
|
190 |
+
plt.gca()
|
191 |
+
return
|
192 |
+
|
193 |
+
|
194 |
+
@app.cell(hide_code=True)
|
195 |
+
def _(mo):
|
196 |
+
mo.md(
|
197 |
+
r"""
|
198 |
+
## Relationship to Bernoulli Random Variables
|
199 |
+
|
200 |
+
One way to think of the binomial is as the sum of $n$ Bernoulli variables. Say that $Y_i$ is an indicator Bernoulli random variable which is 1 if experiment $i$ is a success. Then if $X$ is the total number of successes in $n$ experiments, $X \sim \text{Bin}(n, p)$:
|
201 |
+
|
202 |
+
$$X = \sum_{i=1}^n Y_i$$
|
203 |
+
|
204 |
+
Recall that the outcome of $Y_i$ will be 1 or 0, so one way to think of $X$ is as the sum of those 1s and 0s.
|
205 |
+
"""
|
206 |
+
)
|
207 |
+
return
|
208 |
+
|
209 |
+
|
210 |
+
@app.cell(hide_code=True)
|
211 |
+
def _(mo):
|
212 |
+
mo.md(
|
213 |
+
r"""
|
214 |
+
## Binomial Probability Mass Function (PMF)
|
215 |
+
|
216 |
+
The most important property to know about a binomial is its [Probability Mass Function](https://marimo.app/https://github.com/marimo-team/learn/blob/main/probability/10_probability_mass_function.py):
|
217 |
+
|
218 |
+
$$P(X=k) = {n \choose k}p^k(1-p)^{n-k}$$
|
219 |
+
|
220 |
+
```
|
221 |
+
P(X = k) = (n) p^k(1-p)^(n-k)
|
222 |
+
↑ (k)
|
223 |
+
| ↑
|
224 |
+
| +-- Binomial coefficient:
|
225 |
+
| number of ways to choose
|
226 |
+
| k successes from n trials
|
227 |
+
|
|
228 |
+
Probability that our
|
229 |
+
variable takes on the
|
230 |
+
value k
|
231 |
+
```
|
232 |
+
|
233 |
+
Recall, we derived this formula in Part 1. There is a complete example on the probability of $k$ heads in $n$ coin flips, where each flip is heads with probability $p$.
|
234 |
+
|
235 |
+
To briefly review, if you think of each experiment as being distinct, then there are ${n \choose k}$ ways of permuting $k$ successes from $n$ experiments. For any of the mutually exclusive permutations, the probability of that permutation is $p^k \cdot (1-p)^{n-k}$.
|
236 |
+
|
237 |
+
The name binomial comes from the term ${n \choose k}$ which is formally called the binomial coefficient.
|
238 |
+
"""
|
239 |
+
)
|
240 |
+
return
|
241 |
+
|
242 |
+
|
243 |
+
@app.cell(hide_code=True)
|
244 |
+
def _(mo):
|
245 |
+
mo.md(
|
246 |
+
r"""
|
247 |
+
## Expectation of Binomial
|
248 |
+
|
249 |
+
There is an easy way to calculate the expectation of a binomial and a hard way. The easy way is to leverage the fact that a binomial is the sum of Bernoulli indicator random variables $X = \sum_{i=1}^{n} Y_i$ where $Y_i$ is an indicator of whether the $i$-th experiment was a success: $Y_i \sim \text{Bernoulli}(p)$.
|
250 |
+
|
251 |
+
Since the expectation of the sum of random variables is the sum of expectations, we can add the expectation, $E[Y_i] = p$, of each of the Bernoulli's:
|
252 |
+
|
253 |
+
\begin{align}
|
254 |
+
E[X] &= E\Big[\sum_{i=1}^{n} Y_i\Big] && \text{Since }X = \sum_{i=1}^{n} Y_i \\
|
255 |
+
&= \sum_{i=1}^{n}E[ Y_i] && \text{Expectation of sum} \\
|
256 |
+
&= \sum_{i=1}^{n}p && \text{Expectation of Bernoulli} \\
|
257 |
+
&= n \cdot p && \text{Sum $n$ times}
|
258 |
+
\end{align}
|
259 |
+
|
260 |
+
The hard way is to use the definition of expectation:
|
261 |
+
|
262 |
+
\begin{align}
|
263 |
+
E[X] &= \sum_{i=0}^n i \cdot P(X = i) && \text{Def of expectation} \\
|
264 |
+
&= \sum_{i=0}^n i \cdot {n \choose i} p^i(1-p)^{n-i} && \text{Sub in PMF} \\
|
265 |
+
& \cdots && \text{Many steps later} \\
|
266 |
+
&= n \cdot p
|
267 |
+
\end{align}
|
268 |
+
"""
|
269 |
+
)
|
270 |
+
return
|
271 |
+
|
272 |
+
|
273 |
+
@app.cell(hide_code=True)
|
274 |
+
def _(mo):
|
275 |
+
mo.md(
|
276 |
+
r"""
|
277 |
+
## Binomial Distribution in Python
|
278 |
+
|
279 |
+
As you might expect, you can use binomial distributions in code. The standardized library for binomials is `scipy.stats.binom`.
|
280 |
+
|
281 |
+
One of the most helpful methods that this package provides is a way to calculate the PMF. For example, say $n=5$, $p=0.6$ and you want to find $P(X=2)$, you could use the following code:
|
282 |
+
"""
|
283 |
+
)
|
284 |
+
return
|
285 |
+
|
286 |
+
|
287 |
+
@app.cell
|
288 |
+
def _(stats, x):
|
289 |
+
# define variables for x, n, and p
|
290 |
+
_n = 5 # Integer value for n
|
291 |
+
_p = 0.6
|
292 |
+
_x = 2
|
293 |
+
|
294 |
+
# use scipy to compute the pmf
|
295 |
+
p_x = stats.binom.pmf(_x, _n, _p)
|
296 |
+
|
297 |
+
# use the probability for future work
|
298 |
+
print(f'P(X = {x}) = {p_x:.4f}')
|
299 |
+
return (p_x,)
|
300 |
+
|
301 |
+
|
302 |
+
@app.cell(hide_code=True)
|
303 |
+
def _(mo):
|
304 |
+
mo.md(r"""Another particularly helpful function is the ability to generate a random sample from a binomial. For example, say $X$ represents the number of requests to a website. We can draw 100 samples from this distribution using the following code:""")
|
305 |
+
return
|
306 |
+
|
307 |
+
|
308 |
+
@app.cell
|
309 |
+
def _(n, np, p, plt, stats):
|
310 |
+
# Ensure n is an integer to prevent TypeError
|
311 |
+
n_int = int(n)
|
312 |
+
|
313 |
+
# samples from the binomial distribution
|
314 |
+
samples = stats.binom.rvs(n_int, p, size=100)
|
315 |
+
|
316 |
+
# Print the samples
|
317 |
+
print(samples)
|
318 |
+
|
319 |
+
# Plot histogram of samples
|
320 |
+
plt.figure(figsize=(10, 5))
|
321 |
+
plt.hist(samples, bins=np.arange(-0.5, n_int+1.5, 1), alpha=0.7, color='royalblue',
|
322 |
+
edgecolor='black', density=True)
|
323 |
+
|
324 |
+
# Overlay the PMF
|
325 |
+
x_values = np.arange(0, n_int+1)
|
326 |
+
pmf_values = stats.binom.pmf(x_values, n_int, p)
|
327 |
+
plt.plot(x_values, pmf_values, 'ro-', ms=8, label='Theoretical PMF')
|
328 |
+
|
329 |
+
# Add labels and title
|
330 |
+
plt.xlabel('Number of Successes')
|
331 |
+
plt.ylabel('Relative Frequency / Probability')
|
332 |
+
plt.title(f'Histogram of 100 Samples from Bin({n_int}, {p})')
|
333 |
+
plt.legend()
|
334 |
+
plt.grid(alpha=0.3)
|
335 |
+
|
336 |
+
# Annotate
|
337 |
+
plt.annotate('Sample mean: %.2f' % np.mean(samples),
|
338 |
+
xy=(0.7, 0.9), xycoords='axes fraction',
|
339 |
+
bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.3))
|
340 |
+
plt.annotate('Theoretical mean: %.2f' % (n_int*p),
|
341 |
+
xy=(0.7, 0.8), xycoords='axes fraction',
|
342 |
+
bbox=dict(boxstyle='round,pad=0.5', fc='lightgreen', alpha=0.3))
|
343 |
+
|
344 |
+
plt.tight_layout()
|
345 |
+
plt.gca()
|
346 |
+
return n_int, pmf_values, samples, x_values
|
347 |
+
|
348 |
+
|
349 |
+
@app.cell(hide_code=True)
|
350 |
+
def _(mo):
|
351 |
+
mo.md(
|
352 |
+
r"""
|
353 |
+
You might be wondering what a random sample is! A random sample is a randomly chosen assignment for our random variable. Above we have 100 such assignments. The probability that value $k$ is chosen is given by the PMF: $P(X=k)$.
|
354 |
+
|
355 |
+
There are also functions for getting the mean, the variance, and more. You can read the [scipy.stats.binom documentation](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.binom.html), especially the list of methods.
|
356 |
+
"""
|
357 |
+
)
|
358 |
+
return
|
359 |
+
|
360 |
+
|
361 |
+
@app.cell(hide_code=True)
|
362 |
+
def _(mo):
|
363 |
+
mo.md(
|
364 |
+
r"""
|
365 |
+
## Interactive Exploration of Binomial vs. Negative Binomial
|
366 |
+
|
367 |
+
The standard binomial distribution is a special case of a broader family of distributions. One related distribution is the negative binomial, which can model count data with overdispersion (where the variance is larger than the mean).
|
368 |
+
|
369 |
+
Below, you can explore how the negative binomial distribution compares to a Poisson distribution (which can be seen as a limiting case of the binomial as $n$ gets large and $p$ gets small, with $np$ held constant).
|
370 |
+
|
371 |
+
Adjust the sliders to see how the parameters affect the distribution:
|
372 |
+
|
373 |
+
*Note: The interactive visualization in this section was inspired by work from [liquidcarbon on GitHub](https://github.com/liquidcarbon).*
|
374 |
+
"""
|
375 |
+
)
|
376 |
+
return
|
377 |
+
|
378 |
+
|
379 |
+
@app.cell
|
380 |
+
def _(mo):
|
381 |
+
alpha_slider = mo.ui.slider(
|
382 |
+
value=0.1,
|
383 |
+
steps=[0, 0.01, 0.02, 0.03, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1],
|
384 |
+
label="α (overdispersion)",
|
385 |
+
show_value=True,
|
386 |
+
)
|
387 |
+
mu_slider = mo.ui.slider(
|
388 |
+
value=100, start=1, stop=100, step=1, label="μ (mean)", show_value=True
|
389 |
+
)
|
390 |
+
return alpha_slider, mu_slider
|
391 |
+
|
392 |
+
|
393 |
+
@app.cell
|
394 |
+
def _():
|
395 |
+
equation = """
|
396 |
+
$$
|
397 |
+
P(X = k) = \\frac{\\Gamma(k + \\frac{1}{\\alpha})}{\\Gamma(k + 1) \\Gamma(\\frac{1}{\\alpha})} \\left( \\frac{1}{\\mu \\alpha + 1} \\right)^{\\frac{1}{\\alpha}} \\left( \\frac{\\mu \\alpha}{\\mu \\alpha + 1} \\right)^k
|
398 |
+
$$
|
399 |
+
|
400 |
+
$$
|
401 |
+
\\sigma^2 = \\mu + \\alpha \\mu^2
|
402 |
+
$$
|
403 |
+
"""
|
404 |
+
return (equation,)
|
405 |
+
|
406 |
+
|
407 |
+
@app.cell
|
408 |
+
def _(alpha_slider, alt, mu_slider, np, pd, stats):
|
409 |
+
mu = mu_slider.value
|
410 |
+
alpha = alpha_slider.value
|
411 |
+
n = 1000 - mu if alpha == 0 else 1 / alpha
|
412 |
+
p = n / (mu + n)
|
413 |
+
x = np.arange(0, mu * 3 + 1, 1)
|
414 |
+
df = pd.DataFrame(
|
415 |
+
{
|
416 |
+
"x": x,
|
417 |
+
"y": stats.nbinom.pmf(x, n, p),
|
418 |
+
"y_poi": stats.nbinom.pmf(x, 1000 - mu, 1 - mu / 1000),
|
419 |
+
}
|
420 |
+
)
|
421 |
+
r1k = stats.nbinom.rvs(n, p, size=1000)
|
422 |
+
df["in 95% CI"] = df["x"].between(*np.percentile(r1k, q=[2.5, 97.5]))
|
423 |
+
base = alt.Chart(df)
|
424 |
+
|
425 |
+
chart_poi = base.mark_bar(
|
426 |
+
fillOpacity=0.25, width=100 / mu, fill="magenta"
|
427 |
+
).encode(
|
428 |
+
x=alt.X("x").scale(domain=(-0.4, x.max() + 0.4), nice=False),
|
429 |
+
y=alt.Y("y_poi").scale(domain=(0, df.y_poi.max() * 1.1)).title(None),
|
430 |
+
)
|
431 |
+
chart_nb = base.mark_bar(fillOpacity=0.75, width=100 / mu).encode(
|
432 |
+
x="x",
|
433 |
+
y="y",
|
434 |
+
fill=alt.Fill("in 95% CI")
|
435 |
+
.scale(domain=[False, True], range=["#aaa", "#7c7"])
|
436 |
+
.legend(orient="bottom-right"),
|
437 |
+
)
|
438 |
+
|
439 |
+
chart = (chart_poi + chart_nb).configure_view(continuousWidth=450)
|
440 |
+
return alpha, base, chart, chart_nb, chart_poi, df, mu, n, p, r1k, x
|
441 |
+
|
442 |
+
|
443 |
+
@app.cell
|
444 |
+
def _(alpha_slider, chart, equation, mo, mu_slider):
|
445 |
+
mo.vstack(
|
446 |
+
[
|
447 |
+
mo.md(f"## Negative Binomial Distribution (Poisson + Overdispersion)\n{equation}"),
|
448 |
+
mo.hstack([mu_slider, alpha_slider], justify="start"),
|
449 |
+
chart,
|
450 |
+
], justify='space-around'
|
451 |
+
).center()
|
452 |
+
return
|
453 |
+
|
454 |
+
|
455 |
+
@app.cell(hide_code=True)
|
456 |
+
def _(mo):
|
457 |
+
mo.md(
|
458 |
+
r"""
|
459 |
+
## Key Takeaways
|
460 |
+
|
461 |
+
The binomial distribution is a fundamental discrete probability distribution that models the number of successes in a fixed number of independent trials, each with the same probability of success.
|
462 |
+
|
463 |
+
Here's what we've learned:
|
464 |
+
|
465 |
+
1. **Binomial Distribution Definition**: It models the number of successes in $n$ independent trials, each with probability $p$ of success.
|
466 |
+
|
467 |
+
2. **PMF Formula**: $P(X=k) = {n \choose k}p^k(1-p)^{n-k}$, which calculates the probability of getting exactly $k$ successes.
|
468 |
+
|
469 |
+
3. **Key Properties**:
|
470 |
+
- Expected value: $E[X] = np$
|
471 |
+
- Variance: $Var(X) = np(1-p)$
|
472 |
+
|
473 |
+
4. **Relation to Other Distributions**:
|
474 |
+
- Sum of Bernoulli random variables
|
475 |
+
- Related to negative binomial and Poisson distributions
|
476 |
+
|
477 |
+
5. **Practical Usage**:
|
478 |
+
- Easily model in Python using `scipy.stats.binom`
|
479 |
+
- Generate random samples and calculate probabilities
|
480 |
+
|
481 |
+
The binomial distribution is widely used in many fields including computer science, quality control, epidemiology, and data science to model scenarios with binary outcomes over multiple trials.
|
482 |
+
"""
|
483 |
+
)
|
484 |
+
return
|
485 |
+
|
486 |
+
|
487 |
+
@app.cell
|
488 |
+
def _():
|
489 |
+
import marimo as mo
|
490 |
+
return (mo,)
|
491 |
+
|
492 |
+
|
493 |
+
@app.cell
|
494 |
+
def _():
|
495 |
+
import numpy as np
|
496 |
+
import matplotlib.pyplot as plt
|
497 |
+
import scipy.stats as stats
|
498 |
+
import pandas as pd
|
499 |
+
import altair as alt
|
500 |
+
from wigglystuff import TangleSlider
|
501 |
+
return TangleSlider, alt, np, pd, plt, stats
|
502 |
+
|
503 |
+
|
504 |
+
if __name__ == "__main__":
|
505 |
+
app.run()
|