I would like to get a posterior distribution for the shape k and scale \lambda parameters of a 2-parameter Weibull distribution, and my prior on k is not behaving as expected. I would like to combine two prior beliefs on k by multiplying them together, but when I use the prior k~two_normals(2.25,0.2,2.0,0.2);
the fit returns k=0.24, which is way off of the Weibull distribution I created the data from, with shape$=2.25$. Why? Shouldn’t this prior bias samples towards 2.1 or so?
I am new to Stan and to Bayesian statistics so any advice, comment and/or discussion is welcome.
functions{
// the "two_normals" must end in lpdf
real two_normals_lpdf(real x,real mu1,real sigma1, real mu2, real sigma2){
real prob;
prob = normal_lpdf(x|mu1,sigma1)*normal_lpdf(x|mu2,sigma2);
return(prob);
}
}
data {
int<lower=0> N;
real<lower=0> y[N];
}
parameters {
real k;
real lambda;
}
model {
// shorthand for target+=two_normals_lpdf(k | 2.25,0.2,2.0,0.2)
k~two_normals(2.25,0.2,2.0,0.2);
lambda~normal(30,3);
y~weibull(k,lambda);
}
Here is the data generation and model sampling
N_OBSERVATIONS = 1000
w_shape = 2.25
w_scale = 30
observation = w_scale*np.random.weibull(w_shape,(N_OBSERVATIONS,))
observation[-10:]
data = {'N': len(observation), 'y':observation}
sm = pystan.StanModel(model_code=model)
fit = sm.sampling(data=data, iter=N_SAMPLES, chains=4, warmup=500, thin=1, seed=101,verbose=True)
print(fit)
Output…
4 chains, each with iter=10000; warmup=500; thin=1;
post-warmup draws per chain=9500, total post-warmup draws=38000.
mean se_mean sd 2.5% 25% 50% 75% 97.5% n_eff Rhat
k BAD -->0.24<--BAD 5.3e-5 9.5e-3 0.22 0.23 0.24 0.25 0.26 32394 1.0
lambda 27.58 0.01 2.36 23.09 25.97 27.55 29.15 32.28 29979 1.0
lp__ -3681 7.7e-3 1.0 -3684 -3681 -3681 -3680 -3680 16836 1.0
I thought I was specifying something like this (adapted from what I learned at MCMC/MCMC.ipynb at master · Joseph94m/MCMC · GitHub)
transition_model = lambda x: np.random.normal(x,[0.2,5],(2,))
def log_prior(w):
if(w[0]<=0 or w[1]<=0):
return np.log(0)
else:
# Two priors for k=w[0], one prior for lambda=w[1], all multiplied together (and log taken)
return scipy.stats.norm.logpdf(w[0],2.25,.2)+scipy.stats.norm.logpdf(w[0],2,.2)+scipy.stats.norm.logpdf(w[1],30,3)
def log_lik(x,data):
return np.sum(scipy.stats.weibull_min(c=x[0],scale=x[1],loc=0).logpdf(data))
def acceptance(x, x_new):
if x_new > x:
return True
else:
accept = np.random.uniform(0,1)
return (accept < (np.exp(x_new-x)))
def metropolis_hastings(likelihood_computer,log_prior,transition_model,param_init,iterations,data,acceptance_rule):
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 + log_prior(x),x_new_lik+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(log_lik,log_prior,transition_model,[0.1,0.1],5000,observation,acceptance)
The last 10 accepted values are
GOOD-->array([[ 2.23895553, 29.34343911],
[ 2.26532006, 29.54858588],
[ 2.1739458 , 29.28039943],
[ 2.17658563, 29.65725183],
[ 2.18846247, 29.37056121],
[ 2.17330239, 30.10245334],
[ 2.26786791, 30.83535897],
[ 2.30913441, 30.71661784],
[ 2.16745388, 29.93292969],
[ 2.16873363, 29.98811978]])