%macro CtoN(version, data=, var=_character_, out=, prefix=, suffix=_N, numvals=, numvaldat=, order=internal, options=) / minoperator; %let time = %sysfunc(datetime()); %let _version=2.0; %if &version ne %then %put NOTE: &sysmacroname macro Version &_version; %let _opts = %sysfunc(getoption(notes)); %let newchk=1; %let version=%upcase(&version); %if %index(&version,NOCHK) %then %let newchk=0; %if %index(&version,DEBUG) %then %do; options notes mprint %if %index(&version,DEBUG2) %then mlogic symbolgen; ; ods select all; %put _user_; %end; %else %do; options nonotes nomprint nomlogic nosymbolgen; ods exclude all; %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 &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 */ %let validopts=NOREPLACE NOFORMAT NONOTES ASIS DESC SUMMARY; %let replace=1; %let format=1; %let notes=1; %let asis=0; %let desc=0; %let sum=0; %let i=1; %do %while (%scan(&options,&i) ne %str() ); %let option&i=%upcase(%scan(&options,&i)); %if &&option&i=NOREPLACE %then %let replace=0; %if &&option&i=NOFORMAT %then %let format=0; %if &&option&i=NONOTES %then %let notes=0; %if &&option&i=ASIS %then %let asis=1; %if &&option&i=DESC %then %let desc=1; %if &&option&i=SUMMARY %then %let sum=1; %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; %if &data= or %sysfunc(exist(&data))=0 %then %do; %put ERROR: Required DATA= data set not specified or not found.; %goto exit; %end; %if (%quote(&numvals) ne or %quote(&numvaldat) ne) and %sysfunc(getoption(mautosource)) ne MAUTOSOURCE %then %do; options mautosource; %if %sysfunc(getoption(mautosource)) ne MAUTOSOURCE %then %do; %put ERROR: NUMVALS= or NUMVALDAT= requires system option MAUTOSOURCE.; %goto exit; %end; %end; %if %quote(&numvaldat) ne and %quote(&numvals) ne %then %do; %if ¬es %then options notes;; %put NOTE: NUMVALS= is ignored when NUMVALDAT= is specified.; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %let numvals=; %end; %if %quote(&numvaldat) ne %then %do; %if %sysfunc(exist(&numvaldat))=0 %then %do; %put ERROR: NUMVALDAT= data %upcase(&numvaldat) not found.; %goto exit; %end; %else %do; %let badnvdat=0; %let dsid=%sysfunc(open(&numvaldat)); %if &dsid %then %do; %let varnum=%sysfunc(varnum(&dsid,VAR)); %if &varnum=0 %then %do; %put ERROR: Required variable VAR not found in NUMVALDAT= data set.; %let badnvdat=1; %end; %else %do; %if %sysfunc(vartype(&dsid,&varnum))=N %then %do; %put ERROR: Required variable VAR in NUMVALDAT= must be character.; %let badnvdat=1; %end; %end; %let varnum=%sysfunc(varnum(&dsid,NUMVALS)); %if &varnum=0 %then %do; %put ERROR: Required variable NUMVALS not found in NUMVALDAT= data set.; %let badnvdat=1; %end; %else %do; %if %sysfunc(vartype(&dsid,&varnum))=N %then %do; %put ERROR: Required variable NUMVALS in NUMVALDAT= must be character.; %let badnvdat=1; %end; %end; %let rc=%sysfunc(close(&dsid)); %if &badnvdat %then %goto exit; %end; %else %do; %put ERROR: Could not open NUMVALDAT= data set.; %goto exit; %end; %end; %end; %if %quote(&out) ne %then %do; %if %sysfunc(notfirst(%substr(&out,1,1))) or %sysfunc(notname(&out)) %then %do; %put ERROR: OUT= must begin with a letter or _ and contain only letters,; %put ERROR- numbers, or underscores.; %goto exit; %end; %end; %if %quote(&var)= %then %do; %put ERROR: VAR= is required.; %goto exit; %end; %if %quote(&prefix)= and %quote(&suffix)= %then %do; %put ERROR: At least one of PREFIX= or SUFFIX= must not be null.; %goto exit; %end; %if %quote(&prefix) ne %then %do; %if %sysfunc(notfirst(%substr(&prefix,1,1))) %then %do; %put ERROR: PREFIX= must begin with a letter or _.; %goto exit; %end; %if %sysfunc(notname(&prefix)) %then %do; %put ERROR: PREFIX= must contain only letters, numbers, or _.; %goto exit; %end; %end; %if %quote(&suffix) ne %then %do; %if %sysfunc(notname(&suffix)) %then %do; %put ERROR: SUFFIX= must contain only letters, numbers, or _.; %goto exit; %end; %end; %let addlen=%eval(%length(&prefix)+%length(&suffix)); %if &addlen>31 %then %do; %put ERROR: The combined lengths of prefix= and suffix= is too long.; %goto exit; %end; %let valorder=INTERNAL DATA FORMATTED FREQ; %if not (%upcase(&order) in &valorder) %then %do; %put ERROR: Valid values of ORDER= are INTERNAL, DATA, FORMATTED, FREQ.; %goto exit; %end; /* Truncate output data set if needed. If out= not specified, add _N suffix */ %let addsuff=0; %let maxlen=32; %if &out= %then %do; %let addsuff=1; %let maxlen=30; %let out=&data; %end; %let out1=%scan(&out,1); %let out2=%scan(&out,2); %if &out2= %then %do; %let outlib=; %let outdsn=&out1; %end; %else %do; %let outlib=&out1; %let outdsn=&out2; %end; %let vallen=%eval(%sysfunc(min(%length(&outdsn),&maxlen))); %let outdsn=%substr(&outdsn,1,&vallen); %if &addsuff %then %let outdsn=&outdsn._N; %if &outlib ne %then %let out=&outlib..&outdsn; %else %let out=&outdsn; /* Initialize and prepare list of variables to process */ data _newvars; run; /* var= not specified */ %if %index( %quote(%upcase(%nrquote(&var))) , _CHARACTER_ ) %then %do; proc contents data=&data; ods output variables=_vars; run; data _null_; set _vars end=eof; nvar+(type='Char') ; if eof then call symputx("nvar",nvar); run; %if &nvar=0 %then %goto final; data _null_; set &data; array c (*) _character_; length _varlist _vwids $32767 _cvar $200; do _i=1 to dim(c); call vname(c(_i),_cvar); _varlist=catx(' ',_varlist,_cvar); _vwids=catx(' ',_vwids,vlength(c(_i))); end; call symputx("varlist",_varlist); call symputx("vwids",_vwids); stop; run; %end; /* var= specified: expand any lists and remove any not found or that are numeric */ %else %do; /* check for variable lists using hyphen (-) and expand */ %let i=1; %let varinit=; %do %while (%quote(%scan(%nrquote(&var),&i,%str( ))) ne %str() ); %let v=%scan(%nrquote(&var),&i,%str( )); %if %index(%nrquote(&v),%str(-)) %then %do; data _null_; set &data; array c (*) &v; length _v $32767 _cvar $200; do _i=1 to dim(c); call vname(c(_i),_cvar); _v=catx(' ',_v,_cvar); end; call symputx("vlist",_v); stop; run; %let varinit=&varinit &vlist; %end; %else %let varinit=&varinit &v; %let i=%eval(&i+1); %end; %let var=&varinit; /* Check that each variable exists and is character */ %let varinit=&var; %let varlist=; %let vwids=; %let i=1; %let nvar=0; %let dsid=%sysfunc(open(&data)); %if &dsid %then %do; %do %while (%scan(&varinit,&i) ne %str() ); %let var=%scan(&varinit,&i); %let varnum=%sysfunc(varnum(&dsid,&var)); %if &varnum=0 %then %do; %if ¬es %then options notes;; %put NOTE: VAR= variable, %upcase(&var), not found. Skipping.; %if %index(&version,DEBUG)=0 %then options nonotes;; %goto next; %end; %let vwids=&vwids %sysfunc(varlen(&dsid,&varnum)); %if %sysfunc(vartype(&dsid,&varnum))=N %then %do; %if ¬es %then options notes;; %put NOTE: VAR= variable, %upcase(&var), is already numeric. Skipping.; %if %index(&version,DEBUG)=0 %then options nonotes;; %goto next; %end; %let varlist=&varlist &var; %let nvar=%eval(&nvar+1); %next: %let i=%eval(&i+1); %end; %let rc=%sysfunc(close(&dsid)); %end; %else %do; %put ERROR: Data set, %upcase(&data), could not be opened.; %goto exit; %end; %end; %if &nvar=0 %then %goto final; /* Get order (0,1,2) of the number of variables to process */ %let onvar=%sysfunc(floor(%sysfunc(log10(&nvar)))); /* Process each variable. Check it exists and is character. */ %let j=1; %let numvnotfnd=0; %let badnumval=0; %do %while (%scan(&varlist,&j) ne %str() ); %let var=%scan(&varlist,&j); /* If numvals= or numvaldat= specified, put numeric values in macro variables */ %if &asis=0 and %quote(&numvaldat) ne %then %do; %let numvals=; %let vfnd=0; data _null_; set &numvaldat; if upcase(var)=upcase("&var") then do; call symputx('numvals',numvals); call symputx('vfnd','1'); stop; end; run; %if &vfnd=0 %then %let numvnotfnd=1; %end; %if &asis=0 and %quote(&numvals) ne %then %do; %let k=1; %let badnumval=0; %do %while (%scan(&numvals,&k) ne %str() ); %let tmp=%scan(&numvals,&k); %if %datatyp(&tmp)=NUMERIC %then %let nv&k=&tmp; %else %let badnumval=1; %let k=%eval(&k+1); %end; %if &badnumval %then %do; %let tnumvals=&numvals; %let numvals=; %if ¬es %then options notes;; %put NOTE: Nonnumeric values found for variable %upcase(&var) in the; %put NOTE- NUMVALDAT= data set. Assigning default numeric values.; %if %index(&version,DEBUG)=0 %then options nonotes;; %end; %let nnumval=%eval(&k-1); %end; /* Get character values, order them, create format */ %if &asis=0 %then %do; proc freq data=&data order=ℴ table &var / noprint out=_tmp(where=(not missing(&var))); run; %if &desc %then %do; data _tmp; set _tmp; _obs=_n_; run; proc sort data=_tmp; by descending _obs; run; %end; data _null_; set _tmp nobs=nlev end=eof; call symput(cats('l',_n_),catt(&var)); if eof then call symputx('nlev',nlev); run; %if &format %then %do; /* Create format with the original character values */ %let lastdig=%sysfunc(anydigit(&var,-32)); %if &lastdig=%length(&var) %then %do; %if %length(&var)+3+&onvar > 32 %then %let trunc=%eval(3+&onvar); %else %let trunc=0; %let fmtname=%substr(&var,1,&lastdig-&trunc)_&j._; %end; %else %let fmtname=&var; proc format; value &fmtname %do i=1 %to &nlev; %if %quote(&numvals) ne %then %do; %if &nnumval>=&i %then &&nv&i="%superq(l&i)"; %end; %else &i="%superq(l&i)"; %end; ; run; %if &syserr %then %do; %put ERROR: Cannot assign multiple formatted values to one numeric value for; %put ERROR- variable %upcase(&var). Try options=noformat to prevent format creation.; %goto skip; %end; %end; %end; /* Create numeric variable name using prefix= and suffix= */ %let overvalid=%eval(%length(&var)+&addlen-32); %if &overvalid>0 %then %do; %let newnamlen=%eval(%length(&var)-&overvalid); %if &newnamlen<=0 %then %do; %put ERROR: The length of prefix= and/or suffix= is too long for variable &var..; %put ERROR: Skipping this variable.; %goto skip; %end; %let newname=&prefix%substr(&var,1,&newnamlen)&suffix; %end; %else %let newname=&prefix&var&suffix; /* Create new variable with numeric values and optional format */ data _newvar; set &data; %if &asis %then %do; &newname=input(&var, ?? comma%scan(&vwids,&j).); %let format=0; %end; %else %do; select (&var); %do i=1 %to &nlev; when ("%superq(l&i)") %if %quote(&numvals) ne %then %do; %if &nnumval<&i %then &newname = .; %else &newname = &&nv&i; %end; %else &newname = &i; ; %end; otherwise &newname = .; end; %if &format %then %do; format &newname &fmtname..; %end; %else %do; format &newname; %end; %end; keep &var &newname; run; %if &sum %then %do; proc freq data=_newvar; table &var*&newname/out=_sum&j(drop=count percent); format &var &newname; %if &replace %then %do; label &newname= %if &asis %then "ASIS &var"; %else "CODED &var"; ; %end; run; %end; data _newvar; set _newvar; drop &var; run; %if &replace %then %do; data _newvar; set _newvar; &var=&newname; %if &format %then %do; format &var &fmtname..; %end; %else %do; format &var; %end; keep &var; run; %end; data _newvars; merge _newvars _newvar; %if &format %then %do; %if &replace %then %str(format &var &fmtname..;); %else %str(format &newname &fmtname..;); %end; run; %skip: %if %quote(&numvals) ne and %quote(&numvaldat)= %then %do; %if &badnumval %then %let numvals=&tnumvals; %end; %let j=%eval(&j+1); %end; %if %quote(&numvaldat) ne and &numvnotfnd=1 %then %do; %if ¬es %then options notes;; %put NOTE: VAR= variables not found in NUMVALDAT= were assigned default values.; %if %index(&version,DEBUG)=0 %then options nonotes;; %end; /* Create final data set */ %final: %if &nvar=0 %then %do; %if ¬es %then options notes;; %put NOTE: No character variables to process.; %end; %else %do; %if &sum %then %do; data _sumall; merge _sum1-_sum&nvar; run; ods select all; proc print data=_sumall noobs %if &replace %then label; ; title2 "CtoN Macro: Summary of value conversions"; run; %end; data &out; merge &data %if &replace %then (drop=&varlist); _newvars; run; %if %sysfunc(exist(&out)) %then %do; %let dsid=%sysfunc(open(&out)); %let outnobs=%sysfunc(attrn(&dsid,nobs)); %let outnvar=%sysfunc(attrn(&dsid,nvars)); %let rc=%sysfunc(close(&dsid)); %if ¬es %then options notes;; %put NOTE: The data set %upcase(&out) has &outnobs observations and &outnvar variables.; %end; %end; %if %index(&version,DEBUG)=0 %then options nonotes;; %exit: %if %index(&version,DEBUG)=0 %then %do; proc datasets nolist; delete _tmp _vars _newvar _newvars _sum: ; run; quit; %end; %if %index(&version,PASS)=0 %then %do; %if %index(&version,DEBUG) %then %do; options nomprint nomlogic nosymbolgen; %put _user_; %end; %end; ods select all; %let time = %sysfunc(round(%sysevalf(%sysfunc(datetime()) - &time), 0.01)); %if ¬es %then options notes;; %put NOTE: The &sysmacroname macro used &time seconds.; %if &sum %then title2;; options &_opts; %mend;