Skip to content

Commit

Permalink
first set of files
Browse files Browse the repository at this point in the history
  • Loading branch information
mronkko committed Oct 29, 2015
1 parent 378429f commit a01854c
Show file tree
Hide file tree
Showing 2 changed files with 357 additions and 0 deletions.
250 changes: 250 additions & 0 deletions pls.ado
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

107 changes: 107 additions & 0 deletions pls.sthlp
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}

0 comments on commit a01854c

Please sign in to comment.