-
Notifications
You must be signed in to change notification settings - Fork 0
/
metropolis2.py
132 lines (106 loc) · 4.59 KB
/
metropolis2.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# -*- coding: utf-8 -*-
"""
Created on Tue Jun 28 16:26:42 2022
@author: ISA
"""
import numpy as np
import scipy.stats
import matplotlib.pyplot as plt
mod1=lambda t:np.random.normal(10,3,t)
#Form a population of 30,000 individual, with average=10 and scale=3
population = mod1(30000)
#Assume we are only able to observe 1,000 of these individuals.
observation = population[np.random.randint(0, 30000, 1000)]
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(1,1,1)
ax.hist( observation,bins=35 ,)
ax.set_xlabel("Value")
ax.set_ylabel("Frequency")
ax.set_title("Figure 1: Distribution of 1000 observations sampled from a population of 30,000 with mu=10, sigma=3")
mu_obs=observation.mean()
mu_obs
#The tranistion model defines how to move from sigma_current to sigma_new
transition_model = lambda x: [x[0],np.random.normal(x[1],0.5,(1,))]
def prior(x):
#x[0] = mu, x[1]=sigma (new or current)
#returns 1 for all valid values of sigma. Log(1) =0, so it does not affect the summation.
#returns 0 for all invalid values of sigma (<=0). Log(0)=-infinity, and Log(negative number) is undefined.
#It makes the new sigma infinitely unlikely.
if(x[1] <=0):
return 0
return 1
#Computes the likelihood of the data given a sigma (new or current) according to equation (2)
def manual_log_like_normal(x,data):
#x[0]=mu, x[1]=sigma (new or current)
#data = the observation
return np.sum(-np.log(x[1] * np.sqrt(2* np.pi) )-((data-x[0])**2) / (2*x[1]**2))
#Same as manual_log_like_normal(x,data), but using scipy implementation. It's pretty slow.
def log_lik_normal(x,data):
#x[0]=mu, x[1]=sigma (new or current)
#data = the observation
return np.sum(np.log(scipy.stats.norm(x[0],x[1]).pdf(data)))
#Defines whether to accept or reject the new sample
def acceptance(x, x_new):
if x_new>x:
return True
else:
accept=np.random.uniform(0,1)
# Since we did a log likelihood, we need to exponentiate in order to compare to the random number
# less likely x_new are less likely to be accepted
return (accept < (np.exp(x_new-x)))
def metropolis_hastings(likelihood_computer,prior, transition_model, param_init,iterations,data,acceptance_rule):
# likelihood_computer(x,data): returns the likelihood that these parameters generated the data
# transition_model(x): a function that draws a sample from a symmetric distribution and returns it
# param_init: a starting sample
# iterations: number of accepted to generated
# data: the data that we wish to model
# acceptance_rule(x,x_new): decides whether to accept or reject the new sample
x = param_init
accepted = []
rejected = []
for i in range(iterations):
x_new = transition_model(x)
x_lik = likelihood_computer(x,data)
x_new_lik = likelihood_computer(x_new,data)
if (acceptance_rule(x_lik + np.log(prior(x)),x_new_lik+np.log(prior(x_new)))):
x = x_new
accepted.append(x_new)
else:
rejected.append(x_new)
return np.array(accepted), np.array(rejected)
accepted, rejected = metropolis_hastings(manual_log_like_normal,prior,transition_model,[mu_obs,0.1], 50000,observation,acceptance)
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(2,1,1)
ax.plot( rejected[0:500,1], 'rx', label='Rejected',alpha=0.5)
ax.plot( accepted[0:500,1], 'b.', label='Accepted',alpha=0.5)
ax.set_xlabel("Iteration")
ax.set_ylabel("$\sigma$")
ax.set_title("Figure 2: MCMC sampling for $\sigma$ with Metropolis-Hastings. First 50 samples are shown.")
ax.grid()
ax.legend()
ax2 = fig.add_subplot(2,1,2)
to_show=-accepted.shape[0]
ax2.plot( rejected[to_show:,1], 'rx', label='Rejected',alpha=0.5)
ax2.plot( accepted[to_show:,1], 'b.', label='Accepted',alpha=0.5)
ax2.set_xlabel("Iteration")
ax2.set_ylabel("$\sigma$")
ax2.set_title("Figure 3: MCMC sampling for $\sigma$ with Metropolis-Hastings. All samples are shown.")
ax2.grid()
ax2.legend()
fig.tight_layout()
accepted.shape
show=int(-0.75*accepted.shape[0])
hist_show=int(-0.75*accepted.shape[0])
mu=accepted[show:,0].mean()
sigma=accepted[show:,1].mean()
print(mu, sigma)
model = lambda t,mu,sigma:np.random.normal(mu,sigma,t)
observation_gen=model(population.shape[0],mu,sigma)
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(1,1,1)
ax.hist( observation_gen,bins=70 ,label="Predicted distribution of 30,000 individuals")
ax.hist( population,bins=70 ,alpha=0.5, label="Original values of the 30,000 individuals")
ax.set_xlabel("Mean")
ax.set_ylabel("Frequency")
ax.set_title("Figure 6: Posterior distribution of predicitons")
ax.legend()