version 10 cap program drop pls program pls, rclass byable(recall) syntax anything(id="Indicator blocks" name=blocks equalok) [if] [in] , /// Adjacent(string) /// [ ModeB(namelist) /// Scheme(string)] /* Parse the specified blocks. * Macros: * C`i' - composite name * i`i' - indicators for composite, by composite index * i`C`i'' - indicators for composite, by composite name * allindicators - all indicators * allcomposites - all composites */ tokenize `"`blocks'"', parse(" ()=") scalar inblock = 0 scalar startblock = 0 // Count of composites local j 0 while "`1'"~=""{ if "`1'"=="(" { if inblock{ di as error "Unexpected (" error 197 } scalar inblock = 1 scalar startblock = 1 local ++j } else if inblock{ if "`1'"==")" { if("C`j'"=="" || "i`j'"==""){ di as error "Incomplete block specification" error 197 } else{ scalar inblock = 0 local i`C`j'' `i`j'' local allindicators "`allindicators' `i`j''" local allcomposites "`allcomposites' `C`j''" } } else if "`1'"=="=" { scalar startblock = 0 } else if startblock { if "`C`j''" ~= ""{ di as error "Missing =" error 197 } confirm new variable `1' local C`j' `1' } else{ foreach var of varlist `1'{ local i`j' "`i`j'' `var'" } } } else error 197 macro shift } if inblock{ di as error "Missing )" error 197 } /* End of parsing the blocks */ /* Parse the inner weight scheme */ if "`scheme'" == "" local scheme "centroid" if ! ("`scheme'" == "centroid" || "`scheme'" == "factor" || "`scheme'" == "path"){ di as error "scheme must be either centroid, factor, or path" error 198 } /* Set obs to use */ marksample touse, novarlist markout `touse'`allindicators' quietly count if `touse' if r(N) == 0 error 2000 /* Initialize composites with equal weights */ foreach i of numlist 1/`j'{ quietly egen `C`i'' = rowtotal(`i`i'') if `touse' quietly sum `C`i'' quietly replace `C`i'' = (`C`i''-r(mean))/r(sd) } /* Parse adjacencies */ tokenize `"`adjacent'"', parse(",") while "`1'"~=""{ local 0 `1' syntax varlist(min=2) // Use macros r`var' and c`compositename' to store whether // the adjacency is treated as directional in path weighting scheme local dv: word 1 of `varlist' local ivs: list varlist - dv local r`dv' `r`dv'' `ivs' foreach iv in `ivs'{ local c`iv' `c`iv'' `dv' } macro shift macro shift } foreach var in `allcomposites'{ if "`r`var''`c`var''"== ""{ di as error "Composite `var' is not adjacent to any other composite" error 198 } if("`scheme'" == "path"){ local c`var': list uniq c`var' local r`var': list uniq r`var' local c`var': list c`var' - r`var' } else{ local c`var' `c`var'' `r`var'' local c`var': list uniq c`var' local r`var' "" } } /* Verify Mode B specification and create Mode A specification*/ local 0 `modeB' syntax [varlist] local modeA: list allcomposites - modeB /* * Start of the PLS weight algorithm */ scalar converged = 0 scalar iteration = 0 while(!converged){ // Inner estimation. The three commonly used schemes are // Centroid: The sign of correlations // Factor: The correlations // Path: Correlations and regresions // Update the composites as weighted sums of adjacent composites. // These are stored as separate temporary variables // for computational reasons foreach var in `allcomposites'{ tempvar t`var' // Inner estimation with regression weights (path) if("`r`var''"!=""){ quietly regress `var' `r`var'' if `touse' quietly predict `t`var'' if `touse' } else quietly generate `t`var'' = 0 if `touse' // Inner estimation with correlational weights (all schemes) foreach var2 in `c`var''{ quietly correlate `var' `var2' if `touse' matrix define C = r(C) if "`scheme'" == "centroid" quietly replace `t`var'' = `t`var'' + `var2' * C[1,2]/abs(C[1,2]) if `touse' else quietly replace `t`var'' = `t`var'' + `var2' * C[1,2] if `touse' } } // Store weights in matrix. These are unscaled and only used for // convergence check matrix define W = 0 // Outer estimation (Mode A) foreach var in `modeA'{ quietly replace `var' = 0 if `touse' foreach var2 in `i`var''{ quietly correlate `t`var'' `var2' if `touse' matrix define C = r(C) matrix define W = W , C[1,2] quietly replace `var' = `var' + `var2' * C[1,2] if `touse' } } // Outer estimation (Mode B) foreach var in `modeB'{ tempvar tv quietly regress `t`var'' `i`var'' if `touse' matrix define W = W , e(b) quietly predict `tv' if `touse' quietly replace `var' = `tv' if `touse' quietly drop `tv' } // Clean up foreach var in `allcomposites'{ quietly drop `t`var'' } // Standardize the composites foreach var of varlist `allcomposites'{ quietly sum `var' quietly replace `var' = (`var'-r(mean))/r(sd) } // Convergence check:compare new weights (W) with the weights from previous // iteration (Wold). if(iteration > 0) scalar converged = mreldif(W, Wold) < 0.00001 matrix define Wold = W scalar iteration = iteration +1 // No convergence if(iteration>1000) error 430 } return scalar iterations = iteration end