Dear Dave,<br><br>You are right. This can be used as a template file for handing growth mixture modeling using ADMB. Thanks for the effort. <br><br>I plan to spend some time adapting the code to handle my empirical questions at hand, and comparing the results to those obtained from Mplus, as well as some benchmarks. I will report to the community once I have something to report. <br>
<br>Many thanks.<br><br>Best,<br>Shige<br><br><div class="gmail_quote">On Sun, Feb 22, 2009 at 4:32 PM, dave fournier <span dir="ltr"><<a href="mailto:otter@otter-rsch.com">otter@otter-rsch.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><br>
Shige,<br>
<br>
I think the "ADMB team" consists mostly of me. So I'll try and answer<br>
your question about finite mixtures and growth models. Until today I had<br>
very little idea what growth models were, but in writing the simulator I<br>
hope I am getting the idea.<br>
<br>
Actually finite mixtures are handled directly so it is not necessary to<br>
use the random effects package. This is actually an advantage since it<br>
greatly speeds up the calculations.<br>
To illustrate the ideas I built a simulator to create a data set<br>
something like you describe in<br>
<br>
This could be done in R but I used ADMB since it illustrates some of the<br>
techniques which are useful for building the analyzer. since you didn't<br>
identify the growth curves you used I use the von Bertalanfy curve<br>
<br>
L_t = Linf * ( 1 - exp(-k*age))<br>
<br>
where L_0=0 is assumed. Of course it is a simple matter to switch to a<br>
different curve There are two curves one with Linf=100 and the other<br>
with Linf=120. For both cases k=0.11 (time measured in years).<br>
<br>
I generated 2000 samples. First the size measured in 2 month intervals<br>
and then at ages 8,11,15,19<br>
<br>
The individuals were generated with a probability of 0.4 of belonging to<br>
group1 (linf=100) and 0.6 of belonging to group 2. After the first<br>
period the individual were changed at random to groups 1 or 2 with a<br>
transition matrix<br>
<br>
.8  prob stay in group 1<br>
.2  prob to move from group 2 to group 1<br>
.7  prob to say in group 2<br>
.3  prob to move from group 2 to group 1<br>
<br>
The the size data was generator using the appropriate growth curve.<br>
the resulting sizes were then perturbed using  multiplicative lognormal<br>
random variables (std dev of .08) which were highly positively<br>
correlated (0.9)<br>
<br>
The resulting output was analyzed with a finite mixture model where<br>
the 12 initial measuremeasurements are assumed to come from a mixture of<br>
two multivariate log-normal distributions with a correlation matrix of<br>
the form<br>
<br>
1     rho rho^2  .... rho^11<br>
rho    1<br>
rho^2   .......<br>
<br>
etc.<br>
<br>
<br>
The model was fit and the Hessian evaluated in under a minute.<br>
<br>
these are the parameter estimates for a typical run.<br>
<br>
# Number of parameters = 10  Objective function value = -84436.9<br>
# p1coff:<br>
0.379774 0.620226<br>
# p2coff:<br>
0.482844 0.517158<br>
# Linf:<br>
99.2992 119.646<br>
# k:<br>
0.109928 0.110294<br>
# rho:<br>
0.882750993879<br>
# lsigma:<br>
-2.54165198426<br>
<br>
<br>
For each individual the conditional probabilities of belonging in either<br>
of the two groups was calculated after observing the first sizes and<br>
again after observing the second group of sizes. this was used to<br>
estimate the transition probabilities. (Of course this could be done in<br>
the model which would be more efficient.)<br>
~<br>
~ 0.793901 0.206099<br>
0.292103 0.707897<br>
~<br>
which is almost perfect.<br>
<br>
the C++ code for the simulator is<br>
<br>
~<br>
~<br>
<br>
dvector growth_function(const dvector & age,const dvector& theta)<br>
{<br>
dvector tmp=theta(1)*(1.0-exp(-theta(2)*age)); // use you own<br>
return tmp;                           // growth function here<br>
}<br>
<br>
int main(int argc,char * argv[])<br>
{<br>
int n=2000;<br>
int nm=16;<br>
int iseed=289;<br>
random_number_generator rng(iseed);<br>
dvector p1(1,2);  // priori probability that a child is in group 1 or 2<br>
p1(1)=.4; p1(2)=.6;<br>
dvector p2(1,2);<br>
dmatrix M(1,2,1,2);   // transition probabilities<br>
M(1,1)=.8;  // 1 remains 1<br>
M(2,1)=.3;  // 2 moves to 1<br>
M(1,2)=.2;  // 1 moves to 2<br>
M(2,2)=.7;  // 2 remains 2<br>
dvector choose(1,n);<br>
dvector choose1(1,n);<br>
double sigma=0.08;<br>
double rho=0.9;<br>
dmatrix size(1,n,1,nm);<br>
imatrix group(1,n,1,2);<br>
dvector age(1,nm);<br>
dvector eta(1,nm);<br>
dvector eps(1,nm);<br>
age(13)=8;<br>
age(14)=11;<br>
age(15)=15;<br>
age(16)=19;<br>
dmatrix theta(1,2,1,2);  //parameters for growth curves<br>
theta(1,1)=100;<br>
theta(2,1)=120;<br>
theta(1,2)=.11;<br>
theta(2,2)=.11;<br>
<br>
choose.fill_randu(rng);  // use to pick group that each child belongs to<br>
choose1.fill_randu(rng);  // use to pick group that each child belongs to<br>
int i;<br>
for (i=1;i<=n;i++)   // loop over children<br>
{<br>
if (choose(i)<p1(1))<br>
{<br>
group(i,1)=1;  //child in group1<br>
}<br>
else<br>
{<br>
group(i,1)=2; // child in group2<br>
}<br>
if (group(i,1)==1)  // use probability that group 1 remains in group1<br>
if (choose1(i)<M(1,1))<br>
group(i,2)=1;    // remained in group1<br>
else<br>
group(i,2)=2; // moved to group2<br>
else<br>
if (choose1(i)<M(2,1)) // use probability that group 2 changes to<br>
group1<br>
group(i,2)=1;    // changed to group1<br>
else<br>
group(i,2)=2; // remained in group2<br>
}<br>
<br>
for (i=1;i<=n;i++)   // loop over children and get size data<br>
{<br>
size(i)(1,12)=growth_function(age(1,12),theta(group(i,1)));<br>
size(i)(13,16)=growth_function(age(13,16),theta(group(i,2)));<br>
}<br>
double rho2=rho*rho;<br>
for (i=1;i<=n;i++)   // loop over children and  add noise<br>
{<br>
eta.fill_randn(rng);<br>
eps(1)=eta(1);<br>
int j;<br>
for (j=2;j<=4;j++)   // loop over children and  add noise<br>
{<br>
eps(j)=rho*eps(j-1)+sqrt(1.0-rho2)*eta(j);<br>
}<br>
eps(5)=eta(1);<br>
for (j=6;j<=16;j++)   // loop over children and  add noise<br>
{<br>
eps(j)=rho*eps(j-1)+sqrt(1.0-rho2)*eta(j);<br>
}<br>
size(i)=elem_prod(size(i),exp(sigma*eps));  // lognormal error<br>
}<br>
<br>
ofstream ofs("growth.dat");<br>
ofs << "# nobs" << endl << n << endl ;<br>
ofs << "# nmeasure" << endl << nm << endl ;<br>
ofs << "# ages" << endl << setprecision(3) << setw(7) << age << endl ;<br>
ofs << "# observations" << endl << setprecision(3) << setw(7) << size<br>
<< endl ;<br>
ivector icount1(1,2);<br>
icount1.initialize();<br>
ivector icount2(1,2);<br>
icount2.initialize();<br>
for (i=1;i<=n;i++)   // loop over children<br>
{<br>
icount1(group(i,1))++;<br>
icount2(group(i,2))++;<br>
}<br>
cout << "proportions in groups " << icount1/sum(icount1) << endl;<br>
cout << "proportions in groups " << icount2/sum(icount2) << endl;<br>
}<br>
<br>
<br>
while the TPL code for the analyzer is<br>
<br>
DATA_SECTION<br>
init_int n<br>
init_int nm<br>
init_vector ages(1,nm)<br>
init_matrix sizes(1,n,1,nm)<br>
matrix q0(1,n,1,2)<br>
matrix q1(1,n,1,2)<br>
number lmin<br>
number lmax<br>
LOC_CALCS<br>
ages(13)=8;<br>
ages(14)=11;<br>
ages(15)=15;<br>
ages(16)=19;<br>
lmin=min(column(sizes,nm));<br>
lmax=max(column(sizes,nm));<br>
PARAMETER_SECTION<br>
init_bounded_vector p1coff(1,2,.001,1.1)<br>
init_bounded_vector p2coff(1,2,.001,1.1)<br>
vector p1(1,2)<br>
vector p2(1,2)<br>
number psum1<br>
number psum2<br>
init_bounded_vector Linf(1,2,0.9*lmin,1.1*lmax,2)<br>
init_bounded_vector k(1,2,.01,.3)<br>
init_bounded_number rho(-.1,.95,3)<br>
init_bounded_number lsigma(-5.0,5.0)<br>
number sigma<br>
matrix cor1(1,12,1,12)<br>
matrix cor2(1,4,1,4)<br>
matrix is1(1,12,1,12)<br>
matrix is2(1,4,1,4)<br>
matrix ic1(1,12,1,12)<br>
matrix ic2(1,4,1,4)<br>
matrix pred_size(1,2,1,nm)<br>
number lds1<br>
number lds2<br>
objective_function_value f<br>
PROCEDURE_SECTION<br>
get_correlation();<br>
get_proportions();<br>
get_predicted_sizes();<br>
get_likelihood();<br>
<br>
FUNCTION get_correlation<br>
f+=square(rho);<br>
<br>
sigma=exp(lsigma);<br>
dvariable vinv=1.0/(sigma*sigma);<br>
int i,j;<br>
for (i=1;i<=12;i++)<br>
for (j=1;j<=12;j++)<br>
cor1(i,j)=pow(rho,fabs(i-j));<br>
<br>
for (i=1;i<=4;i++)<br>
for (j=1;j<=4;j++)<br>
cor2(i,j)=pow(rho,fabs(i-j));<br>
<br>
ic1=inv(cor1);<br>
ic2=inv(cor2);<br>
<br>
is1=ic1*vinv;<br>
is2=ic2*vinv;<br>
<br>
lds1=ln_det(is1);<br>
lds2=ln_det(is2);<br>
<br>
FUNCTION get_proportions<br>
psum1=sum(p1coff);<br>
p1=p1coff/psum1;<br>
f+=100.*square(log(psum1));<br>
<br>
<br>
psum2=sum(p2coff);<br>
p2=p2coff/psum2;<br>
f+=100.*square(log(psum2));<br>
<br>
FUNCTION get_predicted_sizes<br>
lmax=max(column(sizes,nm));<br>
pred_size(1)=log(Linf(1)*(1.0-exp(-k(1)*ages)));<br>
pred_size(2)=log(Linf(2)*(1.0-exp(-k(2)*ages)));<br>
<br>
FUNCTION get_likelihood<br>
int i;<br>
dvar_matrix res(1,2,1,nm);<br>
for (i=1;i<=n;i++)<br>
{<br>
res(1)=log(sizes(i))-pred_size(1);<br>
res(2)=log(sizes(i))-pred_size(2);<br>
dvar_vector res1a=res(1)(1,12);<br>
dvar_vector res1b=(res(1)(13,16)).shift(1);<br>
dvar_vector res2a=res(2)(1,12);<br>
dvar_vector res2b=(res(2)(13,16)).shift(1);<br>
dvariable l1a=exp(-0.5*res1a*(is1*res1a));<br>
dvariable l1b=exp(-0.5*res1b*(is2*res1b));<br>
dvariable l2a=exp(-0.5*res2a*(is1*res2a));<br>
dvariable l2b=exp(-0.5*res2b*(is2*res2b));<br>
f-=log(1.e-10+(p1(1)*l1a+p1(2)*l2a));<br>
f-=log(1.e-10+(p2(1)*l1b+p2(2)*l2b));<br>
f-=0.5*(lds1+lds2);<br>
<br>
q0(i,1)=value(p1(1))*value(l1a);<br>
q0(i,2)=value(p1(2))*value(l2a);<br>
q0(i)/=sum(q0(i));<br>
q1(i,1)=value(p2(1))*value(l1b);<br>
q1(i,2)=value(p2(2))*value(l2b);<br>
q1(i)/=sum(q1(i));<br>
}<br>
REPORT_SECTION<br>
report << setprecision(4) << setw(8) << inv(is1) << endl;<br>
report << setprecision(4) << setw(8) << inv(is2) << endl;<br>
<br>
report << setprecision(4) << setw(8) << cor1 << endl;<br>
report << setprecision(4) << setw(8) << cor2 << endl;<br>
<br>
report << colsum(q0)/n << endl;<br>
report << colsum(q1)/n << endl;<br>
<br>
report << n << endl;<br>
report << q0 << endl;<br>
report << q1 << endl;<br>
<br>
<br>
I hope this illustrates how ADMB might be applied to growth models.<br>
<br>
Dave<br>
<br>
<br>
<br>
<br>
<br>
--<br>
David A. Fournier<br>
P.O. Box 2040,<br>
Sidney, B.C. V8l 3S3<br>