# I'll use the MLE parameters of the gamma distribution
# to help me choose priors
k_est, _, theta_est = scipy.stats.gamma.fit(prcp, floc=0)
k_est, theta_est
(0.8898876017525283, 0.25043291132301665)
# I'll use uniform priors for the parameters.
# I chose the upper bounds by trial and error.
ks = np.linspace(0.01, 2, num=51)
prior_k = make_uniform(ks, name='k')
thetas = np.linspace(0.01, 1.5, num=51)
prior_theta = make_uniform(thetas, name='theta')
# Here's the joint prior
prior = make_joint(prior_k, prior_theta)
# I'll use a grid to compute the densities
k_mesh, theta_mesh, data_mesh = np.meshgrid(
prior.columns, prior.index, prcp)
# Here's the 3-D array of densities
densities = gamma_dist(k_mesh, theta_mesh).pdf(data_mesh)
densities.shape
(51, 51, 14)
# Which we reduce by multiplying along axis 2
likelihood = densities.prod(axis=2)
likelihood.sum()
150287.91980136465
# Now we can do the update in the usual way
posterior = prior * likelihood
normalize(posterior)
57.780822684107896
# And here's what the posterior looks like
plot_contour(posterior)
decorate(title='Posterior distribution, parameters of a gamma distribution')
# I'll check the marginal distributions to make sure the
# range of the priors is wide enough
from utils import marginal
posterior_k = marginal(posterior, 0)
posterior_theta = marginal(posterior, 1)
# The marginal distribution for k is close to 0 at both ends
posterior_k.plot(color='C4')
decorate(xlabel='k',
ylabel='PDF',
title='Posterior marginal distribution of k')
posterior_k.mean(), posterior_k.credible_interval(0.9)
(0.8437218523899558, array([0.4478, 1.3632]))
# Same with the marginal distribution of theta
posterior_theta.plot(color='C2')
decorate(xlabel='theta',
ylabel='PDF',
title='Posterior marginal distribution of theta')
posterior_theta.mean(), posterior_theta.credible_interval(0.9)
(0.367761307460383, array([0.159 , 0.7848]))
# To compute the posterior predictive distribution,
# I'll stack the joint posterior to make a Series
# with a MultiIndex
posterior_series = posterior.stack()
posterior_series.head()
theta k
0.01 0.0100 4.306265e-156
0.0498 1.304069e-145
0.0896 2.463890e-141
0.1294 2.077828e-138
0.1692 4.227218e-136
dtype: float64
# I'll extend the predictive distribution up to 2 inches
low, high = 0.01, 2
# Now we can iterate through `posterior_series`
# and make a sequence of predictive Pmfs, one
# for each possible pair of parameters
from utils import pmf_from_dist
qs = np.linspace(low, high, num=101)
pmf_seq = []
for (theta, k) in posterior_series.index:
dist = gamma_dist(k, theta)
pmf = pmf_from_dist(dist, qs)
pmf_seq.append(pmf)
# And we can use `make_mixture` to make the posterior predictive
# distribution
post_pred = make_mixture(posterior_series, pmf_seq)
# Here's what it looks like.
post_pred.make_cdf().plot(label='rainfall')
decorate(xlabel='Total rainfall (in)',
ylabel='CDF',
title='Posterior predictive distribution of rainfall')
# The probability of more than 1.5 inches of rain is small
cdf = post_pred.make_cdf()
p_gt = 1 - cdf(1.5)
p_gt
0.00900003598887611
# So it's easier to interpret as the number of rainy
# days between events, on average
1 / p_gt
111.11066680577532