Hi,
I have two-dimensional sparse data to which I fit a hierarchical regression model and was wondering how can I make my stan code more efficient.
Specifically, my data are response observations that come from J individuals treated under T different treatments. Hence it can be organized in a J by T matrix.
The thing is that this matrix is sparse, meaning that I don't necessarily have an observation for each individual j from each treatment t.
To complicate it slightly further, the J individuals belong to two separate groups (labeled "1" and "2") where for each group the regression factors are slightly different.
And to complicate it even slightly further, my data are a mixture of two normals
Specifically, if I denote my response matrix as y (with dimensions J by T):
So if y[j,t] exists, then:
if y[j] is from group "1":
y[j,t] ~ normal(nu[t] + alpha[t] + X[j]*beta, sigma[t]) with probability delta
and
y[j,t] ~ normal(-1*nu[t] + alpha[t] + X[j]*beta, sigma[t]) with probability 1-delta
and if y[j] is from group "2":
y[j,t] ~ normal(nu[t], sigma[t]) with probability delta
and
y[j,t] ~ normal(-1*nu[t], sigma[t]) with probability 1-delta
In words, the "1" y[j]'s are affected by the nu[t] and alpha[t] factors of treatment t and beta co-variates of subject j (specified by the design matrix X which has dimensions J subjects by C co-variates). The "2" y[j]'s are only affected by the nu[t] factor.
Both observations are assumed to have the same variance estimated by sigma[t] of that treatment. The mixture is on the nu[t] factor.
I put a hierarchy on the treatment and therefore have the priors:
sigma[t] ~ inv_gamma(a,b) (where I use empirical a = b = 0.1)
nu[t] ~ lognormal(mu_nu, sigma_nu) (log_normal is due to the mixture)
alpha[t] ~ lognormal(mu_alpha, sigma_alpha)
and then:
mu_nu ~ lognormal(0, 10)
sigma_nu ~ inv_gamma(c, d) (where I use empirical c = d = 0.1)
mu_alpha ~ normal(0, 10)
sigma_alpha ~ inv_gamma(e, f) (where I use empirical e = f = 0.1)
Finally, I use delta = 0.5.
This is my stan code:
data {
int<lower=1> T; //number of treatments
int<lower=1> J; //number of subjects
int<lower=0> C; //number of covariates
matrix[J,T] y; //response observations
matrix<lower=0,upper=2>[J,T] map; //this is an indicator map that corresponds to y where: 0 - no observation; 1 - "1" group; 2 - "2" group
matrix[J,C] X; //covariates
vector<lower=0>[T] n_1s_treatment; //the number of "1"'s in each treatment
real<lower=0> a;
real<lower=0> b;
real<lower=0> c;
real<lower=0> d;
real<lower=0> e;
real<lower=0> f;
real<lower=-1,upper=1> delta;
int<lower=0,upper=1> have_ones; //binary indicator of whether y has "1"'s at all (1/0)
}
parameters {
real<lower=0> sigma_alpha;
real mu_alpha;
real<lower=0> sigma_nu;
real<lower=0> mu_nu;
vector[T] alpha;
vector<lower=0>[T] nu;
vector<lower=0>[T] sigma;
vector[C] beta;
}
model {
if (have_ones == 1) {
sigma_alpha ~ inv_gamma(e, f);
mu_alpha ~ normal(0, 10);
}
sigma_nu ~ inv_gamma(c, d);
mu_nu ~ lognormal(0, 10);
for (t in 1:T) {
if (n_1s_treatment[t] > 0) {
alpha[t] ~ normal(mu_alpha, sigma_alpha);
}
nu[t] ~ lognormal(mu_nu, sigma_nu);
sigma[t] ~ inv_gamma(a, b);
for (j in 1:J) {
if (map[j,t] > 0) {
if (map[j,t] == 1) {
increment_log_prob(log_sum_exp(log(delta) + normal_log(y[j,t], nu[t] + alpha[t] + X[j]*beta, sigma[t]),log1m(delta) + normal_log(y[j,t], -1*nu[t] + alpha[t] + X[j]*beta, sigma[t])));
}
else if (map[j,t] == 2) {
increment_log_prob(log_sum_exp(log(delta) + normal_log(y[j,t], nu[t], sigma[t]), log1m(delta) + normal_log(y[j,t], -1*nu[t], sigma[t])));
}
}
}
}
}
Obviously vectorizing it would make it more efficient but since my data are sparse, even if I split y to the corresponding two groups and hold an array of vectors y's for each group (a vector per treatment) it seems to me that I'll still need to loop over treatments and subjects
since I don't see how sparse data can be represented efficiently. But I'm probably wrong and would greatly appreciate being proven so and corrected.
stan code (code.stan) and an example data in RData file (exampleData.RData) are attached.
Thanks a lot,
rubi