/*---------------------------------------------------------------------- The NLMeans macro provides multiple comparisons (pairwise, sequential, or against a control) on the mean scale among the levels of an effect in a generalized linear model. More complex contrasts of means and ratios of pairs of means can also be estimated and tested. In addition to Base SASŪ, SAS/STATŪ and the NLEST macro (Version 2.1 or later) are required to run this macro. Both the NLMeans macro and the NLEST macro that it calls must be defined in the current SAS session before using the NLMeans macro. For full documentation of the NLMeans macro, including descriptions of all available options and examples, see http://support.sas.com/kb/62362. ------------------------------------------------------------------------ DISCLAIMER: THIS INFORMATION IS PROVIDED BY SAS INSTITUTE INC. AS A SERVICE TO ITS USERS. IT IS PROVIDED "AS IS". THERE ARE NO WARRANTIES, EXPRESSED OR IMPLIED, AS TO MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE REGARDING THE ACCURACY OF THE MATERIALS OR CODE CONTAINED HEREIN. ----------------------------------------------------------------------*/ %macro NLMeans(version, instore=, inest=, incovb=, coef=, where=, link=, diff=, contrasts=, df=, alpha=0.05, title=, null=0, covdrop=, f=, fset=, flabel=, fdata=, nlevels=0, options=NOJOINT NONAMES NOREVERSE NORATIO DIFINFNS NOAPPEND PRINT) / minoperator mindelimiter=' '; %local notesopt; %let notesopt = %sysfunc(getoption(notes)); %let timenlm = %sysfunc(datetime()); %let _version=3.0; %if &version ne %then %do; %put NOTE: &sysmacroname macro Version &_version..; %put NOTE- Requires NLEST macro Version 2.1 or later.; %put NOTE- Documentation and downloads at http://support.sas.com/kb/62362.; %end; %let newchk=1; %if %index(%upcase(&version),DEBUG) %then %do; options notes mprint %if %index(%upcase(&version),DEBUG2) %then mlogic symbolgen; ; ods select all; %put _user_; %let dbgopt=debug,; %if %index(%upcase(&version),DEBUG2) %then %let dbgopt=debug2,; %end; %else %do; options nonotes nomprint nomlogic nosymbolgen; ods exclude all; %let dbgopt=; %if &version ne %then %let dbgopt=v,; %if %index(%upcase(&version),NOCHK) %then %do; %let newchk=0; %let dbgopt=NOCHK,; %end; %end; /* Check for newer version */ %if &newchk %then %do; %let _notfound=0; %let _newver=0; filename _ver url 'http://ftp.sas.com/techsup/download/stat/versions.dat' termstr=crlf; data _null_; infile _ver end=_eof; input name:$15. ver; if upcase(name)="&sysmacroname" then do; call symput("_newver",ver); stop; end; if _eof then call symput("_notfound",1); run; options notes; %if &syserr ne 0 or &_notfound=1 or &_newver=0 %then %put NOTE: Unable to check for newer version of the &sysmacroname macro.; %else %if %sysevalf(&_newver > &_version) %then %do; %put NOTE: A newer version of the &sysmacroname macro is available at; %put NOTE- this location: http://support.sas.com/ ; %end; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %end; /* Check inputs */ %if (&instore= and &inest= and &incovb=) or (&instore= and (&inest= or &incovb=)) %then %do; %put ERROR: Either INSTORE= or both INEST= and INCOVB= must be specified.; %goto exit; %end; %if &coef= or %sysfunc(exist(&coef)) ne 1 %then %do; %put ERROR: Required COEF= data set not specified or not found.; %goto exit; %end; %if &contrasts ne and %sysfunc(exist(&contrasts)) ne 1 %then %do; %put ERROR: CONTRASTS= data set not found.; %goto exit; %end; %if &fdata ne and %sysfunc(exist(&fdata)) ne 1 %then %do; %put ERROR: FDATA= data set not found.; %goto exit; %end; %if %quote(&f)= and &fdata= and &contrasts= and &diff= %then %let diff=all; %if &contrasts ne and &diff ne %then %do; %let diff=; options notes; %put NOTE: When CONTRASTS= is specified, DIFF= is ignored.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %end; %if %quote(&null)= %then %let null=0; %else %do; %if (%sysfunc(verify(%sysfunc(trim(%sysfunc(left(&null)))),'0123456789.-'))>0) + (%sysfunc(findc(%sysfunc(trim(%sysfunc(left(&null)))),'-'))>1) + (%sysfunc(count(&null,-))>1) + (%sysfunc(count(&null,.))>1) %then %do; %put ERROR: The NULL= value must be a numeric value.; %goto exit; %end; %if %quote(&null)=. or %quote(&null)=%quote(-) %then %do; %put ERROR: The NULL= value must be a numeric value.; %goto exit; %end; %end; %if %quote(&df) ne %then %do; %if (%sysfunc(verify(%sysfunc(trim(%sysfunc(left(&df)))),'0123456789.'))>0) + (%sysfunc(count(&df,.))>1) %then %do; %put ERROR: The DF= value must be a non-zero, positive value.; %goto exit; %end; %if %sysevalf(&df=0 or &df=.) %then %do; %put ERROR: The DF= value must be a non-zero, positive value.; %goto exit; %end; %end; %if %sysevalf(&alpha<=0 or &alpha>=1) %then %do; %put ERROR: The ALPHA= value must be between 0 and 1.; %goto exit; %end; %if %index(%quote(&title),%str(%")) or %index(%quote(&title),%str(%')) %then %do; %put ERROR: Do not use quotes (%str(%") or %str(%')) in TITLE=.; %goto exit; %end; /* Verify COEF= and FDATA= data sets have necessary variables */ %let status=ok; %let dsid=%sysfunc(open(&coef)); %if &dsid %then %do; %if %sysfunc(varnum(&dsid,LMATRIX))=0 or %sysfunc(varnum(&dsid,ROW1))=0 or (%sysfunc(varnum(&dsid,PARAMETER))=0 and %sysfunc(varnum(&dsid,EFFECT))=0) %then %do; %put ERROR: Invalid COEF= data set. At least one variable Parameter (or; %put ERROR- Effect), LMatrix, or Row1 not found.; %let status=input_err; %end; %let rc=%sysfunc(close(&dsid)); %if &status=input_err %then %goto exit; %end; %else %do; %put ERROR: Could not open COEF= data set.; %goto exit; %end; %if &contrasts ne %then %do; %let dsid=%sysfunc(open(&contrasts)); %if &dsid %then %do; %if %sysfunc(varnum(&dsid,K1))=0 or %sysfunc(varnum(&dsid,SET))=0 %then %do; %put ERROR: Invalid CONTRASTS= data set. Numeric variables SET and; %put ERROR- K1-Kn are required, where n is the maximum number of; %put ERROR- estimates in a set.; %let status=input_err; %end; %let rc=%sysfunc(close(&dsid)); %if &status=input_err %then %goto exit; %end; %else %do; %put ERROR: Could not open CONTRASTS= data set.; %goto exit; %end; %end; %if &fdata ne %then %do; %let dsid=%sysfunc(open(&fdata)); %if &dsid %then %do; %let fdlabel=0; %if %sysfunc(varnum(&dsid,LABEL)) %then %let fdlabel=1; %if %sysfunc(varnum(&dsid,F))=0 or %sysfunc(varnum(&dsid,SET))=0 %then %do; %put ERROR: Invalid FDATA= data set. Character variable; %put ERROR- F and numeric variable SET are required.; %let status=input_err; %end; %let rc=%sysfunc(close(&dsid)); %if &status=input_err %then %goto exit; %end; %else %do; %put ERROR: Could not open FDATA= data set.; %goto exit; %end; %end; /* Process options= */ %let validopts=JOINT NOJOINT NAMES NONAMES REVERSE NOREVERSE RATIO NORATIO DIFINFNS DIFALL APPEND NOAPPEND PRINT NOPRINT; %let joint=0; %let names=0; %let rdiff=0; %let ratio=0; %let difall=0; %let append=0; %let prt=1; %let i=1; %do %while (%scan(&options,&i) ne %str() ); %let option&i=%upcase(%scan(&options,&i)); %if &&option&i=JOINT %then %let joint=1; %else %if &&option&i=NAMES %then %let names=1; %else %if &&option&i=REVERSE %then %let rdiff=1; %else %if &&option&i=RATIO %then %let ratio=1; %else %if &&option&i=DIFALL %then %let difall=1; %else %if &&option&i=APPEND %then %let append=1; %else %if &&option&i=NOPRINT %then %let prt=0; %else %do; %let chk=%eval(&&option&i in &validopts); %if not &chk %then %do; %put ERROR: Valid values of OPTIONS= are &validopts..; %goto exit; %end; %end; %let i=%eval(&i+1); %end; /* Prepare options to be set in NLEST call */ %if &df= %then %let dfopt=; %else %let dfopt=, df=&df; %if %quote(&null)= %then %let nullopt=; %else %let nullopt=, null=&null; %if &alpha= %then %let alphaopt=; %else %let alphaopt=, alpha=α %if %quote(&title)= %then %let ttlopt=; %else %let ttlopt=, title=%quote(&title); %if &names=1 %then %let namopt=; %else %let namopt=, listnames=no; %if &prt=1 %then %let prtopt=; %else %let prtopt=, print=no; %if &rdiff %then %do; %let locode=-1; %let hicode=1; %end; %else %do; %let locode=1; %let hicode=-1; %end; /* Determine inverse link function */ %let link=%quote(%upcase(&link)); %if &link= %then %do; %put ERROR: The LINK= option is required.; %goto exit; %end; %else %if &link=LOGIT or &link=CUMLOGIT or &link=CLOGIT %then %let ilink="logistic("||trim(left(exp))||")"; %else %if &link=PROBIT or &link=NORMIT or &link=CUMPROBIT or &link=CPROBIT %then %let ilink="probnorm("||trim(left(exp))||")"; %else %if &link=CLL or &link=CLOGLOG or &link=CUMCLL or &link=CCLL or &link=CCLOGLOG or &link=CUMCLOGLOG %then %let ilink="(1-exp(-exp("||trim(left(exp))||")))"; %else %if &link=LOGLOG or &link=CUMLOGLOG %then %let ilink="exp(-exp(-("||trim(left(exp))||")))"; %else %if &link=LOG or &link=GLOGIT %then %let ilink="exp("||trim(left(exp))||")"; %else %if &link=IDENTITY or &link=ID %then %do; options notes; %put NOTE: This macro is not needed when LINK=IDENTITY.; %put NOTE- Use results of LSMEANS, SLICE or ESTIMATE.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %goto exit; %end; %else %if %substr(&link,1,3)=POW or %substr(&link,1,3)=REC %then %do; %if %substr(&link,1,3)=REC %then %let link=%quote(POWER(-1)); %if %index(&link,%str(%())=0 %then %do; %put ERROR: POWER link requires numeric value in parentheses.; %goto exit; %end; %let power=%quote(%substr(&link,%index(&link,%str(%()))); %if &power=0 %then %let ilink="exp("||trim(left(exp))||")"; %else %let ilink="exp(log("||trim(left(exp))||")/&power)"; %end; %else %do; %put ERROR: Valid LINK= values are LOG, LOGIT, CUMLOGIT, PROBIT, ; %put ERROR- CUMPROBIT, CLL, CUMCLL, LOGLOG, CUMLOGLOG, GLOGIT, ; %put ERROR- or POWER(x) where x is a number.; %goto exit; %end; /* Determine if is multinomial model */ %let cumlinks=CUMLOGIT CLOGIT CUMPROBIT CPROBIT CUMCLL CCLL CCLOGLOG CUMCLOGLOG CUMLOGLOG GLOGIT; %let cumchk=%eval(&link in &cumlinks); /* Subset the coef= data set if a where= condition is specified */ data _coefsub; set &coef; %if %quote(&where) ne %then where %str(&where);; run; %if &syserr %then %do; %put ERROR: Subsetting the COEF= data set failed.; %goto exit; %end; /* Count the number of response functions, nfn, in the model */ data _null_; set _coefsub end=eof; if index(Parameter,"Intercept") or index(Effect,"Intercept") then nfn+1; else do; call symput('nfn',cats(nfn)); stop; end; run; %if &cumchk=0 %then %do; /* non multinomial link */ %if &nfn>1 %then %do; %put ERROR: Multiple response functions detected. Specify the multinomial link; %put ERROR- that was used for this multinomial model. Multinomial links are:; %put ERROR- CUMLOGIT CUMPROBIT CUMCLL CUMLOGLOG or GLOGIT.; %goto exit; %end; %else %do; %if &nfn=0 %then %do; options notes; %put NOTE: No intercepts found in COEF= data set. Cannot determine number; %put NOTE- of response functions in the model. Assuming nonmultinomial; %put NOTE- model based on specified link, &link..; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %end; %let nfn=1; %if %sysevalf(&nlevels>0) %then %do; options notes; %put NOTE: NLEVELS= is ignored for nonmultinomial models with the; %put NOTE- specified link, &link..; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %end; %end; %end; %else %do; /* multinomial link */ %if %sysevalf(&nlevels>0) %then %do; %let nlverr=1; %if %sysevalf(&nlevels>0 and &nlevels<1e8) %then %do; %if %sysevalf(%sysfunc(mod(&nlevels,1))=0) %then %let nlverr=0; %end; %if &nlverr %then %do; %put ERROR: NLEVELS= value should be a positive integer.; %goto exit; %end; %else %do; %if &nfn=0 %then %let nfn=%eval(&nlevels-1); %else %if &nlevels-1 ne &nfn %then %do; options notes; %put NOTE: NLEVELS= value, &nlevels, differs from number of response; %put NOTE- levels, %eval(&nfn+1), found in COEF=. %eval(&nfn+1) will be used.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %end; %end; %end; %else %if &nfn=0 %then %do; %put ERROR: No intercepts found in COEF= data set. Cannot determine number; %put ERROR- of response functions in the multinomial model. Specify the; %put ERROR- number of response levels in NLEVELS=.; %goto exit; %end; %end; %if &nfn=1 and &cumchk %then %do; %put ERROR: Only one response function detected. Specify the nonmultinomial; %put ERROR- link that was used for this model. Nonmultinomial links are:; %put ERROR- LOG LOGIT PROBIT LOGLOG CLOGLOG or POWER(x) where x is a number.; %goto exit; %end; /* Get number of sets of estimates */ proc freq data=_coefsub nlevels; table LMatrix / out=_setnums noprint; ods output nlevels=_nlvl; run; data _null_; set _nlvl; call symput('nsets',cats(nlevels)); run; data _null_; set _setnums; call symput(cats('set',_n_),cats(lmatrix)); run; data _coef; set _coefsub; run; /* Verify fset= */ %if &nsets=1 %then %let joint=0; %if %quote(&f) ne %then %do; %if &nsets=1 or (&nsets>1 and &joint) %then %let fset=1; %if &nsets>1 and &joint=0 %then %do; %if %quote(&fset)= %then %do; %put ERROR: FSET= is required when F= is specified.; %goto exit; %end; %else %do; %let fseterr=1; %if %sysevalf(&fset>0 and &fset<=&nsets) %then %do; %if %sysevalf(%sysfunc(mod(&fset,1))=0) %then %let fseterr=0; %end; %if &fseterr %then %do; %put ERROR: FSET= value should be an integer from 1 to &nsets..; %goto exit; %end; %end; %end; %end; /* Handle options=joint with multiple estimate sets */ %if &joint and &nsets>1 %then %do; %if &nfn>1 %then %do; /* multinomial */ %let MNjntf=0; %let nkjnt=0; %if %quote(&f) ne or &fdata ne %then %let MNjntf=1; %if &diff ne or &contrasts ne %then %do; %put ERROR: With OPTIONS=JOINT and a multinomial model, DIFF= and; %put ERROR- CONTRASTS= are not allowed and are ignored. F= or; %put ERROR- FDATA= can be used instead.; %end; %if &MNjntf=0 %then %goto exit; %end; %else %do; /* nonmultinomial */ %let nsets=1; %let set1=1; proc transpose data=_coefsub out=_coef(drop=_name_); by LMatrix; var row:; run; proc transpose data=_coef out=_coef(drop=_name_) prefix=Row; var col:; run; data _coef; set _coef; LMatrix=1; run; %end; %end; /* Check DIFF= value(s) */ %if &contrasts= and &diff ne %then %do; %let diff=%upcase(&diff); %let ndifvals=%sysfunc(countw(&diff)); %if &ndifvals ne 1 and &ndifvals ne &nsets %then %do; %put ERROR: There are &nsets sets of estimates. Specify a single value; %put ERROR- in DIFF= to apply to all sets, or one value for each set.; %put ERROR- Valid values are ALL, SEQ, or a positive integer.; %goto exit; %end; %let i=1; %do %while (%scan(&diff,&i) ne %str() ); %let diferr=0; %let setdif&i=%scan(&diff,&i); %if &&setdif&i ne ALL and &&setdif&i ne SEQ %then %do; %let diferr=1; %if %sysevalf(&&setdif&i>0 and &&setdif&i<1e8) %then %do; %if %sysevalf(%sysfunc(mod(&&setdif&i,1))=0) %then %let diferr=0; %end; %if &diferr %then %do; %put ERROR: Invalid DIFF= value found. Valid values are ALL, SEQ or; %put ERROR- a positive integer.; %goto exit; %end; %end; %let i=%eval(&i+1); %end; %end; /******************************************************************************/ /*************** Process each set of estimates in COEF= data set **************/ %do s=1 %to &nsets; /* Get total number of estimates (nlsm) and number of estimates per response function (nperfn) */ proc transpose data=_coef(where=(LMatrix=&&set&s)) out=_ct(keep=col: where=(col1 ne .)); by lmatrix; var row:; run; %let dsid=%sysfunc(open(_ct)); %let nlsm=%sysfunc(attrn(&dsid,nobs)); %let rc=%sysfunc(close(&dsid)); %let nperfn=%sysevalf(&nlsm/&nfn); /* For GLOGIT, each estimate must yield a response level probability when the inverse link is applied and each estimate must be computed in all logits in order to compute the probabilities of all response levels. */ %if &link=GLOGIT and &nfn>1 %then %do; %let badglgcoef=0; %if %sysevalf(%sysfunc(mod(&nperfn,1)) = 0) %then %do; data %do _f=1 %to &nfn; %let first=%eval((&_f-1)*&nperfn+1); %let last=%eval(&_f*&nperfn); _glg&_f(keep=row&first - row&last) %end; ; set _coef(where=(LMatrix=&&set&s)); %do i=1 %to &nfn-1; if _n_<=&nfn and mod(_n_,&nfn)=&i then output _glg&i; %if &link=GLOGIT %then %do; if mod(_n_,&nfn)=&i then output _glg&i; %end; %else if _n_>&nfn then output _glg&i;; %end; if _n_<=&nfn and mod(_n_,&nfn)=0 then output _glg&i; %if &link=GLOGIT %then %do; if mod(_n_,&nfn)=0 then output _glg&i; %end; %else if _n_>&nfn then output _glg&i;; run; %do _f=2 %to &nfn; proc compare base=_glg1 compare=_glg&_f noprint out=_glgcomp outnoequal; var row1-row&nperfn; %let first=%eval((&_f-1)*&nperfn+1); %let last=%eval(&_f*&nperfn); with row&first-row&last; run; %let dsid=%sysfunc(open(_glgcomp)); %let nobs=%sysfunc(attrn(&dsid,nobs)); %let rc=%sysfunc(close(&dsid)); %if &nobs>0 %then %let badglgcoef=1; %end; %end; %else %let badglgcoef=1; %if &badglgcoef %then %do; %put ERROR: For GLOGIT models, each estimate defined in COEF= must be; %put ERROR- estimated in each of the &nfn logits. If the ESTIMATE; %put ERROR- statement was used, the CATEGORY=JOINT option is required.; %put ERROR: The above message is for estimate set &&set&s.. of COEF=.; %let nsets=%eval(&nsets-1); %goto endset; %end; %end; %if &nperfn=1 %then %do; %if &cumchk %then %do; %if &nsets=1 and &difall=0 %then %do; %put ERROR: For multinomial models with only one estimate per response; %put ERROR- function, analysis is only possible when OPTIONS=DIFALL is; %put ERROR- specified.; %goto exit; %end; %if &nsets>1 and &difall=0 and &joint=0 %then %do; %put ERROR: For multinomial models with only one estimate per response; %put ERROR- function and multiple estimate sets, analysis is only; %put ERROR- possible when JOINT or DIFALL is specified in OPTIONS=.; %put ERROR: The above message is for estimate set &&set&s.. of COEF=.; %goto endset; %end; %end; %end; /* Construct mean expressions for NLEST macro */ data _glognum(keep=fn1) _glogden(keep=sum); set _ct end=last; array lb (*) col:; array fn (&nfn) $32767; length exp sum sum1 $32767; do i=1 to &nfn; *initialize &nfn fns for each row in _ct; if lb(1) ne 0 then fn(i)=catx("*",lb(1),cats("B_p",i)); end; do i=2 to dim(lb); *cols; do j=1 to &nfn; *fns; if lb(i) ne 0 then fn(j)=catx("+",fn(j),catx("*",lb(i),cats("B_p",i+j-1))); end; end; do i=1 to &nfn; exp=fn(i); fn(i)=&ilink; if i=1 then sum1=fn(i); else sum1=catx("+",sum1,fn(i)); end; sum="(1+"||trim(left(sum1))||")"; output _glognum; if _n_<=&nperfn then output _glogden; %if &link=GLOGIT %then %do; if last and &nfn>1 then do; fn1="1"; do i=1 to &nperfn; output _glognum; end; end; %end; run; data _prexp; length exp $32767; %if &link=GLOGIT %then %do; set %do i=1 %to &nfn+1; _glogden %end; ; merge _glognum; exp=catx("/",fn1,sum); %end; %else %do; set _glognum; exp=fn1; %end; exp="("||cats(exp)||")"; keep exp; run; proc transpose data=_prexp prefix=mu %if &joint and &nsets>1 and &nfn>1 %then %do; %if &MNjntf %then out=_lbt&s(keep=mu:); %end; %else out=_lbt(keep=mu:); ; var exp; run; /* For multinomial, get number of means in a contrast, nk */ %if &link=GLOGIT %then %let nk=%sysevalf((&nfn+1)*&nperfn); %else %let nk=&nlsm; %if &joint and &nsets>1 and &nfn>1 %then %do; %if &MNjntf %then %let nkjnt=%eval(&nkjnt+&nk); %end; %if &joint and &nsets>1 and &nfn>1 %then %do; %if &MNjntf %then %goto endset; %end; %let label=0; %let cntnobs=0; %if &contrasts= and &diff ne %then %do; %if &ndifvals=1 %then %let setdif&s=&setdif1; %if %sysevalf(&&setdif&s>0 and &&setdif&s<1e8) %then %do; %if &&setdif&s>&nperfn %then %do; %put ERROR: The DIFF= value, &&setdif&s, for estimate set &&set&s must be an; %put ERROR- integer between 1 and the number of estimates, &nperfn..; %goto exit; %end; %end; %if &nk=1 %then %do; options notes; %put NOTE: Only one response function detected. No differencing; %put NOTE- coefficients can be produced. If there are multiple estimate; %put NOTE- sets, you might want to specify OPTIONS=JOINT.; %put NOTE: The above message is for estimate set &&set&s.. of COEF=.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %goto endset; %end; data _cntrs(keep=k:); array k (&nk) (&nk*0); do f=1 to %if &cumchk and &nfn>1 %then %do; %if &difall %then 1; %else %do; %if &link=GLOGIT %then &nfn+1; %else &nfn; %end; %end; %else &nfn; ; %if &cumchk and &nfn>1 and &difall %then %do; first=(f-1)*&nk+1; last=f*&nk; %end; %else %do; first=(f-1)*&nperfn+1; last=f*&nperfn; %end; %if &&setdif&s=ALL %then %do; do i=first to last-1; do j=i+1 to last; do h=1 to last; if h=i then k(h)=&locode; else if h=j then k(h)=&hicode; else k(h)=0; end; output; end; end; %end; %else %if &&setdif&s=SEQ %then %do; do i=first to last-1; do h=1 to last; if h=i then k(h)=&locode; else if h=i+1 then k(h)=&hicode; else k(h)=0; end; output; end; %end; %else %do; do j=first to last; do h=1 to last; if h=(first-1)+&&setdif&s then k(h)=&locode; else if h=j then k(h)=&hicode; else k(h)=0; end; if j ne (first-1)+&&setdif&s then output; end; %end; end; run; data _null_; dsid=open("_cntrs"); nobs=attrn(dsid, "nobs"); call symput ("cntnobs",cats(nobs)); rc=close(dsid); run; %if &cntnobs=0 %then %do; options notes; %put NOTE: Could not produce differencing coefficients. If a multinomial; %put NOTE- model was used you might need to specify OPTIONS=DIFALL.; %put NOTE: The above message is for estimate set &&set&s.. of COEF=.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %goto endset; %end; %end; %if &contrasts ne %then %do; %let misscoef=0; %let miss_set=0; %let expand=0; data _cntrs(drop=nk nkprev); set &contrasts(where=(set=&&set&s)); array k (*) k:; nk=dim(k)-nmiss(of k:); if _n_=1 then nkprev=nk; retain nkprev; if nk ne nkprev or nk<&nperfn or (&cumchk and &nperfn&nk then call symput("misscoef",1); else if nk=&nperfn and &cumchk then call symput("expand",1); run; %let dsid=%sysfunc(open(_cntrs)); %let cntnobs=%sysfunc(attrn(&dsid,nobs)); %if %sysfunc(varnum(&dsid,LABEL)) %then %let label=1; %let rc=%sysfunc(close(&dsid)); %if &cntnobs=0 %then %do; options notes; %put NOTE: No contrasts found in CONTRASTS= data set for estimate set; %put NOTE- &&set&s... No contrasts computed.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %end; %if &misscoef %then %do; %let numtxt=&nk; %if &cumchk %then %let numtxt=&nperfn or &nk; %let settxt=; %if &joint=0 %then %let settxt=%str(for estimate set &&set&s.. ); %put ERROR: All observations &settxt.in CONTRASTS=; %put ERROR- must have &numtxt coefficients.; %put ERROR- No contrasts computed for estimate set &&set&s.. of COEF=.; %let cntnobs=0; %end; %if &cumchk and &cntnobs %then %do; %if &expand %then %do; options notes; %put NOTE: Contrast coefficients found for only 1 response function.; %put NOTE- Repeating these coefficients for all response functions.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; data _cntrs(where=(set=&&set&s)); set &contrasts; array c (&nperfn) c1-c&nperfn; array k (&nk) (&nk*0); do _i=1 to &nk; if _i<=&nperfn then c(_i)=k(_i); else k(_i)=0; end; output; do _f=1 to &nfn %if &link ne GLOGIT %then -1; ; do _i=1 to &nk; if &nperfn*_f<_i<=&nperfn*(_f+1) then k(_i)=c(_i-&nperfn*_f); else k(_i)=0; end; output; end; run; proc sort data=_cntrs out=_cntrs(drop=c: _i _f); by _f; run; %end; %end; %if &ratio and &cntnobs %then %do; %let badcoef=0; data _cntrs; set _cntrs; badcoef=0; cnt1=0; cntm1=0; drop badcoef cnt1 cntm1; array k (*) k:; do i=1 to dim(k); if k(i) not in(-1,0,1,.) then badcoef=1; if k(i)=1 then cnt1+1; if k(i)=-1 then cntm1+1; end; if cnt1 ne 1 or cntm1 ne 1 then badcoef=1; if badcoef then do; call symput("badcoef","1"); delete; end; run; %if &badcoef %then %do; options notes; %put NOTE: OPTIONS=RATIO requires all contrasts to contain a single 1; %put NOTE- for the numerator, a single -1 for the denominator, and; %put NOTE- zeros otherwise. Contrasts with incorrect values will not be; %put NOTE- computed in estimate set &&set&s.. of COEF=.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %end; %end; %end; /* Construct fdata= data set for NLEST macro */ %let fexpnobs=0; %let fdnobs=0; %let muerr=0; %if %quote(&f) ne and &fset=&&set&s %then %do; data _fexp; length label f _f $32767; if _n_=1 then set _lbt; set=&&set&s; f="&f"; %if %quote(&flabel) ne %then %do; label="&flabel"; %end; %else label="&f";; keep label f; _f=prxchange("s/(mu1)(\D)/mu01$2/i",-1,f); _f=tranwrd(_f,"mu01",cats(mu1)); %do _i=2 %to &nk-1; _f=prxchange("s/(mu&_i)(\D)/mu0&_i$2/i",-1,_f); _f=tranwrd(_f,"mu0&_i",cats(mu&_i)); %end; _f=prxchange("s/(mu&nk)(\D)/mu0&nk$2/i",-1,_f); f=tranwrd(_f,"mu0&nk",cats(mu&nk)); if index(f,"mu") then do; call symput("muerr",cats(1)); delete; end; output; stop; run; %let dsid=%sysfunc(open(_fexp)); %let fexpnobs=%sysfunc(attrn(&dsid,nobs)); %let rc=%sysfunc(close(&dsid)); %end; %if &fdata ne %then %do; data _fdata; length label f _f $32767; if _n_=1 then set _lbt; set &fdata(where=(set=&&set&s)); %if &fdlabel=0 %then label=f;; keep label f; _f=prxchange("s/(mu1)(\D)/mu01$2/i",-1,f); _f=tranwrd(_f,"mu01",cats(mu1)); %do _i=2 %to &nk-1; _f=prxchange("s/(mu&_i)(\D)/mu0&_i$2/i",-1,_f); _f=tranwrd(_f,"mu0&_i",cats(mu&_i)); %end; _f=prxchange("s/(mu&nk)(\D)/mu0&nk$2/i",-1,_f); f=tranwrd(_f,"mu0&nk",cats(mu&nk)); if index(f,"mu") then do; call symput("muerr",cats(1)); delete; end; run; %let dsid=%sysfunc(open(_fdata)); %let fdnobs=%sysfunc(attrn(&dsid,nobs)); %let rc=%sysfunc(close(&dsid)); %end; %if &muerr %then %do; options notes; %put NOTE: One or more MU index values (x in MUx) in F= or FDATA= exceeds the; %put NOTE- number of means available, &nk, or is missing in set &&set&s...; %put NOTE- Some functions will not be computed.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %end; %if (&contrasts ne and &cntnobs) or &diff ne %then %do; data _cntrs; if _n_=1 then set _lbt; set _cntrs; array lb (*) mu:; array k (*) k:; length f $32767; %if &label=0 %then length label $32767;; do i=1 to dim(lb); %if &label=0 %then label=catx(" ",label,k(i));; %if &ratio %then %do; if k(i)=1 then num=lb(i); if k(i)=-1 then den=lb(i); f=catx("/",num,den); %end; %else if k(i) ne 0 then f=catx("+",f,catx("*",k(i),lb(i))); ; end; %if &ratio and &label=0 %then %do; negpos=index(label,"-"); substr(label,negpos,1)="/"; %end; keep label f; run; %end; %if &fexpnobs=0 and &fdnobs=0 and &cntnobs=0 %then %do; options notes; %put NOTE: No functions or contrasts to compute in estimate set &&set&s.. of COEF=. ; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %goto endset; %end; data _fd; length label $32767; set %if %quote(&f) ne and &fset=&&set&s and &fexpnobs ne 0 %then _fexp; %if &fdata ne and &fdnobs ne 0 %then _fdata; %if (&contrasts ne or &diff ne) and &cntnobs ne 0 %then _cntrs; ; run; /* Call NLEST macro to estimate/test the contrasts */ %if &instore ne %then %NLEST(&dbgopt instore=&instore, where=&where, covdrop=&covdrop, fdata=_fd &dfopt &alphaopt &namopt &ttlopt &prtopt &nullopt); %else %if &inest ne and &incovb ne %then %NLEST(&dbgopt inest=&inest, incovb=&incovb, where=&where, covdrop=&covdrop, fdata=_fd &dfopt &alphaopt &namopt &ttlopt &prtopt &nullopt); /* Append results sets if requested or save as separate files */ %if &append %then %do; %if %sysfunc(exist(est_all)) and &s=1 %then %do; options notes; %put NOTE: Data set EST_ALL already exists. Appending to it.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %end; proc append base=est_all data=est; run; %end; %else %if &nsets > 1 %then %do; data est&s; set est; run; %if %sysfunc(exist(est&s)) %then %do; options notes; %put NOTE: The data set EST&&set&s has been created for estimate set &&set&s in COEF. ; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %end; %end; %endset: %end; /********************* End of processing set of estimates *********************/ /******************************************************************************/ /* Do joint processing for multinomial model with multiple estimate sets when f= or fdata= are specified. */ %if &joint and &nsets>1 and &nfn>1 %then %do; %if &MNjntf %then %do; %do i=1 %to &nsets; proc transpose data=_lbt&i out=_tlbt&i; var mu:; run; %end; data _lbtjnt(drop=_name_); set %do i=1 %to &nsets; _tlbt&i %end; ; run; proc transpose data=_lbtjnt out=_lbt(drop=_name_) prefix=mu; var col1; run; /* Construct fdata= data set for NLEST macro */ %let fexpnobs=0; %let fdnobs=0; %let muerr=0; %if %quote(&f) ne %then %do; data _fexp; length label f _f $32767; if _n_=1 then set _lbt; f="&f"; %if %quote(&flabel) ne %then %do; label="&flabel"; %end; %else label="&f";; keep label f; _f=prxchange("s/(mu1)(\D)/mu01$2/i",-1,f); _f=tranwrd(_f,"mu01",cats(mu1)); %do _i=2 %to &nkjnt-1; _f=prxchange("s/(mu&_i)(\D)/mu0&_i$2/i",-1,_f); _f=tranwrd(_f,"mu0&_i",cats(mu&_i)); %end; _f=prxchange("s/(mu&nkjnt)(\D)/mu0&nkjnt$2/i",-1,_f); f=tranwrd(_f,"mu0&nkjnt",cats(mu&nkjnt)); if index(f,"mu") then do; call symput("muerr",cats(1)); delete; end; output; stop; run; %let dsid=%sysfunc(open(_fexp)); %let fexpnobs=%sysfunc(attrn(&dsid,nobs)); %let rc=%sysfunc(close(&dsid)); %end; %if &fdata ne %then %do; data _fdata; length label f _f $32767; if _n_=1 then set _lbt; set &fdata; %if &fdlabel=0 %then label=f;; keep label f; _f=prxchange("s/(mu1)(\D)/mu01$2/i",-1,f); _f=tranwrd(_f,"mu01",cats(mu1)); %do _i=2 %to &nkjnt-1; _f=prxchange("s/(mu&_i)(\D)/mu0&_i$2/i",-1,_f); _f=tranwrd(_f,"mu0&_i",cats(mu&_i)); %end; _f=prxchange("s/(mu&nkjnt)(\D)/mu0&nkjnt$2/i",-1,_f); f=tranwrd(_f,"mu0&nkjnt",cats(mu&nkjnt)); if index(f,"mu") then do; call symput("muerr",cats(1)); delete; end; run; %let dsid=%sysfunc(open(_fdata)); %let fdnobs=%sysfunc(attrn(&dsid,nobs)); %let rc=%sysfunc(close(&dsid)); %end; %if &muerr %then %do; options notes; %put NOTE: One or more MU index values (x in MUx) in F= or FDATA=; %put NOTE- exceeds the number of means available, &nkjnt, or is missing.; %put NOTE- Some functions will not be computed.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %end; %if &fexpnobs=0 and &fdnobs=0 %then %do; options notes; %put NOTE: No functions or contrasts to compute.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %goto exit; %end; data _fd; length label $32767; set %if %quote(&f) ne and &fexpnobs ne 0 %then _fexp; %if &fdata ne and &fdnobs ne 0 %then _fdata; ; run; /* Call NLEST macro to estimate/test the contrasts */ %if &instore ne %then %NLEST(&dbgopt instore=&instore, where=&where, covdrop=&covdrop, fdata=_fd &dfopt &alphaopt &namopt &ttlopt &prtopt &nullopt); %else %if &inest ne and &incovb ne %then %NLEST(&dbgopt inest=&inest, incovb=&incovb, where=&where, covdrop=&covdrop, fdata=_fd &dfopt &alphaopt &namopt &ttlopt &prtopt &nullopt); %end; %end; /*----------------------- Clean up -----------------------*/ %exit: %if %index(%upcase(&version),DEBUG) %then %do; options nomprint nomlogic nosymbolgen; %put _user_; %end; %else %do; proc datasets nolist nowarn; delete _coef: _ct _setnums _nlvl _glognum _glogden _prexp _cntrs _rows _nrows _glg: _lbt: _tlbt: _fd _fexp _fdata ; run; quit; %end; ods select all; options ¬esopt; %let timenlm=%sysfunc(round(%sysevalf(%sysfunc(datetime())-&timenlm),0.01)); %put NOTE: The &sysmacroname macro used &timenlm seconds.; %mend;