Binary data in state space models

Generate some data

Here we specify a bivariate latent process where the 1st process affects the 2nd, and there are stable individual differences in the processes.

library(ctsem)
gm <- ctModel(LAMBDA=diag(2), #diagonal factor loading, 2 latents 2 observables
  Tpoints = 50,
  DRIFT=matrix(c(-1,.5,0,-1),2,2), #temporal dynamics
  TRAITVAR = diag(.5,2), #stable latent intercept variance (cholesky factor)
  DIFFUSION=diag(2)) #within person covariance 

ctModelLatex(gm) #to view latex system equations

#when generating data, free pars are set to 0
d <- data.frame(ctGenerate(ctmodelobj = gm,n.subjects = 100,
  burnin = 20,dtmean = .1))

We didn’t specify any measurement error in the above code, because we’re going to mix the regular Gaussian approach with binary data measurements:

d$Y2binary <-rbinom(n = nrow(d),size = 1, #create binary data based on the latent
  prob = ctsem::inv_logit(d$Y2))
d$Y1 <- d$Y1 + rnorm(nrow(d),0,.2) #gaussian measurement error

Now we setup our model for estimation – we no longer hard code the values of the simulation! We also fix the MANIFESTMEANS (measurement intercept) and free the CINT (continuous / latent process intercept) because that will be more appropriate if there are stable differences in the latent process. In Gaussian cases this choice of how to identify the model very often makes no difference, but it is more crucial with non-linearity. Note that there are many system matrices left unspecified, which will be freely estimated by default, and note also that correlated individual differences are enabled by default for all intercept terms (in this case, inital latent process intercepts, and continuous intercepts). The final step is to specify the measurement model by altering the model object directly – once the binary feature gets more development time I will probably create a nicer approach for this. 0 for the first manifest specifies that it is Gaussian, while the 1 specifies that our second variable (listed in manifestNames) is binary.

m <- ctModel(LAMBDA=diag(2),type='stanct',
  manifestNames = c('Y1','Y2binary'),
  MANIFESTMEANS = 0,
  CINT=c('cint1','cint2'))
##      [,1]
## [1,]  "0"
## [2,]  "0"
##      [,1]   
## [1,] "cint1"
## [2,] "cint2"

m$manifesttype=c(0,1)

Then we fit and summarise the model – note that at time of writing the latex output doesn’t know about the binary measurement model for the 2nd observable, and incorrectly shows the errors as Gaussian.

f <- ctStanFit(datalong = d,ctstanmodel = m,cores=6)
s=summary(f)
print(s$popmeans)
##                    mean     sd    2.5%     50%   97.5%
## T0m_eta1        -0.0458 0.0869 -0.1312 -0.0686  0.0985
## T0m_eta2        -0.0922 0.1993 -0.2988 -0.1488  0.2078
## drift_eta1      -1.1594 0.2148 -1.4699 -1.1525 -0.9054
## drift_eta1_eta2 -0.0301 0.0748 -0.1163 -0.0441  0.0834
## drift_eta2_eta1  0.2036 0.2476 -0.1248  0.1879  0.5339
## drift_eta2      -1.5657 0.9102 -3.1002 -1.2331 -0.9221
## diff_eta1        1.0299 0.0259  0.9899  1.0360  1.0600
## diff_eta2_eta1   0.1112 0.2679 -0.2749  0.1465  0.4524
## diff_eta2        1.1231 0.2898  0.8680  1.0219  1.5403
## mvarY1           0.1919 0.0054  0.1826  0.1938  0.1953
## cint1            0.0257 0.1049 -0.1149  0.0126  0.1470
## cint2            0.2688 0.1645  0.0552  0.2704  0.4969
ctModelLatex(f)

Then add a few plots showing temporal regression coefficients conditional on time interval, measurement expectations conditional on estimated parameters and past data, and finally latent states conditional on estimated parameters and both past and future data.

ctStanDiscretePars(f,plot=T)


ctKalman(f,subjects = c(2,5),plot=T)

ctKalman(f,subjects = c(2,5),plot=T,kalmanvec=c('etasmooth'))

Avatar
Charles Driver
Research Scientist

I’m a quantitative psychologist interested in the dynamic systems perspective of human systems.

comments powered by Disqus