## SAS/IML V8 Modules for Robust Regression

``` /****************************************************************/
/*          S A S   S A M P L E   L I B R A R Y                 */
/*                                                              */
/*    NAME: ROBUSTMC                                            */
/*   TITLE: SAS/IML V8 Modules for Robust Regression            */
/* PRODUCT: IML                                                 */
/*  SYSTEM: ALL                                                 */
/*    KEYS:                                                     */
/*   PROCS:                                                     */
/*    DATA:                                                     */
/*                                                              */
/* SUPPORT: GJW                         UPDATE: FEB 97          */
/*     REF:                                                     */
/*    MISC:                                                     */
/* Modules: PRILMTS - PRINTING LMS AND LMT RESULTS
/*          SCATLMS - PLOTTING LMS AND LMT RESULTS
/*          PRIMVE  - PRINTING MVE RESULTS
/*          SCATMVE - PLOTTING MVE RESULTS
/*          SCATMCD - PLOTTING MCD RESULTS
/*          LMSDIAP - DIAGNOSTIC OUTLIER PLOT
/****************************************************************/

proc iml;
/*------------------------------------------------------------*/

start prilmts(job,sc,coef,wgt);
/* job=1: LMS
=2: LMS + LS
=3: LMS + RLS
=4: LTS
=5: LTS + LS
=6: LTS + RLS */

if job < 4 then do;
title1= "Results of Least Median Squares Estimation";
title2= "Estimated LMS Coefficients";
title3= "LMS Residuals";
end; else do;
title1= "Results of Least Trimmed Squares Estimation";
title2= "Estimated LTS Coefficients";
title3= "LTS Residuals";
end;
print title1[label=""];

print "Quantile. . . . . . . . . . ." (sc[1]),
"Number of Subsets. . . . . . " (sc[2]),
"Number of Singular Subsets . " (sc[3]),
"Number of Nonzero Weights. . " (sc[4]),
"Objective Function. . . . . ." (sc[5]),
"Preliminary Scale Estimate. ." (sc[6]),
"Final Scale Estimate. . . . ." (sc[7]),
"Robust R Squared. . . . . . ." (sc[8]),
"Asymptotic Consistency Factor" (sc[9]);

if job = 2 | job = 5 then do;
print "LS Scale Estimate . . . . ." (sc[11]),
"Sum of Squares. . . . . . ." (sc[12]),
"R-squared . . . . . . . . ." (sc[13]),
"F Statistic . . . . . . . ." (sc[14]);
end;
if job = 3 | job = 6 then do;
print "RLS Scale Estimate. . . . ." (sc[11]),
"Weighted Sum of Squares . ." (sc[12]),
"Weighted R-squared. . . . ." (sc[13]),
"F Statistic . . . . . . . ." (sc[14]);
end;

nr2 = nrow(coef);
if nr2 > 0 then do;
print (coef[1,])[label=title2];
print (coef[2,])[label="Indices of Best Sample"];
if nr2 > 2 then do;
print (coef[3,])[label="Estimated WLS Coefficients"];
if nr2 = 8 then do;
print (coef[4,])[label="Standard Errors"];
print (coef[5,])[label="T Values"];
print (coef[6,])[label="Probabilities"];
print (coef[7,])[label="Lower Wald CI"];
print (coef[8,])[label="Upper Wald CI"];
end; end; end;

nr3 = nrow(wgt);
if nr3 > 0 then do;
print (wgt[1,])[label="Estimated Weights"];
print (wgt[2,])[label=title3];
if nr3 > 2 then do;
print (wgt[3,])[label="Diagnostics"];
if nr3 = 4 then do;
print (wgt[4,])[label="WLS Residuals"];
end; end; end;
finish prilmts;

/*------------------------------------------------------------*/

start scatlms(job,optn,x,y,xnam,ynam,titl,ipsf,filn);

/* job = 0: only draw scatter plot without regression
= 1: LS
= 2: LS + LMS
= 3: LS + LMS + WLS
= 4: LS + LTS
= 5: LS + LTS + WLS
= 6: LS + LMS + LTS
= 7: LS + LMS + LTS + WLS (WLS same LMS and LTS)
* the next 8-14 are 1-7 but with L1 regression *
= 8: LS + L1
= 9: LS + L1 + LMS
=10: LS + L1 + LMS + WLS
=11: LS + L1 + LTS
=12: LS + L1 + LTS + WLS
=13: LS + L1 + LMS + LTS
=14: LS + L1 + LMS + LTS + WLS (WLS same LMS and LTS)

ipsf: specifies GOPTIONS statement:
ipsf=0: draw interactively
ipsf=1: write file with smaller picture for document
ipsf=2: write file with larger picture for color printer
filn: name for graphic segment */

start gscenter(x,y,str);
call gstrlen(len,str);
call gscript(x-len/2,y,str);
finish gscenter;

start goptlms(ipsf,filn);
if ipsf then do;
fnam = (filn || ".ps");
fnam = rowcatc(fnam);
print fnam;
call execute("filename gsasfile'",fnam,"';");
if ipsf = 1 then do;
call execute('goptions reset=goptions dev=pslepsf
gsfmode=replace gaccess=gsasfile gsfname=GSASFILE
hsize=5.625 in vsize=4.65 in htext=3.0 pct border;');
end; else do;
call execute('goptions reset=goptions dev=pscolor
gsfmode=replace gaccess=gsasfile border erase;');
end;
end; else do;
call execute('goptions reset=goptions /*dev=xcolor*/;');
end;
finish goptlms;

noint = optn[1]; nobs = nrow(x); n = ncol(x);
if nrow(y) ^= nobs | ncol(y) ^= 1 | n ^= 1 then do;
print "Data vectors x and y not compatible";
goto leave;
end;

/* get [xmin,xmax] and [ymin,ymax] */
xmin1 = x[><]; xmax1 = x[<>];
xd1 = xmax1 - xmin1; xc1 = .1 * xd1;
xmin2 = xmin1 - xc1; xmax2 = xmax1 + xc1;
xd2 = xmax2 - xmin2; xc2 = .1 * xd2;
xme = .5*(xmin2+xmax2);

ymin1 = y[><]; ymax1 = y[<>];
yd1 = ymax1 - ymin1;

ils = 0; iwls = 0; ilms = 0; ilts = 0; il1 = 0;
if job > 0 then ils = 1;
if job = 3 | job = 5 | job = 7 then iwls = 1;
if job =10 | job =12 | job =14 then iwls = 1;
if job = 2 | job = 3 | job = 6 | job = 7 then ilms = 1;
if job = 9 | job =10 | job =13 | job =14 then ilms = 1;
if job = 4 | job = 5 | job = 6 | job = 7 then ilts = 1;
if job =11 | job =12 | job =13 | job =14 then ilts = 1;
if job > 7 & job < 15 then il1 = 1;
/* print "LS=" ils "WLS=" iwls "L1=" il1; */
/* print "LMS=" ilms "LTS=" ilts; */

/* enumerate outlier points if either LMS or LTS */
klmts = 0;
if job = 2 | job = 3 | job = 9 | job =10 then klmts = 1;
if job = 4 | job = 5 | job =11 | job =12 then klmts = 2;

nr = 0;
kls = 0; kwls = 0; klms = 0; klts = 0; kl1 = 0;
vreg = j(10,2,0); nreg = j(10,1,"        ");
if job then do;

/* A. Do LMS and LTS */
if (ilms) then do;
/*--- 1. Compute LS and LMS ---*/
optn[3]= 2;    /* ilsq: LS and LMS */
/* print "LS and LMS"; */
CALL LMS(sc,coef,wgt,optn,y,x);
/* print "End of LMS" coef; */

kls= 1; b = coef[3,1];
if noint=0 then a = coef[3,2]; else a = 0.;
nr = nr + 1; nreg[nr]= "LS";
vreg[nr,1]= xmin1 * b + a;
vreg[nr,2]= xmax1 * b + a;

klms=1; b = coef[1,1];
if noint=0 then a = coef[1,2]; else a = 0.;
nr = nr + 1; nreg[nr]= "LMS";
vreg[nr,1]= xmin1 * b + a;
vreg[nr,2]= xmax1 * b + a;

if ilts then do;
/*--- 2. Compute LTS and WLS ---*/
if iwls then optn[3]= 1;    /* ilsq: LTS and WLS */
else optn[3]= 0;
/* print "LTS and WLS"; */
CALL LTS(sc,coef,wgt,optn,y,x);
/* print "End of LTS" coef; */

klts=1; b = coef[1,1];
if noint=0 then a = coef[1,2]; else a = 0.;
nr = nr + 1; nreg[nr]= "LTS";
vreg[nr,1]= xmin1 * b + a;
vreg[nr,2]= xmax1 * b + a;

if iwls then do;
kwls=1; b = coef[3,1];
if noint=0 then a = coef[3,2]; else a = 0.;
nr = nr + 1; nreg[nr]= "WLS";
vreg[nr,1]= xmin1 * b + a;
vreg[nr,2]= xmax1 * b + a;
end;
end; else if iwls then do;
/*--- 3. Compute LMS and WLS: if no LTS is asked ---*/
optn[3]= 1;    /* ilsq: LMS and WLS */
/* print "LMS"; */
CALL LMS(sc,coef,wgt,optn,y,x);
/* print "End of LMS" coef; */

kwls=1; b = coef[3,1];
if noint=0 then a = coef[3,2]; else a = 0.;
nr = nr + 1; nreg[nr]= "WLS";
vreg[nr,1]= xmin1 * b + a;
vreg[nr,2]= xmax1 * b + a;
end;
end; else if ilts then do;

/* 4. Do LS and LTS: if no LMS is asked */
optn[3]= 2;    /* ilsq: LS and LTS */
/* print "LS and LTS"; */
CALL LTS(sc,coef,wgt,optn,y,x);
/* print "End of LTS" coef; */

kls= 1; b = coef[3,1];
if noint=0 then a = coef[3,2]; else a = 0.;
nr = nr + 1; nreg[nr]= "LS";
vreg[nr,1]= xmin1 * b + a;
vreg[nr,2]= xmax1 * b + a;

klts=1; b = coef[1,1];
if noint=0 then a = coef[1,2]; else a = 0.;
nr = nr + 1; nreg[nr]= "LTS";
vreg[nr,1]= xmin1 * b + a;
vreg[nr,2]= xmax1 * b + a;

if iwls then do;
/*--- 3. Compute LTS and WLS: if no LMS is asked ---*/
optn[3]= 1;    /* ilsq: LMS and WLS */
CALL LTS(sc,coef,wgt,optn,y,x);

kwls=1; b = coef[3,1];
if noint=0 then a = coef[3,2]; else a = 0.;
nr = nr + 1; nreg[nr]= "WLS";
vreg[nr,1]= xmin1 * b + a;
vreg[nr,2]= xmax1 * b + a;
end;
end;

/* B. Do L1 and leftover LS */
if il1 then do;
l1opt = j(2,1,0); l1opt[1]=1000;
if noint=0 then xx = x || j(nobs,1,1.);
else xx = x;
CALL LAV(rc,coef,xx,y,,l1opt);

kl1=1; b = coef[1,1];
if noint=0 then a = coef[1,2]; else a = 0.;
nr = nr + 1; nreg[nr]= "L1";
vreg[nr,1]= xmin1 * b + a;
vreg[nr,2]= xmax1 * b + a;
end;
if ils && kls=0 then do;
if noint=0 then do;
xx = x || j(nobs,1,1.);
sxx = xx` * xx;
sxy = xx` * y;
xls = inv(sxx) * sxy;
kls=1; b = xls[1]; a = xls[2];
end; else do;
sxx = x` * x; sxy = x` * y;
kls=1; b = sxy / sxx; a = 0.;
end;
nr = nr + 1; nreg[nr]= "LS";
vreg[nr,1]= xmin1 * b + a;
vreg[nr,2]= xmax1 * b + a;
end;

/* correct ymin and ymax */
do ir=1 to nr;
a = vreg[ir,1]; b = vreg[ir,2];
if a < ymin1 then ymin1 = a;
if b < ymin1 then ymin1 = b;
if a > ymax1 then ymax1 = a;
if b > ymax1 then ymax1 = b;
end;
end;
/* print "NR=" nr; */

/* now with corrected ymin and ymax */
yd1 = ymax1 - ymin1; yc1 = .1 * yd1;
ymin2 = ymin1 - yc1; ymax2 = ymax1 + yc1;
yd2 = ymax2 - ymin2; yc2 = .1 * yd2;
yme = .5*(ymin2+ymax2);

/* No Intercept: regression through origin;
start axis with zero */
if noint^=0 & xmin1 > 0 & xmin2 < xd2
& ymin2 < yd2 then do;
xmin1 = 0.; xmin2 = 0.;
xd2 = xmax2 - xmin2; xc2 = .1 * xd2;
xme = .5*(xmin2+xmax2);
ymin1 = 0.; ymin2 = 0.;
yd2 = ymax2 - ymin2; yc2 = .1 * yd2;
yme = .5*(ymin2+ymax2);
do ir=1 to nr;
vreg[ir,1] = 0;
end;
end;

/* prepare the plot */
xbox = { 0 100 100   0};
ybox = { 0   0 100 100};
wind = (xmin2 || ymin2) // (xmax2 || ymax2);

/* create the plot */
call goptlms(ipsf,filn);
call gstart;
call gopen(filn);
call gset("font","swiss"); /* set character font */
call gpoly(xbox,ybox);     /* draw a box around plot */
call gwindow(wind);        /* define window */
call gport({15 15 , 85 85}); /* define viewport */
call gxaxis((xmin2 || ymin2),xd2,11, , ,"4.",1.);
call gyaxis((xmin2 || ymin2),yd2,11, , ,"4.",1.);

/* plot xnam, ynam variable names of axis and the title */
call gset("height",1.5);
call gstrlen(len,xnam);
call gtext(xmax2-len,ymin2-yc2,xnam);
call gvtext(xmin2-xc2,ymax2,ynam);
call gset("height",2.0);
call gscenter(xme,ymax2,titl);

/* draw regression lines */
xa = xmin1; xb = xmax1;
do ir=1 to nr;
ya = vreg[ir,1]; yb = vreg[ir,2];
call gdraw((xa || xb),(ya || yb));
call gscript(xb,yb,nreg[ir]);
end;

/* print the scatter points */
call gpoint(x,y,"dot","magenta",1.5);

/* enumerate the zero weight points */
if klmts>0 then do;
do i=1 to nobs;
if wgt[i] = 0 then do;
z = char(i,2,0);
nzwx = x[i] + .01 * xd2;
nzwy = y[i];
call gscript(nzwx,nzwy,z,,,1.);
end; end; end;

call gshow;
call gclose;
call gstop;
leave:
finish scatlms;

/*------------------------------------------------------------*/

start primve(sc,xmve,dist);
n  = ncol(xmve);
nr = nrow(xmve);
print "Quantile . . . . . . . . ." (sc[1]),
"Number of Subsets . . . . " (sc[2]),
"Number of Singular Subsets" (sc[3]),
"Number of Nonzero Weights " (sc[4]),
"Minimum Volume . . . . . ." (sc[5]),
"Distance Quantile. . . . ." (sc[6]),
"Cutoff Value . . . . . . ." (sc[7]),
"Correction Factor. . . . ." (sc[8]),
"Scaling Factor . . . . . ." (sc[9]);
print "Robust Location", (xmve[1,]);
print "Robust Scatter Matrix", (xmve[3:2+n,]);
print "Eigenvalues of Robust Scatter Matrix", (xmve[2,]);
if (nr > n+2) then
print "Robust Correlation Matrix", (xmve[3+n:2+n+n,]);
print "Mahalanobis Distances", (dist[1,]);
print "Robust Distances", (dist[2,]);
print "Estimated Weights", (dist[3,]);
finish primve;

/*------------------------------------------------------------*/

start scatmve(job,optn,prob,a,vnam,titl,ipsf,filn);

/* job=1: draw confidence ellipse for mean,
job=2: draw confidence ellipse for prediction
ipsf: specifies GOPTIONS statement:
ipsf=0: draw interactively
ipsf=1: write file with smaller picture for document
ipsf=2: write file with larger picture for color printer
filn: prefix name of postscript file */

start gscenter(x,y,str);
call gstrlen(len,str);
call gscript(x-len/2,y,str);
finish gscenter;

start goptmve(ipsf,filn,n,jx,jy);
if ipsf then do;
if n < 10 then do;
post = jy * 10 + jx;
post = char(post,2,0);
end; else do;
post = jy * 100 + jx;
post = char(post,4,0);
end;
fnam = (filn || post || ".ps");
fnam = rowcatc(fnam);
print fnam;
call execute("filename gsasfile'",fnam,"';");
if ipsf = 1 then do;
call execute('goptions reset=goptions dev=pslepsf
gsfmode=replace gaccess=gsasfile gsfname=GSASFILE
hsize=5.625 in vsize=4.65 in htext=3.0 pct border;');
end; else do;
call execute('goptions reset=goptions dev=pscolor
gsfmode=replace gaccess=gsasfile border erase;');
end;
end; else do;
call execute('goptions reset=goptions /*dev=xcolor*/;');
end;
finish goptmve;

start elli1(job,prob,nobs,xm,covm,nn,corn,elp);
/* This is my version */
/* job=1: draw confidence ellipse for mean,
job=2: draw confidence ellipse for prediction */
tf = 2. * (nobs-1) / (nobs * (nobs - 2));
ff = tf * FINV(prob,2,nobs-2);
if job = 2 then ff = ff * (nobs + 1.);
ff = sqrt(ff);

/* Eigenvectors are stored in columns */
/* get orthogonal rotation matrix */
/* since length of eigenvectors is 1:
rot[1,1]= rot[2,2]= cos(alfa),
rot[2,1]= sin(alfa) = -rot[1,2] */
CALL EIGEN(eval,evec,covm);
if covm[1,1] > covm[2,2] then do;
t = eval[1];   eval[1] = eval[2];     eval[2] = t;
t = evec[1,1]; evec[1,1] = evec[1,2]; evec[1,2] = t;
t = evec[2,1]; evec[2,1] = evec[2,2]; evec[2,2] = t;
end;
if eval[1] <= 0 then da = 0.;
else da = sqrt(eval[1]);
if eval[2] <= 0 then db = 0.;
else db = sqrt(eval[2]);
rot = j(2,2,0.);
rot[1,1] =  da * ff * evec[1,1];
rot[2,1] =  da * ff * evec[1,2];
rot[1,2] = -db * ff * evec[1,2];
rot[2,2] =  db * ff * evec[1,1];

/* Get axes of ellipsoid: a axis: 1 -> 2
b axis: 3 -> 4 */
corn = j(4,2,0.);
corn[1,1] = -1.; corn[1,2] =  0.;
corn[2,1] =  1.; corn[2,2] =  0.;
corn[3,1] =  0.; corn[3,2] = -1.;
corn[4,1] =  0.; corn[4,2] =  1.;
corn = corn * rot` + j(4,1,1) * xm;

/* Points of ellipsoid: elp[nn+1,2] */
pi = 3.141592653589; pi2 = 2. * pi;
elp = j(nn+1,2,0.);
phi = 0.; dp = pi2 / nn;
do j = 1 to nn+1;
x = cos(phi); y = sin(phi);
elp[j,] = (x || y) * rot` + xm;
phi = phi + dp;
end;
finish elli1;

start elli2(job,prob,nobs,xm,covm,nn,corn,elp);

/* This is the INSIGHT version */
/* job=1: draw confidence ellipse for mean,
job=2: draw confidence ellipse for prediction */
tf = 2. * (nobs-1) / (nobs * (nobs - 2));
ff = tf * FINV(prob,2,nobs-2);
if job = 2 then ff = ff * (nobs + 1.);
ff = sqrt(ff);

/* Eigenvectors are stored in columns */
CALL EIGEN(eval,evec,covm);
if eval[1] <= 0 then da = 0.;
else da = sqrt(eval[1]);
if eval[2] <= 0 then db = 0.;
else db = sqrt(eval[2]);

v0 = da * evec[1,1] * evec[1,1]
+ db * evec[1,2] * evec[1,2];
v1 = da * evec[1,1] * evec[2,1]
+ db * evec[1,2] * evec[2,2];
v2 = da * evec[2,1] * evec[2,1]
+ db * evec[2,2] * evec[2,2];
elp = j(nn+1,2,0.);
phi = 0.; dp = 1. / nn; s2 = 4. * sqrt(2.);
do j = 1 to nn+1;
if phi <= .25 then do;
v = phi - .125; xv = s2 * v;
yv = -sqrt(1. - xv * xv);
end; else
if phi <= .5 then do;
v = phi - .375; yv = s2 * v;
xv = sqrt(1. - yv * yv);
end; else
if phi <= .75 then do;
v = .625 - phi; xv = s2 * v;
yv = sqrt(1. - xv * xv);
end; else do;
v = .875 - phi; yv = s2 * v;
xv = -sqrt(1. - yv * yv);
end;
elp[j,1] = ff * (v0 * xv + v1 * yv) + xm[1];
elp[j,2] = ff * (v1 * xv + v2 * yv) + xm[2];
phi = phi + dp;
end;

/* Get axes of ellipsoid: a axis: 1 -> 2
b axis: 3 -> 4 */
if covm[1,1] > covm[2,2] then do;
t = da; da = db; db = t;
t = evec[1,1]; evec[1,1] = evec[1,2]; evec[1,2] = t;
t = evec[2,1]; evec[2,1] = evec[2,2]; evec[2,2] = t;
end;
rot = j(2,2,0.);
rot[1,1] =  da * ff * evec[1,1];
rot[2,1] =  da * ff * evec[1,2];
rot[1,2] = -db * ff * evec[1,2];
rot[2,2] =  db * ff * evec[1,1];
corn = j(4,2,0.);
corn[1,1] = -1.; corn[1,2] =  0.;
corn[2,1] =  1.; corn[2,2] =  0.;
corn[3,1] =  0.; corn[3,2] = -1.;
corn[4,1] =  0.; corn[4,2] =  1.;
corn = corn * rot` + j(4,1,1) * xm;
finish elli2;

/* Initialization */
n = ncol(a); nobs = nrow(a);
nn = 120; /* number of ellipsoid points */

/* Compute classical scatter matrix */
muc = a[+,] / nobs;
covc = (a` * a - muc` * muc * nobs) / (nobs - 1.);

/* Compute robust scatter matrix */
CALL MVE(sc,xmve,dist,optn,a);
mur = xmve[1,]; covr = xmve[3:2+n,];
wgts = dist[3,];

/*--- do plots for all pairs of variables ---*/
ig = 0;
do jy = 2 to n;
do jx = 1 to jy-1;
ig = ig + 1;
call goptmve(ipsf,filn,n,jx,jy);

x = a[,jx]; y = a[,jy];
mu1  = (muc[jx] || muc[jy]);
cov1 = ( (covc[jx,jx] || covc[jx,jy]) //
(covc[jy,jx] || covc[jy,jy]) );
/* Compute points of classic Ellipsoid */
CALL ELLI1(job,prob,nobs,mu1,cov1,nn,corn1,elp1);

mu2  = (mur[jx] || mur[jy]);
cov2 = ( (covr[jx,jx] || covr[jx,jy]) //
(covr[jy,jx] || covr[jy,jy]) );
/* Compute points of robust Ellipsoid */
CALL ELLI1(job,prob,nobs,mu2,cov2,nn,corn2,elp2);

/* Get dimension of axes */
/* get [xmin,xmax] and [ymin,ymax] */
xmin1 = x[><]; xmax1 = x[<>];
ymin1 = y[><]; ymax1 = y[<>];
xmin2 = elp1[><,1]; xmax2 = elp1[<>,1];
ymin2 = elp1[><,2]; ymax2 = elp1[<>,2];
if xmin2 < xmin1 then xmin1 = xmin2;
if xmax2 > xmax1 then xmax1 = xmax2;
if ymin2 < ymin1 then ymin1 = ymin2;
if ymax2 > ymax1 then ymax1 = ymax2;

xmin2 = elp2[><,1]; xmax2 = elp2[<>,1];
ymin2 = elp2[><,2]; ymax2 = elp2[<>,2];
if xmin2 < xmin1 then xmin1 = xmin2;
if xmax2 > xmax1 then xmax1 = xmax2;
if ymin2 < ymin1 then ymin1 = ymin2;
if ymax2 > ymax1 then ymax1 = ymax2;

xd1 = xmax1 - xmin1; xc1 = .1 * xd1;
xmin2 = xmin1 - xc1; xmax2 = xmax1 + xc1;
xd2 = xmax2 - xmin2; xc2 = .1 * xd2;
xme = .5*(xmin2+xmax2);

yd1 = ymax1 - ymin1; yc1 = .1 * yd1;
ymin2 = ymin1 - yc1; ymax2 = ymax1 + yc1;
yd2 = ymax2 - ymin2; yc2 = .1 * yd2;
yme = .5*(ymin2+ymax2);

/* prepare the plot */
xbox = { 0 100 100   0};
ybox = { 0   0 100 100};
wind = (xmin2 || ymin2) // (xmax2 || ymax2);

/* create the plot */
call gstart;
call gopen("mve");
call gset("font","swiss"); /* set character font */
call gpoly(xbox,ybox);     /* draw a box around plot */
call gwindow(wind);        /* define window */
call gport({15 15 , 85 85}); /* define viewport */
call gxaxis((xmin2 || ymin2),xd2,11, , ,"4.1",1);
call gyaxis((xmin2 || ymin2),yd2,11, , ,"4.1",1);

/* plot xnam, ynam variable names of axis and the title */
call gset("height",1.5);
call gstrlen(len,vnam[jx]);
call gtext(xmax2-len,ymin2-yc2,vnam[jx]);
call gvtext(xmin2-xc2,ymax2,vnam[jy]);
call gset("height",2.0);
call gscenter(xme,ymax2,titl);

/* draw location point */
call gpoint(mu1[1],mu1[2],"circle","red",2);
/* draw points of ellipsoid */
call gpoint(elp1[,1],elp1[,2],"diamond","red",0.5);
/* plot the two ellipsoid axes */
/*
call gpoint(corn1[1,1],corn1[1,2],"diamond","red",1.2);
call gpoint(corn1[2,1],corn1[2,2],"diamond","red",1.2);
call gpoint(corn1[3,1],corn1[3,2],"diamond","red",1.2);
call gpoint(corn1[4,1],corn1[4,2],"diamond","red",1.2);
*/
call gdrawl(corn1[1,1:2],corn1[2,1:2], ,"red");
call gdrawl(corn1[3,1:2],corn1[4,1:2], ,"red");

/* draw location point */
call gpoint(mu2[1],mu2[2],"circle","green",2);
/* draw points of ellipsoid */
/* call gpoint(elp2[,1],elp2[,2],"dot","green",0.5); */
call gdraw(elp2[,1],elp2[,2], ,"green");

/* plot the two ellipsoid axes */
/*
call gpoint(corn2[1,1],corn2[1,2],"dot","green",1.2);
call gpoint(corn2[2,1],corn2[2,2],"dot","green",1.2);
call gpoint(corn2[3,1],corn2[3,2],"dot","green",1.2);
call gpoint(corn2[4,1],corn2[4,2],"dot","green",1.2);
*/
call gdrawl(corn2[1,1:2],corn2[2,1:2], ,"green");
call gdrawl(corn2[3,1:2],corn2[4,1:2], ,"green");

/* print the scatter points */
/* enumerate the zero weight points */
do i=1 to nobs;
xi = x[i]; yi = y[i];
if wgts[i] = 0 then do;
z = char(i,2,0);
call gpoint(xi,yi,"dot","red",1.5);
xi = xi + .01 * xd2;
call gscript(xi,yi,z,,,1.);
end;
else do;
call gpoint(xi,yi,"dot","green",1.5);
end; end;

call gshow;
call gclose;
call gstop;
end; end;
leave:
finish scatmve;

/*------------------------------------------------------------*/

start lmsdiap(job,optlms,optmve,prob,x,y,titl,ipsf,filn);
/* job = 1: plot LMS residuals vs. robust distances
= 2: plot LTS residuals vs. robust distances
= 15: do plot job=1 and job=5
= 25: do plot job=2 and job=5
ipsf: specifies GOPTIONS statement:
ipsf=0: draw interactively
ipsf=1: write file with smaller picture for document
ipsf=2: write file with larger picture for color printer
filn: prefix name of postscript file */

start gscenter(x,y,str);
call gstrlen(len,str);
call gscript(x-len/2,y,str);
finish gscenter;

start goptrd(job,ipsf,filn);
if ipsf then do;
if job=1 then post="lms";
if job=2 then post="lts";
if job=3 then post="ls";
fnam = (filn || post || ".ps");
fnam = rowcatc(fnam);
print fnam;
call execute("filename gsasfile'",fnam,"';");
if ipsf = 1 then do;
call execute('goptions reset=goptions dev=pslepsf
gsfmode=replace gaccess=gsasfile gsfname=GSASFILE
hsize=5.625 in vsize=4.65 in htext=3.0 pct border;');
end; else do;
call execute('goptions reset=goptions dev=pscolor
gsfmode=replace gaccess=gsasfile border erase;');
end;
end; else do;
call execute('goptions reset=goptions /*dev=xcolor*/;');
end;
finish goptrd;

start doplot(job,n,nobs,xdis,yres,wgts,titl);
if job=1 | job=2 then xstr= "Robust Distance RD";
if job=3 then xstr= "Classical Distance MD";
if job=1 then ystr= "LMS Std. Res.";
if job=2 then ystr= "LTS Std. Res.";
if job=3 then ystr= "LS Std. Res.";

chis = sqrt(cinv(.975,n)); /* print chis; */
xmin1 = xdis[><]; xmax1 = xdis[<>];
ymin1 = yres[><]; ymax1 = yres[<>];
if xmax1 < 1.1*chis then xmax1 = 1.1*chis;
if ymax1 <  3. then ymax1 =  3.;
if ymin1 > -3. then ymin1 = -3.;

xd1 = xmax1 - xmin1; xc1 = .1 * xd1;
xmin2 = xmin1 - xc1; xmax2 = xmax1 + xc1;
xd2 = xmax2 - xmin2; xc2 = .1 * xd2;
xme = .5*(xmin2+xmax2);

yd1 = ymax1 - ymin1; yc1 = .1 * yd1;
ymin2 = ymin1 - yc1; ymax2 = ymax1 + yc1;
yd2 = ymax2 - ymin2; yc2 = .1 * yd2;
yme = .5*(ymin2+ymax2);

/* prepare the plot */
xbox = { 0 100 100   0};
ybox = { 0   0 100 100};
wind = (xmin2 || ymin2) // (xmax2 || ymax2);

/* create the plot */
call gstart;
call gopen("diag");
call gset("font","swiss"); /* set character font */
call gpoly(xbox,ybox);     /* draw a box around plot */
call gwindow(wind);        /* define window */
call gport({15 15 , 85 85}); /* define viewport */
call gxaxis((xmin2 || ymin2),xd2,11, , ,"4.1",1);
call gyaxis((xmin2 || ymin2),yd2,11, , ,"4.1",1);

/* plot names of axis and the title */
call gset("height",1.5);
call gstrlen(xlen,xstr);
call gtext(xmax2-xlen,ymin2-yc2,xstr);
call gvtext(xmin2-xc2,ymax2,ystr);
call gset("height",2.0);
call gscenter(xme,ymax2,titl);

/* print the scatter points */
call gpoint(xdis,yres,"dot","magenta",1.5);

/* enumerate the zero weight points */
do i=1 to nobs;
if wgts[i] = 0 then do;
z = char(i,2,0);
xi = xdis[i]; yi = yres[i];
call gpoint(xi,yi,"dot","blue",1.5);
xi = xi + .01 * xd2;
call gscript(xi,yi,z,,,1.);
end; end;

xya = j(3,2,0.); xyb = j(3,2,0.);
xya[1,1]= xmin2; xya[1,2]= 2.5;
xyb[1,1]= xmax2; xyb[1,2]= 2.5;
xya[2,1]= xmin2; xya[2,2]=-2.5;
xyb[2,1]= xmax2; xyb[2,2]=-2.5;
xya[3,1]= chis;  xya[3,2]=ymin2;
xyb[3,1]= chis;  xyb[3,2]=ymax2;
call gdrawl(xya,xyb, ,"black");

call gshow;
call gclose;
call gstop;
finish doplot;

nobs = nrow(x); n = ncol(x);
if nrow(y) ^= nobs then do;
print "Data vectors x and y not compatible";
goto leave;
end;

/* Compute LMS or LTS: residuals are in (wgt[2,]); */
/* Compute LS: residuals are in (wgt[4,]); */
if job=1 | job=15 then do;
iob = 1;
optlms[3]= 2;    /* ilsq: LS and LMS */
CALL LMS(sc,coef,wgt,optlms,y,x);
end;
if job=2 | job=25 then do;
iob = 2;
optlms[3]= 2;    /* ilsq: LS and LTS */
CALL LTS(sc,coef,wgt,optlms,y,x);
end;

/* Compute robust distances: dist[2,] */
/* Mahalanobis Distances: dist[1,] */
CALL MVE(sc,xmve,dist,optmve,x);

/* Plot LMS_Res vs Rob_Dist */
xdis = dist[2,]; yres = wgt[2,]; wgts = dist[3,];

call goptrd(iob,ipsf,filn);
call doplot(iob,n,nobs,xdis,yres,wgts,titl);
if job=15 | job=25 then do;
/* Plot LS_Res vs Mah_Dist */
xdis = dist[1,]; yres = wgt[4,]; wgts = dist[3,];
call goptrd(3,ipsf,filn);
call doplot(3,n,nobs,xdis,yres,wgts,titl);
end;
leave:
finish lmsdiap;

/*------------------------------------------------------------*/

start scatmcd(job,optn,prob,a,vnam,titl,ipsf,filn);

/* job=1: draw confidence ellipse for mean,
job=2: draw confidence ellipse for prediction
ipsf: specifies GOPTIONS statement:
ipsf=0: draw interactively
ipsf=1: write file with smaller picture for document
ipsf=2: write file with larger picture for color printer
filn: prefix name of postscript file */

start gscenter(x,y,str);
call gstrlen(len,str);
call gscript(x-len/2,y,str);
finish gscenter;

start goptmcd(ipsf,filn,n,jx,jy);
if ipsf then do;
if n < 10 then do;
post = jy * 10 + jx;
post = char(post,2,0);
end; else do;
post = jy * 100 + jx;
post = char(post,4,0);
end;
fnam = (filn || post || ".ps");
fnam = rowcatc(fnam);
print fnam;
call execute("filename gsasfile'",fnam,"';");
if ipsf = 1 then do;
call execute('goptions reset=goptions dev=pslepsf
gsfmode=replace gaccess=gsasfile gsfname=GSASFILE
hsize=5.625 in vsize=4.65 in htext=3.0 pct border;');
end; else do;
call execute('goptions reset=goptions dev=pscolor
gsfmode=replace gaccess=gsasfile border erase;');
end;
end; else do;
call execute('goptions reset=goptions /*dev=xcolor*/;');
end;
finish goptmcd;

start elli1(job,prob,nobs,xm,covm,nn,corn,elp);
/* This is my version */
/* job=1: draw confidence ellipse for mean,
job=2: draw confidence ellipse for prediction */
tf = 2. * (nobs-1) / (nobs * (nobs - 2));
ff = tf * FINV(prob,2,nobs-2);
if job = 2 then ff = ff * (nobs + 1.);
ff = sqrt(ff);

/* Eigenvectors are stored in columns */
/* get orthogonal rotation matrix */
/* since length of eigenvectors is 1:
rot[1,1]= rot[2,2]= cos(alfa),
rot[2,1]= sin(alfa) = -rot[1,2] */
CALL EIGEN(eval,evec,covm);
if covm[1,1] > covm[2,2] then do;
t = eval[1];   eval[1] = eval[2];     eval[2] = t;
t = evec[1,1]; evec[1,1] = evec[1,2]; evec[1,2] = t;
t = evec[2,1]; evec[2,1] = evec[2,2]; evec[2,2] = t;
end;
if eval[1] <= 0 then da = 0.;
else da = sqrt(eval[1]);
if eval[2] <= 0 then db = 0.;
else db = sqrt(eval[2]);
rot = j(2,2,0.);
rot[1,1] =  da * ff * evec[1,1];
rot[2,1] =  da * ff * evec[1,2];
rot[1,2] = -db * ff * evec[1,2];
rot[2,2] =  db * ff * evec[1,1];

/* Get axes of ellipsoid: a axis: 1 -> 2
b axis: 3 -> 4 */
corn = j(4,2,0.);
corn[1,1] = -1.; corn[1,2] =  0.;
corn[2,1] =  1.; corn[2,2] =  0.;
corn[3,1] =  0.; corn[3,2] = -1.;
corn[4,1] =  0.; corn[4,2] =  1.;
corn = corn * rot` + j(4,1,1) * xm;

/* Points of ellipsoid: elp[nn+1,2] */
pi = 3.141592653589; pi2 = 2. * pi;
elp = j(nn+1,2,0.);
phi = 0.; dp = pi2 / nn;
do j = 1 to nn+1;
x = cos(phi); y = sin(phi);
elp[j,] = (x || y) * rot` + xm;
phi = phi + dp;
end;
finish elli1;

start elli2(job,prob,nobs,xm,covm,nn,corn,elp);

/* This is the INSIGHT version */
/* job=1: draw confidence ellipse for mean,
job=2: draw confidence ellipse for prediction */
tf = 2. * (nobs-1) / (nobs * (nobs - 2));
ff = tf * FINV(prob,2,nobs-2);
if job = 2 then ff = ff * (nobs + 1.);
ff = sqrt(ff);

/* Eigenvectors are stored in columns */
CALL EIGEN(eval,evec,covm);
if eval[1] <= 0 then da = 0.;
else da = sqrt(eval[1]);
if eval[2] <= 0 then db = 0.;
else db = sqrt(eval[2]);

v0 = da * evec[1,1] * evec[1,1]
+ db * evec[1,2] * evec[1,2];
v1 = da * evec[1,1] * evec[2,1]
+ db * evec[1,2] * evec[2,2];
v2 = da * evec[2,1] * evec[2,1]
+ db * evec[2,2] * evec[2,2];
elp = j(nn+1,2,0.);
phi = 0.; dp = 1. / nn; s2 = 4. * sqrt(2.);
do j = 1 to nn+1;
if phi <= .25 then do;
v = phi - .125; xv = s2 * v;
yv = -sqrt(1. - xv * xv);
end; else
if phi <= .5 then do;
v = phi - .375; yv = s2 * v;
xv = sqrt(1. - yv * yv);
end; else
if phi <= .75 then do;
v = .625 - phi; xv = s2 * v;
yv = sqrt(1. - xv * xv);
end; else do;
v = .875 - phi; yv = s2 * v;
xv = -sqrt(1. - yv * yv);
end;
elp[j,1] = ff * (v0 * xv + v1 * yv) + xm[1];
elp[j,2] = ff * (v1 * xv + v2 * yv) + xm[2];
phi = phi + dp;
end;

/* Get axes of ellipsoid: a axis: 1 -> 2
b axis: 3 -> 4 */
if covm[1,1] > covm[2,2] then do;
t = da; da = db; db = t;
t = evec[1,1]; evec[1,1] = evec[1,2]; evec[1,2] = t;
t = evec[2,1]; evec[2,1] = evec[2,2]; evec[2,2] = t;
end;
rot = j(2,2,0.);
rot[1,1] =  da * ff * evec[1,1];
rot[2,1] =  da * ff * evec[1,2];
rot[1,2] = -db * ff * evec[1,2];
rot[2,2] =  db * ff * evec[1,1];
corn = j(4,2,0.);
corn[1,1] = -1.; corn[1,2] =  0.;
corn[2,1] =  1.; corn[2,2] =  0.;
corn[3,1] =  0.; corn[3,2] = -1.;
corn[4,1] =  0.; corn[4,2] =  1.;
corn = corn * rot` + j(4,1,1) * xm;
finish elli2;

/* Initialization */
n = ncol(a); nobs = nrow(a);
nn = 120; /* number of ellipsoid points */

/* Compute classical scatter matrix */
muc = a[+,] / nobs;
covc = (a` * a - muc` * muc * nobs) / (nobs - 1.);

/* Compute robust scatter matrix */
CALL MCD(sc,xmcd,dist,optn,a);
mur = xmcd[1,]; covr = xmcd[3:2+n,];
wgts = dist[3,];

/*--- do plots for all pairs of variables ---*/
ig = 0;
do jy = 2 to n;
do jx = 1 to jy-1;
ig = ig + 1;
call goptmcd(ipsf,filn,n,jx,jy);

x = a[,jx]; y = a[,jy];
mu1  = (muc[jx] || muc[jy]);
cov1 = ( (covc[jx,jx] || covc[jx,jy]) //
(covc[jy,jx] || covc[jy,jy]) );
/* Compute points of classic Ellipsoid */
CALL ELLI1(job,prob,nobs,mu1,cov1,nn,corn1,elp1);

mu2  = (mur[jx] || mur[jy]);
cov2 = ( (covr[jx,jx] || covr[jx,jy]) //
(covr[jy,jx] || covr[jy,jy]) );
/* Compute points of robust Ellipsoid */
CALL ELLI1(job,prob,nobs,mu2,cov2,nn,corn2,elp2);

/* Get dimension of axes */
/* get [xmin,xmax] and [ymin,ymax] */
xmin1 = x[><]; xmax1 = x[<>];
ymin1 = y[><]; ymax1 = y[<>];
xmin2 = elp1[><,1]; xmax2 = elp1[<>,1];
ymin2 = elp1[><,2]; ymax2 = elp1[<>,2];
if xmin2 < xmin1 then xmin1 = xmin2;
if xmax2 > xmax1 then xmax1 = xmax2;
if ymin2 < ymin1 then ymin1 = ymin2;
if ymax2 > ymax1 then ymax1 = ymax2;

xmin2 = elp2[><,1]; xmax2 = elp2[<>,1];
ymin2 = elp2[><,2]; ymax2 = elp2[<>,2];
if xmin2 < xmin1 then xmin1 = xmin2;
if xmax2 > xmax1 then xmax1 = xmax2;
if ymin2 < ymin1 then ymin1 = ymin2;
if ymax2 > ymax1 then ymax1 = ymax2;

xd1 = xmax1 - xmin1; xc1 = .1 * xd1;
xmin2 = xmin1 - xc1; xmax2 = xmax1 + xc1;
xd2 = xmax2 - xmin2; xc2 = .1 * xd2;
xme = .5*(xmin2+xmax2);

yd1 = ymax1 - ymin1; yc1 = .1 * yd1;
ymin2 = ymin1 - yc1; ymax2 = ymax1 + yc1;
yd2 = ymax2 - ymin2; yc2 = .1 * yd2;
yme = .5*(ymin2+ymax2);

/* prepare the plot */
xbox = { 0 100 100   0};
ybox = { 0   0 100 100};
wind = (xmin2 || ymin2) // (xmax2 || ymax2);

/* create the plot */
call gstart;
call gopen("mcd");
call gset("font","swiss"); /* set character font */
call gpoly(xbox,ybox);     /* draw a box around plot */
call gwindow(wind);        /* define window */
call gport({15 15 , 85 85}); /* define viewport */
call gxaxis((xmin2 || ymin2),xd2,11, , ,"4.1",1);
call gyaxis((xmin2 || ymin2),yd2,11, , ,"4.1",1);

/* plot xnam, ynam variable names of axis and the title */
call gset("height",1.5);
call gstrlen(len,vnam[jx]);
call gtext(xmax2-len,ymin2-yc2,vnam[jx]);
call gvtext(xmin2-xc2,ymax2,vnam[jy]);
call gset("height",2.0);
call gscenter(xme,ymax2,titl);

/* draw location point */
call gpoint(mu1[1],mu1[2],"circle","red",2);
/* draw points of ellipsoid */
call gpoint(elp1[,1],elp1[,2],"diamond","red",0.5);
/* plot the two ellipsoid axes */
/*
call gpoint(corn1[1,1],corn1[1,2],"diamond","red",1.2);
call gpoint(corn1[2,1],corn1[2,2],"diamond","red",1.2);
call gpoint(corn1[3,1],corn1[3,2],"diamond","red",1.2);
call gpoint(corn1[4,1],corn1[4,2],"diamond","red",1.2);
*/
call gdrawl(corn1[1,1:2],corn1[2,1:2], ,"red");
call gdrawl(corn1[3,1:2],corn1[4,1:2], ,"red");

/* draw location point */
call gpoint(mu2[1],mu2[2],"circle","green",2);
/* draw points of ellipsoid */
/* call gpoint(elp2[,1],elp2[,2],"dot","green",0.5); */
call gdraw(elp2[,1],elp2[,2], ,"green");

/* plot the two ellipsoid axes */
/*
call gpoint(corn2[1,1],corn2[1,2],"dot","green",1.2);
call gpoint(corn2[2,1],corn2[2,2],"dot","green",1.2);
call gpoint(corn2[3,1],corn2[3,2],"dot","green",1.2);
call gpoint(corn2[4,1],corn2[4,2],"dot","green",1.2);
*/
call gdrawl(corn2[1,1:2],corn2[2,1:2], ,"green");
call gdrawl(corn2[3,1:2],corn2[4,1:2], ,"green");

/* print the scatter points */
/* enumerate the zero weight points */
do i=1 to nobs;
xi = x[i]; yi = y[i];
if wgts[i] = 0 then do;
z = char(i,2,0);
call gpoint(xi,yi,"dot","red",1.5);
xi = xi + .01 * xd2;
call gscript(xi,yi,z,,,1.);
end;
else do;
call gpoint(xi,yi,"dot","green",1.5);
end; end;

call gshow;
call gclose;
call gstop;
end; end;
leave:
finish scatmcd;

```