-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
357 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
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' | ||
else local C`j' `1' | ||
} | ||
else{ | ||
confirm variable `1' | ||
else local i`j' "`i`j'' `1'" | ||
} | ||
|
||
} | ||
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 center `C`i'', standardize inplace | ||
} | ||
|
||
/* 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,1]/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 | ||
|
||
quietly center `allcomposites' if `touse', standardize inplace | ||
|
||
// 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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
{smcl} | ||
{* *! version 1.0 29oct2015}{...} | ||
{viewerjumpto "Syntax" "pls##syntax"}{...} | ||
{viewerjumpto "Description" "pls##description"}{...} | ||
{viewerjumpto "Options" "pls##options"}{...} | ||
{viewerjumpto "Remarks" "pls##remarks"}{...} | ||
{viewerjumpto "Examples" "pls##examples"}{...} | ||
{title:Title} | ||
|
||
{phang} | ||
{bf:pls} {hline 2} calculates composite variables using the partial least squares path modeling (PLS) algorithm | ||
|
||
|
||
{marker syntax}{...} | ||
{title:Syntax} | ||
|
||
{p 8 14 2} | ||
{cmd:pls} {cmd:(}{newvar:1} = {varlist:1}{cmd:)} | ||
{cmd:(}{newvar:2} = {varlist:2}{cmd:)} | ||
{it:...} {cmd:(}{newvar:N} = {varlist:N}{cmd:)} {ifin} | ||
[, {it:options}] | ||
|
||
{synoptset 50 tabbed}{...} | ||
{synopthdr} | ||
{synoptline} | ||
{syntab:Main} | ||
{synopt:{cmdab:a:djacent(}{varlist:1} [, {varlist:2}, {it:...}, {varlist:N}])}defines which composites are adjacent to each other{p_end} | ||
{synopt:{opt m:odeB}({varlist})}sets Mode B outer estimation{p_end} | ||
{synopt:{opt s:cheme(scheme)}}sets innner estimation scheme{p_end} | ||
{synoptline} | ||
{p2colreset}{...} | ||
{p 4 6 2} | ||
{cmd:by} is allowed; see {manhelp by D}.{p_end} | ||
|
||
{marker description}{...} | ||
{title:Description} | ||
|
||
{pstd} | ||
{cmd:pls} calculates composite variables using the partial least squares path | ||
modeling (PLS) algorithm. | ||
|
||
{pstd} | ||
The composites are calculated as weighted combinations of existing variables | ||
using the weight algorithm introduced by Wold (see {help pls##wold1982:Wold (1982)}). | ||
The produces composites are indentical to the composties produced by commercial | ||
PLS software as well as the open source | ||
{browse "https://cran.r-project.org/web/packages/matrixpls/index.html":matrixpls} | ||
R package except for small numerical differences due to different convergence | ||
criterion. | ||
|
||
{marker options}{...} | ||
{title:Options} | ||
|
||
{dlgtab:Main} | ||
|
||
{phang} | ||
{cmd:adjacent(}{varlist:1} [, {varlist:2}, {it:...}, {varlist:N}]) defines which composites are adjacent to each other during inner | ||
estimation. The first variable of varlist is defined as being adjacent to the | ||
other variables. If the path scheme is used for inner estimation, the | ||
directionality of adjacencies is from the other variables toward the first | ||
variable. | ||
|
||
{phang} | ||
{opt modeB}({varlist})}sets Mode B outer estimation for the composites in varlist. All other composites are calculated with Mode A outer estimation. | ||
|
||
{phang} | ||
{opt scheme(scheme)} sets innner estimation scheme. Valid values for scheme are centroid, factor, and path. The default is the centroid method.{p_end} | ||
|
||
{marker remarks}{...} | ||
{title:Remarks} | ||
|
||
{phang} | ||
This program is provided for educational purposes. It is difficult to recommend | ||
the PLS composites for any serious empirical work | ||
(see {help pls##rma2015:Ršnkkš, McIntosh, and Antonakis (2015)}) | ||
|
||
{marker examples}{...} | ||
{title:Example} | ||
|
||
{pstd}Setup{p_end} | ||
{phang2}{cmd:. webuse sem_sm2}{p_end} | ||
|
||
{pstd}Calculate PLS composites{p_end} | ||
{phang2}{cmd:. pls (Alien67 = anomia67 pwless67)}{break} | ||
{cmd:(Alien71 = anomia71 pwless71)}{break} | ||
{cmd:(SES = educ66 occstat66),}{break} | ||
{cmd:adjacent(Alien71 Alien67 SES, Alien67 SES)}{break} | ||
{cmd:modeB(SES)}{break} | ||
{cmd:scheme("path")}{break} | ||
|
||
{pstd}Regression between composites{p_end} | ||
{phang2}{cmd:. regress Alien67 SES}{p_end} | ||
{phang2}{cmd:. regress Alien71 Alien67 SES}{p_end} | ||
|
||
{marker references}{...} | ||
{title:References} | ||
|
||
{marker wold1982}{...} | ||
{phang} | ||
Wold, H. (1982). Soft modeling: The basic design and some extensions. | ||
In K. G. Jšreskog & S. Wold (Eds.), {it:Systems under indirect observation?: causality, structure, prediction} (pp. 1Ð54). Amsterdam: North-Holland. | ||
|
||
{marker rma2015}{...} | ||
{phang} | ||
Ršnkkš, M., McIntosh, C. N., & Antonakis, J. (2015). On the adoption of partial least squares in psychological research: Caveat emptor. {it:Personality and Individual Differences}, (87), 76Ð84. | ||
{browse "http://doi.org/10.1016/j.paid.2015.07.019":DOI:10.1016/j.paid.2015.07.019} | ||
{p_end} |