GitHub

@@ -35,7 +35,7 @@ In the lecture we'll learn about

3535

* using a Monte Carlo simulation of a multivariate normal distribution to evaluate the quality of a normal approximation

3636

* the administrator's problem and why the multivariate hypergeometric distribution is the right tool

373738-

## The Administrator's Problem

38+

## The administrator's problem

39394040

An administrator in charge of allocating research grants is in the following situation.

4141

@@ -62,7 +62,7 @@ The $n$ balls drawn represent successful proposals and are awarded research fu

62626363

The remaining $N-n$ balls receive no research funds.

646465-

### Details of the Awards Procedure Under Study

65+

### Details of the awards procedure under study

66666767

Let $k_i$ be the number of balls of color $i$ that are drawn.

6868

@@ -106,11 +106,11 @@ the population of $N$ balls.

106106107107

The right tool for the administrator's job is the **multivariate hypergeometric distribution**.

108108109-

### Multivariate Hypergeometric Distribution

109+

### Multivariate hypergeometric distribution

110110111111

Let's start with some imports.

112112113-

```{code-cell} ipython

113+

```{code-cell} ipython3

114114

import matplotlib.pyplot as plt

115115

import numpy as np

116116

from scipy.special import comb

@@ -159,7 +159,7 @@ $$

159159160160

To do our work for us, we'll write an `Urn` class.

161161162-

```{code-cell} python3

162+

```{code-cell} ipython3

163163

class Urn:

164164165165

def __init__(self, K_arr):

@@ -209,20 +209,14 @@ class Urn:

209209

number of draws.

210210

"""

211211212-

K_arr, N, c = self.K_arr, self.N, self.c

212+

K_arr, N = self.K_arr, self.N

213213214214

# mean

215215

μ = n * K_arr / N

216216217217

# variance-covariance matrix

218-

Σ = np.full((c, c), n * (N - n) / (N - 1) / N ** 2)

219-

for i in range(c-1):

220-

Σ[i, i] *= K_arr[i] * (N - K_arr[i])

221-

for j in range(i+1, c):

222-

Σ[i, j] *= - K_arr[i] * K_arr[j]

223-

Σ[j, i] = Σ[i, j]

224-225-

Σ[-1, -1] *= K_arr[-1] * (N - K_arr[-1])

218+

p = K_arr / N

219+

Σ = n * (N - n) / (N - 1) * (np.diag(p) - np.outer(p, p))

226220227221

return μ, Σ

228222

@@ -265,15 +259,15 @@ $$

265259

P(2{\text{ black}},2{\text{ white}},2{\text{ red}})={{{5 \choose 2}{10 \choose 2}{15 \choose 2}} \over {30 \choose 6}}=0.079575596816976

266260

$$

267261268-

```{code-cell} python3

262+

```{code-cell} ipython3

269263

# construct the urn

270264

K_arr = [5, 10, 15]

271265

urn = Urn(K_arr)

272266

```

273267274268

Now use the Urn Class method `pmf` to compute the probability of the outcome $X = \begin{bmatrix} 2 & 2 & 2 \end{bmatrix}$

275269276-

```{code-cell} python3

270+

```{code-cell} ipython3

277271

k_arr = [2, 2, 2] # array of number of observed successes

278272

urn.pmf(k_arr)

279273

```

@@ -283,99 +277,99 @@ constructing a 2-dimensional

283277

array `k_arr` and `pmf` will return an array of probabilities for

284278

observing each case.

285279286-

```{code-cell} python3

280+

```{code-cell} ipython3

287281

k_arr = [[2, 2, 2], [1, 3, 2]]

288282

urn.pmf(k_arr)

289283

```

290284291285

Now let's compute the mean vector and variance-covariance matrix.

292286293-

```{code-cell} python3

287+

```{code-cell} ipython3

294288

n = 6

295289

μ, Σ = urn.moments(n)

296290

```

297291298-

```{code-cell} python3

292+

```{code-cell} ipython3

299293

μ

300294

```

301295302-

```{code-cell} python3

296+

```{code-cell} ipython3

303297

Σ

304298

```

305299306-

### Back to The Administrator's Problem

300+

### Back to the administrator's problem

307301308302

Now let's turn to the grant administrator's problem.

309303310304

Here the array of

311305

numbers of $i$ objects in the urn is

312306

$\left(157, 11, 46, 24\right)$.

313307314-

```{code-cell} python3

308+

```{code-cell} ipython3

315309

K_arr = [157, 11, 46, 24]

316310

urn = Urn(K_arr)

317311

```

318312319313

Let's compute the probability of the outcome $\left(10, 1, 4, 0 \right)$.

320314321-

```{code-cell} python3

315+

```{code-cell} ipython3

322316

k_arr = [10, 1, 4, 0]

323317

urn.pmf(k_arr)

324318

```

325319326320

We can compute probabilities of three possible outcomes by constructing a 3-dimensional

327321

arrays `k_arr` and utilizing the method `pmf` of the `Urn` class.

328322329-

```{code-cell} python3

323+

```{code-cell} ipython3

330324

k_arr = [[5, 5, 4 ,1], [10, 1, 2, 2], [13, 0, 2, 0]]

331325

urn.pmf(k_arr)

332326

```

333327334328

Now let's compute the mean and variance-covariance matrix of $X$ when $n=6$.

335329336-

```{code-cell} python3

330+

```{code-cell} ipython3

337331

n = 6 # number of draws

338332

μ, Σ = urn.moments(n)

339333

```

340334341-

```{code-cell} python3

335+

```{code-cell} ipython3

342336

# mean

343337

μ

344338

```

345339346-

```{code-cell} python3

340+

```{code-cell} ipython3

347341

# variance-covariance matrix

348342

Σ

349343

```

350344351345

We can simulate a large sample and verify that sample means and covariances closely approximate the population means and covariances.

352346353-

```{code-cell} python3

347+

```{code-cell} ipython3

354348

size = 10_000_000

355349

sample = urn.simulate(n, size=size)

356350

```

357351358-

```{code-cell} python3

352+

```{code-cell} ipython3

359353

# mean

360354

np.mean(sample, 0)

361355

```

362356363-

```{code-cell} python3

357+

```{code-cell} ipython3

364358

# variance covariance matrix

365359

np.cov(sample.T)

366360

```

367361368362

Evidently, the sample means and covariances approximate their population counterparts well.

369363370-

### Quality of Normal Approximation

364+

### Quality of normal approximation

371365372366

To judge the quality of a multivariate normal approximation to the multivariate hypergeometric distribution, we draw a large sample from a multivariate normal distribution with the mean vector and covariance matrix for the corresponding multivariate hypergeometric distribution and compare the simulated distribution with the population multivariate hypergeometric distribution.

373367374-

```{code-cell} python3

368+

```{code-cell} ipython3

375369

sample_normal = np.random.multivariate_normal(μ, Σ, size=size)

376370

```

377371378-

```{code-cell} python3

372+

```{code-cell} ipython3

379373

def bivariate_normal(x, y, μ, Σ, i, j):

380374381375

μ_x, μ_y = μ[i], μ[j]

@@ -392,7 +386,7 @@ def bivariate_normal(x, y, μ, Σ, i, j):

392386

return np.exp(-z / (2 * (1 - ρ**2))) / denom

393387

```

394388395-

```{code-cell} python3

389+

```{code-cell} ipython3

396390

@jit

397391

def count(vec1, vec2, n):

398392

size = sample.shape[0]

@@ -404,7 +398,7 @@ def count(vec1, vec2, n):

404398

return count_mat

405399

```

406400407-

```{code-cell} python3

401+

```{code-cell} ipython3

408402

c = urn.c

409403

fig, axs = plt.subplots(c, c, figsize=(14, 14))

410404

@@ -454,7 +448,7 @@ The null hypothesis is that the sample follows normal distribution.

454448455449

> `normaltest` returns an array of p-values associated with tests for each $k_i$ sample.

456450457-

```{code-cell} python3

451+

```{code-cell} ipython3

458452

test_multihyper = normaltest(sample)

459453

test_multihyper.pvalue

460454

```

@@ -463,7 +457,7 @@ As we can see, all the p-values are almost $0$ and the null hypothesis is soundl

463457464458

By contrast, the sample from normal distribution does not reject the null hypothesis.

465459466-

```{code-cell} python3

460+

```{code-cell} ipython3

467461

test_normal = normaltest(sample_normal)

468462

test_normal.pvalue

469463

```

Read the original on github.com ↗