You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
504 lines
15 KiB
Plaintext
504 lines
15 KiB
Plaintext
9 months ago
|
*! Mata definitions for confa package; 3 March 2009; v.2.0
|
||
|
|
||
|
set matalnum on
|
||
|
|
||
|
mata:
|
||
|
|
||
|
mata set matastrict on
|
||
|
mata clear
|
||
|
|
||
|
//////////////////////////////////////////////
|
||
|
// needed by confa.ado
|
||
|
|
||
|
real CONFA_NF( string input ) {
|
||
|
real scalar nopenpar, nclosepar, ncolon;
|
||
|
// opening and closing parentheses
|
||
|
|
||
|
nopenpar = length(tokens(input, ")" ))
|
||
|
nclosepar = length(tokens(input, "(" ))
|
||
|
// n*par will be 2*nf
|
||
|
ncolon = length(tokens(input, ":" ))
|
||
|
// ncolon will be 2*nf + 1
|
||
|
|
||
|
if ( (nopenpar == nclosepar) & (nopenpar == ncolon-1 ) ) {
|
||
|
if (mod(nopenpar,2) == 0) {
|
||
|
return( nopenpar/2 )
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if everything was OK, should've exited by now
|
||
|
// if something's wrong, return zero
|
||
|
return(0)
|
||
|
}
|
||
|
|
||
|
matrix CONFA_StrucToSigma(real vector parms) {
|
||
|
real scalar CONFA_loglevel, nobsvar, nfactors, eqno;
|
||
|
real matrix Lambda, Phi, Theta, Sigma, CONFA_Struc;
|
||
|
|
||
|
// loglevel
|
||
|
CONFA_loglevel = strtoreal( st_global("CONFA_loglevel"))
|
||
|
|
||
|
CONFA_Struc = st_matrix("CONFA_Struc")
|
||
|
if (CONFA_loglevel>4) CONFA_Struc
|
||
|
|
||
|
if (CONFA_loglevel>4) {
|
||
|
printf("{txt}Current parameter values:\n")
|
||
|
parms
|
||
|
}
|
||
|
|
||
|
// the length should coinicde with the # pars from CONFA_Struc
|
||
|
if ( cols(parms) ~= rows(CONFA_Struc) ) {
|
||
|
// something's wrong, let's just drop out with an empty matrix
|
||
|
if (CONFA_loglevel>4) {
|
||
|
printf("{txt}Expected parameters: {res}%3.0f{txt}; received parameters: {res}%3.0f\n",
|
||
|
rows(CONFA_Struc),cols(parms))
|
||
|
}
|
||
|
return(J(0,0,0))
|
||
|
}
|
||
|
|
||
|
// # observed variables: max entry in the number of means
|
||
|
nobsvar = colmax( select(CONFA_Struc[,3], !(CONFA_Struc[,1]-J(rows(CONFA_Struc),1,1)) ) )
|
||
|
if (CONFA_loglevel>4) printf("{txt}No. of observed variables: {res}%3.0f\n",nobsvar)
|
||
|
|
||
|
// # observed factors: max entry in the phi indices
|
||
|
nfactors = colmax( select(CONFA_Struc[,3], !(CONFA_Struc[,1]-J(rows(CONFA_Struc),1,3)) ) )
|
||
|
if (CONFA_loglevel>4) printf("{txt}No. of latent factors: {res}%3.0f\n",nfactors)
|
||
|
|
||
|
// set up the matrices
|
||
|
Lambda = J(nobsvar,nfactors,0)
|
||
|
Phi = J(nfactors,nfactors,0)
|
||
|
Theta = J(nobsvar,nobsvar,0)
|
||
|
|
||
|
// fill the stuff in
|
||
|
for(eqno=nobsvar+1;eqno<=rows(CONFA_Struc);eqno++) {
|
||
|
if (CONFA_Struc[eqno,1] == 2) {
|
||
|
// a lambda-type entry
|
||
|
Lambda[ CONFA_Struc[eqno,3], CONFA_Struc[eqno,4] ] = parms[eqno]
|
||
|
}
|
||
|
if (CONFA_Struc[eqno,1] == 3) {
|
||
|
// a phi-type entry
|
||
|
Phi[ CONFA_Struc[eqno,3], CONFA_Struc[eqno,4] ] = parms[eqno]
|
||
|
Phi[ CONFA_Struc[eqno,4], CONFA_Struc[eqno,3] ] = parms[eqno]
|
||
|
}
|
||
|
if (CONFA_Struc[eqno,1] == 4) {
|
||
|
// a theta-type entry
|
||
|
Theta[ CONFA_Struc[eqno,3], CONFA_Struc[eqno,3] ] = parms[eqno]
|
||
|
}
|
||
|
if (CONFA_Struc[eqno,1] == 5) {
|
||
|
// a theta-type correlated errors entry
|
||
|
Theta[ CONFA_Struc[eqno,3], CONFA_Struc[eqno, 4] ] = parms[eqno]
|
||
|
Theta[ CONFA_Struc[eqno,4], CONFA_Struc[eqno, 3] ] = parms[eqno]
|
||
|
}
|
||
|
}
|
||
|
if (CONFA_loglevel > 4) {
|
||
|
printf("{txt}Loadings:\n")
|
||
|
Lambda
|
||
|
printf("{txt}Factor covariances:\n")
|
||
|
Phi
|
||
|
printf("{txt}Residual variances:\n")
|
||
|
Theta
|
||
|
}
|
||
|
Sigma = Lambda*Phi*Lambda' + Theta
|
||
|
if (CONFA_loglevel > 4) {
|
||
|
printf("{txt}Implied moments:\n")
|
||
|
Sigma
|
||
|
}
|
||
|
|
||
|
if (CONFA_loglevel == -1) {
|
||
|
// post matrices to Stata
|
||
|
st_matrix("CONFA_Lambda",Lambda)
|
||
|
st_matrix("CONFA_Phi",Phi)
|
||
|
st_matrix("CONFA_Theta",Theta)
|
||
|
st_matrix("CONFA_Sigma",Sigma)
|
||
|
}
|
||
|
|
||
|
// done with model structure, compute and return implied matrix
|
||
|
return( Sigma )
|
||
|
}
|
||
|
|
||
|
// vech covariance matrix, for Satorra-Bentler
|
||
|
void SBvechZZtoB(string dlist, string blist) {
|
||
|
real matrix data, moments, B;
|
||
|
real scalar i;
|
||
|
|
||
|
// view the deviation variables
|
||
|
st_view(data=.,.,tokens(dlist))
|
||
|
// view the moment variables
|
||
|
// blist=st_local("blist")
|
||
|
st_view(moments=.,.,tokens(blist))
|
||
|
// vectorize!
|
||
|
for(i=1; i<=rows(data); i++) {
|
||
|
B = data[i,.]'*data[i,.]
|
||
|
moments[i,.] = vech(B)'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// duplication matrix, for Satorra-Bentler
|
||
|
void Dupl(scalar p, string Dname) {
|
||
|
real scalar pstar, k;
|
||
|
real matrix Ipstar, D;
|
||
|
|
||
|
pstar = p*(p+1)/2
|
||
|
Ipstar = I(pstar)
|
||
|
D = J(p*p,0,.)
|
||
|
for(k=1;k<=pstar;k++) {
|
||
|
D = (D, vec(invvech(Ipstar[.,k])))
|
||
|
}
|
||
|
st_matrix(Dname,D)
|
||
|
}
|
||
|
|
||
|
// Satorra-Bentler Delta matrix
|
||
|
// Delta = \frac \partial{\partial \theta} vech \Sigma(\theta)
|
||
|
void SBStrucToDelta(string DeltaName) {
|
||
|
real scalar CONFA_loglevel, p, t, varno, facno, i, j, k, fac1, fac2, k1, k2;
|
||
|
// log level, # obs vars, # parameters, current var, current factor, cycle indices, temp indices
|
||
|
real matrix Lambda, Phi, Theta, Sigma, CONFA_Struc, Delta, DeltaRow;
|
||
|
// must be self-explanatory
|
||
|
real matrix U, E;
|
||
|
// identity matrices of the size #factors and #obs vars
|
||
|
|
||
|
// loglevel
|
||
|
|
||
|
CONFA_loglevel = strtoreal( st_global("CONFA_loglevel"))
|
||
|
|
||
|
// need the CONFA matrices
|
||
|
CONFA_Struc = st_matrix("CONFA_Struc")
|
||
|
Sigma = st_matrix("CONFA_Sigma")
|
||
|
Lambda = st_matrix("CONFA_Lambda")
|
||
|
Phi = st_matrix("CONFA_Phi")
|
||
|
// Theta = st_matrix("CONFA_Theta")
|
||
|
|
||
|
if (CONFA_loglevel>4) CONFA_Struc
|
||
|
|
||
|
// # parameters in the model
|
||
|
t = rows(CONFA_Struc)
|
||
|
|
||
|
// cols(Delta) = t = # pars
|
||
|
// rows(Delta) = pstar = p*(p+1)/2 = length( vech( Sigma ) )
|
||
|
// but that should be accumulated one by one...
|
||
|
Delta = J(0,t,.)
|
||
|
|
||
|
// sources of u and e vectors
|
||
|
p = rows( Sigma )
|
||
|
U = I( p )
|
||
|
E = I( rows(Phi) )
|
||
|
|
||
|
for(i=1;i<=p;i++) {
|
||
|
for(j=i;j<=p;j++) {
|
||
|
if (CONFA_loglevel > 4) printf("{txt}Working with pair ({res}%2.0f{txt},{res}%2.0f{txt})\n",i,j)
|
||
|
DeltaRow = J(1,t,0)
|
||
|
// parse Struc matrix and see how each parameter affects Cov(X_i,X_j)
|
||
|
for(k=1;k<=t;k++) {
|
||
|
if (CONFA_Struc[k,1] == 1) {
|
||
|
// a mean-type entry
|
||
|
// for the moment, assume it does not affect anything
|
||
|
}
|
||
|
if (CONFA_Struc[k,1] == 2) {
|
||
|
// a lambda-type entry
|
||
|
// CONFA_Struc[k,.] = (2, equation #, variable #, factor #)
|
||
|
varno = CONFA_Struc[k,3]
|
||
|
facno = CONFA_Struc[k,4]
|
||
|
DeltaRow[1,k] = U[i,.] * U[.,varno] * E[facno,.] * Phi * Lambda' * U[.,j] +
|
||
|
U[i,.] * Lambda * Phi * E[.,facno] * U[varno,.] * U[.,j]
|
||
|
}
|
||
|
if (CONFA_Struc[k,1] == 3) {
|
||
|
// a phi-type entry
|
||
|
// CONFA_Struc[k,.] = (3, equation #, `factor`kk'', `factor`k'')
|
||
|
fac1 = CONFA_Struc[k,3]
|
||
|
fac2 = CONFA_Struc[k,4]
|
||
|
DeltaRow[1,k] = U[i,.] * Lambda * E[.,fac1] * E[fac2,.] * Lambda' * U[.,j]
|
||
|
}
|
||
|
if (CONFA_Struc[k,1] == 4) {
|
||
|
// a theta-type entry
|
||
|
// CONFA_Struc[k,.] = (4, equation #, variable #, 0)
|
||
|
varno = CONFA_Struc[k,3]
|
||
|
DeltaRow[1,k] = (i==j) & (i==varno)
|
||
|
}
|
||
|
if (CONFA_Struc[k,1] == 5) {
|
||
|
// a theta_{jk}-type entry
|
||
|
// CONFA_Struc[k,.] = (5, equation #, variable k1, variable k2)
|
||
|
k1 = CONFA_Struc[k,3]
|
||
|
k2 = CONFA_Struc[k,4]
|
||
|
DeltaRow[1,k] = ((i==k1) & (j==k2) ) | ((i==k2) & (j==k1))
|
||
|
}
|
||
|
}
|
||
|
Delta = Delta \ DeltaRow
|
||
|
}
|
||
|
}
|
||
|
|
||
|
st_matrix(DeltaName,Delta)
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////
|
||
|
// needed by confa_p.ado
|
||
|
|
||
|
void CONFA_P_EB(string Fnames, string ObsVarNames, string ToUseName) {
|
||
|
real matrix ff, xx;
|
||
|
// views
|
||
|
real matrix bb, Sigma, Lambda, Theta, Phi;
|
||
|
// substantive matrices
|
||
|
real scalar p
|
||
|
|
||
|
// view on the newly generated factors
|
||
|
st_view(ff=.,.,tokens(Fnames),ToUseName)
|
||
|
|
||
|
// view on the observed variables
|
||
|
st_view(xx=.,.,tokens(ObsVarNames),ToUseName)
|
||
|
|
||
|
// get the estimated matrices
|
||
|
bb = st_matrix("e(b)")
|
||
|
Sigma = st_matrix("e(Sigma)")
|
||
|
Theta = st_matrix("e(Theta)")
|
||
|
Lambda = st_matrix("e(Lambda)")
|
||
|
Phi = st_matrix("e(Phi)")
|
||
|
|
||
|
// # observed vars
|
||
|
p = rows(Sigma)
|
||
|
|
||
|
// prediction
|
||
|
ff[,] = (xx-J(rows(xx),1,1)*bb[1..p]) * invsym(Sigma) * Lambda * Phi
|
||
|
}
|
||
|
|
||
|
void CONFA_P_MLE(string Fnames, string ObsVarNames, string ToUseName) {
|
||
|
real matrix ff, xx;
|
||
|
// views
|
||
|
real matrix bb, Sigma, Lambda, Theta, Phi, ThetaInv;
|
||
|
// substantive matrices
|
||
|
real scalar p
|
||
|
|
||
|
// view on the newly generated factors
|
||
|
st_view(ff=.,.,tokens(Fnames),ToUseName)
|
||
|
|
||
|
// view on the observed variables
|
||
|
st_view(xx=.,.,tokens(ObsVarNames),ToUseName)
|
||
|
|
||
|
// get the estimated matrices
|
||
|
bb = st_matrix("e(b)")
|
||
|
Sigma = st_matrix("e(Sigma)")
|
||
|
Theta = st_matrix("e(Theta)")
|
||
|
Lambda = st_matrix("e(Lambda)")
|
||
|
Phi = st_matrix("e(Phi)")
|
||
|
|
||
|
// # observed vars
|
||
|
p = rows(Sigma)
|
||
|
|
||
|
// Theta is the vector of diagonal elements,
|
||
|
// so the inverse is easy!
|
||
|
ThetaInv = diag( 1:/Theta )
|
||
|
|
||
|
// prediction
|
||
|
ff[,] = (xx-J(rows(xx),1,1)*bb[1..p]) * ThetaInv * Lambda * invsym(Lambda' * ThetaInv * Lambda)
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////
|
||
|
// needed by confa_lf.ado
|
||
|
|
||
|
void CONFA_NormalLKHDr( string ParsName, string lnfname) {
|
||
|
// ParsName are the parameters
|
||
|
// lnfname is the name of the likelihood variable
|
||
|
// the observed variables are in $CONFA_obsvar
|
||
|
|
||
|
real scalar CONFA_loglevel, nobsvar, ldetWS, i;
|
||
|
// log level, # obs vars, log determinant, cycle index
|
||
|
real matrix Sigma, means, SS, InvWorkSigma;
|
||
|
// intermediate computations
|
||
|
string scalar obsvar, touse;
|
||
|
// list of observed variables
|
||
|
real matrix data, lnl, parms;
|
||
|
// views
|
||
|
|
||
|
CONFA_loglevel = strtoreal( st_global("CONFA_loglevel"))
|
||
|
|
||
|
obsvar = st_global("CONFA_obsvar")
|
||
|
nobsvar = length(tokens(obsvar))
|
||
|
|
||
|
touse = st_global("CONFA_touse")
|
||
|
|
||
|
st_view(data=., ., tokens(obsvar), touse )
|
||
|
st_view(lnl=., ., tokens(lnfname), touse)
|
||
|
st_view(parms=., ., tokens(ParsName), touse)
|
||
|
|
||
|
// using the set up where the means are the first nobsvar entries of the parameter vector,
|
||
|
means = parms[1,1..nobsvar]
|
||
|
|
||
|
Sigma = CONFA_StrucToSigma(parms[1,.])
|
||
|
|
||
|
if (CONFA_loglevel > 2) {
|
||
|
parms[1,.]
|
||
|
means
|
||
|
Sigma
|
||
|
}
|
||
|
|
||
|
// do some equilibration??
|
||
|
|
||
|
SS = cholesky(Sigma)
|
||
|
InvWorkSigma = solvelower(SS,I(rows(SS)))
|
||
|
InvWorkSigma = solveupper(SS',InvWorkSigma)
|
||
|
ldetWS = 2*ln(dettriangular(SS))
|
||
|
|
||
|
for( i=1; i<=rows(data); i++ ) {
|
||
|
lnl[i,1] = -.5*(data[i,.]-means)*InvWorkSigma*(data[i,.]-means)' - .5*ldetWS - .5*nobsvar*ln(2*pi())
|
||
|
}
|
||
|
|
||
|
if (CONFA_loglevel>2) {
|
||
|
sum(lnl)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// normal likelihood with missing data
|
||
|
void CONFA_NormalLKHDrMiss( string ParsName, string lnfname) {
|
||
|
// ParsName are the parameters
|
||
|
// lnfname is the name of the likelihood variable
|
||
|
// the observed variables are in $CONFA_obsvar
|
||
|
|
||
|
|
||
|
real scalar CONFA_loglevel, nobsvar, thisldetWS, i, j;
|
||
|
// log level, # obs vars, log determinant, cycle index
|
||
|
real matrix Sigma, means, thisSigma, thisSS, thisInvSigma, thispattern, parms;
|
||
|
// intermediate computations
|
||
|
string scalar obsvar, misspat, touse;
|
||
|
// list of observed variables; the names of the missing patterns and touse tempvars
|
||
|
real matrix data, lnl, parmview, pattern, mdata, mlnl, info;
|
||
|
// views
|
||
|
|
||
|
CONFA_loglevel = strtoreal( st_global("CONFA_loglevel"))
|
||
|
|
||
|
obsvar = st_global("CONFA_obsvar")
|
||
|
nobsvar = length(tokens(obsvar))
|
||
|
|
||
|
misspat = st_global("CONFA_miss")
|
||
|
touse = st_global("CONFA_touse")
|
||
|
|
||
|
st_view(pattern=., ., misspat, touse )
|
||
|
st_view(data=., ., tokens(obsvar), touse )
|
||
|
st_view(lnl=., ., lnfname, touse )
|
||
|
|
||
|
// STILL USING THE FIRST OBSERVATIONS TO GET THE PARAMETERS!!!
|
||
|
st_view(parmview=., ., tokens(ParsName), touse )
|
||
|
parms = parmview[1,1..cols(parmview)]
|
||
|
|
||
|
if (CONFA_loglevel>2) {
|
||
|
obsvar
|
||
|
parms
|
||
|
}
|
||
|
|
||
|
// using the set up where the means are the first nobsvar entries of the parameter vector,
|
||
|
means = parms[1..nobsvar]
|
||
|
|
||
|
Sigma = CONFA_StrucToSigma(parms)
|
||
|
|
||
|
// utilize an existing set up of the missing data patterns
|
||
|
// data assumed to be sorted by the patterns of missing data
|
||
|
|
||
|
info = panelsetup( pattern, 1 )
|
||
|
|
||
|
for (i=1; i<=rows(info); i++) {
|
||
|
panelsubview(mdata=., data, i, info)
|
||
|
panelsubview(mlnl=., lnl, i, info)
|
||
|
// mdata should contain the portion of the data with the same missing data pattern
|
||
|
// mlnl will be conforming to mdata
|
||
|
|
||
|
// OK, now need to figure out that pattern
|
||
|
thispattern = J(1, cols(data), 1) - colmissing( mdata[1,] )
|
||
|
if (CONFA_loglevel > 2) {
|
||
|
printf("{txt}Pattern #{res}%5.0f{txt} :", i)
|
||
|
thispattern
|
||
|
};
|
||
|
|
||
|
// modify the matrices
|
||
|
|
||
|
thisSigma = select( select( Sigma, thispattern), thispattern' )
|
||
|
thisSS = cholesky(thisSigma)
|
||
|
thisInvSigma = solvelower(thisSS,I(rows(thisSS)))
|
||
|
thisInvSigma = solveupper(thisSS',thisInvSigma)
|
||
|
thisldetWS = 2*ln(dettriangular(thisSS))
|
||
|
|
||
|
if (CONFA_loglevel > 3) {
|
||
|
thisSigma
|
||
|
thisInvSigma
|
||
|
};
|
||
|
|
||
|
for( j=1; j<=rows(mdata); j++ ) {
|
||
|
// this is actually a single line broken by arithmetic operator signs
|
||
|
// that's bad style but it works
|
||
|
mlnl[j,1] = -.5*(select(data[j,.],thispattern)-select(means,thispattern)) *
|
||
|
thisInvSigma *
|
||
|
(select(data[j,.],thispattern)-select(means,thispattern))' -
|
||
|
.5*thisldetWS - .5*sum(thispattern)*ln(2*pi())
|
||
|
}
|
||
|
|
||
|
if (CONFA_loglevel>3) {
|
||
|
mlnl
|
||
|
};
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
// Bollen-Stine bootstrap rotation
|
||
|
void CONFA_BSrotate(
|
||
|
string SigmaName, // the parameter matrix name
|
||
|
string varnames // the variable names
|
||
|
) {
|
||
|
|
||
|
// declarations
|
||
|
real matrix data // views of the data
|
||
|
real matrix Sigma, SS, S2, SS2 // the covariance matrices and temp matrices
|
||
|
real matrix means // the means -- need modifications for weighted data!!!
|
||
|
real scalar n // dimension, no. obs
|
||
|
|
||
|
// get the data in
|
||
|
st_view(data=., ., tokens(varnames) )
|
||
|
n=rows(data)
|
||
|
|
||
|
Sigma = st_matrix(SigmaName)
|
||
|
|
||
|
// probability weights!!!
|
||
|
means = colsum(data)/n
|
||
|
SS = (cross(data,data)-n*means'*means)/(n-1)
|
||
|
|
||
|
S2 = cholesky(Sigma)
|
||
|
SS2 = cholesky(SS)
|
||
|
SS2 = solveupper(SS2',I(rows(SS)))
|
||
|
|
||
|
data[,] = data*SS2*S2'
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
// build a library
|
||
|
mata mlib create lconfa, replace
|
||
|
mata mlib add lconfa *()
|
||
|
mata mlib index
|
||
|
|
||
|
end
|
||
|
// of mata
|
||
|
|
||
|
exit
|
||
|
|
||
|
// don't need this:
|
||
|
|
||
|
string scalar CONFA_UL( string input ) {
|
||
|
|
||
|
string rowvector s;
|
||
|
real scalar i,j,n;
|
||
|
|
||
|
// tokenize input into a string vector
|
||
|
s = tokens( input )
|
||
|
n = cols( s )
|
||
|
for(i=1;i<=n;i++) {
|
||
|
// as I go over the elements, compare to the previous ones
|
||
|
for(j=1;j<i;j++) {
|
||
|
if ( s[i] == s[j] ) {
|
||
|
s[i] = ""
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// assemble back into a string scalar
|
||
|
return( stritrim(invtokens( s ) ) )
|
||
|
}
|