import numpy as npimport polars as plimport cmdstanpyimport arviz as azimport iqplotimport bebi103import bokeh.iobokeh.io.output_notebook()
Loading BokehJS ...
In this lesson, we will perform parameter estimation using a variate-covariate model. We will use the data set from Good, et al., which investigates how the length of mitotic spindles varies with the size of the encapsulating droplet.
We start by loading in the data set and making a quick plot to remind us of the data set.
# Load in Data Framedf = pl.read_csv(os.path.join(data_path, "good_invitro_droplet_data.csv"), comment_prefix="#")# Pull out numpy arraysell = df["Spindle Length (um)"].to_numpy()d = df["Droplet Diameter (um)"].to_numpy()# Make a plotp = bokeh.plotting.figure( frame_height=200, frame_width=300, x_axis_label="droplet diameter (µm)", y_axis_label="spindle length (µm)", x_range=[0, 250], y_range=[0, 50],)p.scatter( source=df.to_dict(), x="Droplet Diameter (um)", y="Spindle Length (um)", alpha=0.3,)bokeh.io.show(p)
43.1 Full generative model
We have previously worked out a likelihood for the conserved tubulin model. In this model, the spindle length \(l\) is a function of droplet diameter \(d\) according to
We are now left to choose priors to complete the model. We will build the prior for the three parameters \(\phi\), \(\gamma\), and \(\sigma\), taking each to be independent of the others. Let’s start with \(\sigma\). It is possible that the spindle size is very carefully controlled. It is also possible that it could be highly variable. So, we can choose a Half Normal prior for \(\sigma\) with a large scale parameter of 10 µm; \(\sigma \sim \text{HalfNorm}(10)\). For \(\gamma\), we know the physically is must be between zero and one (as we have worked out in previous lessons), so we will assume a prior on is close to uniform that domain, but with probability density dropping right at zero and one; \(\gamma \sim \text{Beta}(1.1, 1.1)\). For \(\phi\), the typical largest spindle size, we assume a smallest spindle of about a micron (right where I would start to feel uncomfortable betting the farm) and the largest spindle to be about a millimeter (the size of a very large cell, like a Xenopus egg). We therefore take \(\log_{10}\phi \sim \text{Norm}(1.5, 0.75)\), where \(\phi\) is in units of microns.
functions {real ell_theor(real d, real phi, real gamma_) {real denom_ratio = (gamma_ * d / phi)^3;return gamma_ * d / cbrt(1 + denom_ratio); }}data {int N;int N_ppc;array[N] real d;array[N_ppc] real d_ppc;array[N] real ell;}parameters {real log10_phi;real<lower=0, upper=1> gamma_;real<lower=0> sigma;}transformed parameters {real phi = 10 ^ log10_phi;array[N] real mu;for (i in1:N) { mu[i] = ell_theor(d[i], phi, gamma_); }}model {// Priors log10_phi ~ normal(1.5, 0.75); gamma_ ~ beta(1.1, 1.1); sigma ~ normal(0.0, 10.0);// Likelihood ell ~ normal(mu, sigma);}generated quantities {array[N_ppc] real ell_ppc;for (i in1:N_ppc) {real mu_ppc = ell_theor(d_ppc[i], phi, gamma_); ell_ppc[i] = normal_rng(mu_ppc, sigma); }}
I pause to point out a few syntactical elements.
Note how I used gamma_ as the variable name for gamma. This is because gamma is used as a distribution name in Stan.
I have used the functions block in this Stan program. The definition of the function is real ell_theor(real d, real phi, real gamma_) { }, where the code to be executed in the function is between the braces. This is how you declare a function in Stan. The first word, real specifies what data type is expected to be returned. Then comes the name of the function, followed by its arguments in parentheses preceded by their data type.
Within the function, I had to declare denom_ratio to be real. Remember, Stan is statically typed, so every variable needs a declaration of its type.
The return statement is like Python; return followed by a space and then an expression for the value to be returned.
There is no Half-Normal distribution in Stan. It is implemented by putting a bound on the variable (in this case real<lower=0> sigma;) and then model sigma as being Normally distributed with location parameter zero.
We computed an array of mu values in the transformed parameters block, which enabled us to use the simpler sampling statement of ell ~ normal(mu, sigma).
We are also specifying for what values of the covariate d we want to have posterior predictive values for the spindly length. We do not need to have the covariate be exactly the same as what was used for the experimental observations. We simply need to have the covariate vary over the range of the experimental observations.
The corner plot provides a complete picture of the posterior and allows for direct assessment of confidence regions. Apparently, we have that \(\phi \approx 38.25\) µm, \(\gamma \approx 0.86\), and \(\sigma \approx 3.75\) µm.
43.3 Posterior predictive checks
Let’s make a plot for a graphical posterior predictive check. Because we have a covariate, we want to make a plot of what values we might get for the variate (spindle length) for various values of the covariate (droplet diameter). We can make sure a plot using the bebi103.viz.predictive_regression() function.