Open gravesti opened 4 months ago
@gravesti this looks awesome and it runs and I agree it looks similar. The code is quite clever and I am going to need a few days for a proper review as I do my matrix multiplication! Some questions in the meantime:
make_durations
is probably a better solution, though would be curious to benchmark. Do you trust glue
with floats? Have not dug into yet but I suspect better to pass floats as data to cmdstanr
than via glue for vector[M] starts = [0.00123, ... 40.99199]
. Does glue
have a global option for decimal separators, for instance? row_vector[M] starts = [1,2,3,4,5]
instead of using the transpose '
operator@gravesti just tried out the approach I had in mind with mroe preprocessing in R. It gave identical results but was about 75% the sampling time.
data {
int<lower=0> N;
vector[N] trt;
vector[N] time;
vector[N] cens;
array[N] int ai;
int<lower=0> K;
matrix[N, K] X;
vector[K] L_beta;
vector[K] U_beta;
}
parameters {
real beta_trt;
vector<lower=L_beta, upper=U_beta>[K] beta;
vector[5] alpha;
}
transformed parameters {
real HR_trt = exp(beta_trt);
}
model {
beta_trt ~ normal(0, 1000);
beta[1] ~ normal(0, 1000) ;
beta[2] ~ normal(0, 1000) ;
alpha ~ normal(0, 1000) ;
for (i in 1:N) {
if (cens[i] == 1) {
target += exponential_lccdf(time[i] | exp(alpha[ai[i]] + X[i]*beta + trt[i] * beta_trt) );
} else {
target += exponential_lpdf(time[i] | exp(alpha[ai[i]] + X[i]*beta + trt[i] * beta_trt) );
}
}
}
Here, N is the data in long format such as from survSplit
and ai
is the index of the alpha that the observation corresponds to.
id ext trt cov4 cov3 cov2 cov1 status cnsr resp tstart time evnt ai
1 1 0 0 1 1 1 0 1 0 1 0 2.422641 1 1
2 2 0 0 1 1 0 1 0 1 1 0 10.000000 0 1
3 2 0 0 1 1 0 1 0 1 1 10 20.000000 0 2
4 2 0 0 1 1 0 1 0 1 1 20 30.000000 0 3
5 2 0 0 1 1 0 1 0 1 1 30 40.000000 0 4
Bonus is also that I think it would be easier to work into the current STAN code?
@gravesti this looks awesome and it runs and I agree it looks similar. The code is quite clever and I am going to need a few days for a proper review as I do my matrix multiplication!
A nice guide for the maths https://grodri.github.io/glms/notes/c7s4
Some questions in the meantime:
- I was going to do more of this in R but your
make_durations
is probably a better solution, though would be curious to benchmark. Do you trustglue
with floats? Have not dug into yet but I suspect better to pass floats as data tocmdstanr
than via glue forvector[M] starts = [0.00123, ... 40.99199]
. Doesglue
have a global option for decimal separators, for instance?
Good idea to pass as data. I guess I was trying to touch the least other functions. I'm sure we can control how we go from numeric to character, either via glue or printf.
Maybe easier to read if you declare
row_vector[M] starts = [1,2,3,4,5]
instead of using the transpose'
operator I don't know why I did it as a vector, so we can try to change.It occurs to me that it is not problematic to have vectors with 0 events b/c it's a fully parametric model and these parameters have priors and are not referenced in any likelihood increment. Do you agree?
Sounds right to me.
Obvi this is an early draft but some other thoughts for later: 1) not super clear how events outside of range of cutpoints will e handled;
In my set up we only have to set the start of the periods, so we only miss events which happen too early. I guess in most data we don't expect to have times less than 0, so we could always have a period starting at 0.
2) should we allow different priors within diffferent cutpoints?
Yeah, I was thinking about this already. We might already have this for easily with stan notation, I think it's possible to specify eg a normal prior with a vector of means, so that all the alphas could have their own mean. This needs more thought when to comes to the borrowing method as well.
3) weights should accepted as matrix (1-row-per-pt-per-period) or R vector (1-row-per-pt), right?
In our current set up we specify weights as a column name, so we'd need new machinery for adding the weight matrix.
Re: long format and pre-processing in R I suppose then we'd need to introduce a slot for an R preprocessing function in one of the objects. I guess it belongs to the outcome object? My idea for doing it all in Stan was to keep the data the same between all models. I don't know if that really saves us much memory
Proof of concept for an implementation of piecewise constant (exponential) hazard.
Had to do some messy things in
make_model_string_parameters.R
, so that needs some thinkingNeed to think about adding option for
weight_matrix
. My naive thought is it can be added simply in the likelihood line ifW[N,M]
, ie