Last active
October 2, 2018 14:59
-
-
Save statgeek/0ff46bbd76bcc101e40945816b232303 to your computer and use it in GitHub Desktop.
SAS Summary table chi square
This file contains hidden or 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
| /*--------------------------------------------------------------------------------*/ | |
| /* Author: F. Khurshed */ | |
| /* Date: June 17, 2011 */ | |
| /* Purpose: This macro creates a set of chisquare tables appended together. */ | |
| /* It includes the chisquare value. */ | |
| /* Modified by: F. Khurshed */ | |
| /* Date: January 17, 2012 */ | |
| /* Purpose: Adding in the ability for the macro to add the total column. */ | |
| /* Modified by: F. Khurshed */ | |
| /* Date: February 6, 2012 */ | |
| /* Purpose: Add in row of overall by cross category. */ | |
| /* Modified by: W. Qiu */ | |
| /* Date: December 14, 2016 */ | |
| /* Purpose: 1. Make lengths of some output variables consistent to avoid */ | |
| /* warning messages; */ | |
| /* 2. Re-order output variables for better display; */ | |
| /* 3. Remove some unnecessary coding; */ | |
| /* 4. Change positional parameters in the macro to keyword parameters */ | |
| /* so that the order of parameters doesn't matter; */ | |
| /* 5. Add the parameter validation process so that if any parameter is */ | |
| /* not correctly listed, a warning message will pop up and execution */ | |
| /* stops; */ | |
| /* 6. Add a valid value for rc to allow both row and column percentages; */ | |
| /* 7. Allow rc to take versatile values as long as the first letter is */ | |
| /* r, c, or b (case insensitive); */ | |
| /* 8. Add two more parameters: replace and nlevel_cutoff; */ | |
| /* 9. Allow cross variable to have missing values and output a seperate */ | |
| /* column as the missing category if parameter missing is not null. */ | |
| /*--------------------------------------------------------------------------------*/ | |
| /*--------------------------------------------------------------------------------*/ | |
| /* PARAMETERS: */ | |
| /* dsetin = name of the input dataset */ | |
| /* dsetout = name of output dataset */ | |
| /* varlist = varlist to perform the operations, seperated by space */ | |
| /* varcross = var to be across the top row */ | |
| /* rc = row, column or both, depending on what you want the percentages to be. */ | |
| /* You don't have to spell completely, as long as the first letter is */ | |
| /* r, c, or b, case insensitive */ | |
| /* missing = null or missprint or missing, depending on whether you want missing */ | |
| /* values to be included in the frequency table or counted in the */ | |
| /* percentage */ | |
| /* replace = whether allow output dataset to be replaced by a new run. */ | |
| /* If not specified as a word with initial of Y or y, no replacement */ | |
| /* will be assumed, and if a same named dataset as dsetout already */ | |
| /* exists, a warning message will pop up */ | |
| /* nlevel_cutoff = a threshold of number of distinct values in a variable, if */ | |
| /* larger than it, will consider the variable to have too many */ | |
| /* levels and a warning message will pop up. It can take any */ | |
| /* integer values */ | |
| /*--------------------------------------------------------------------------------*/ | |
| /*--------------------------------------------------------------------------------*/ | |
| /* An example of calling the macro: */ | |
| /* %summary_table(dsetin=sashelp.class, dsetout=test, varlist=age weight, */ | |
| /* varcross=sex, rc=c, missing=missing, replace=y, nlevel_cutoff=20)*/ | |
| /*--------------------------------------------------------------------------------*/ | |
| %macro summary_table(dsetin=, dsetout=, varlist=, varcross=, rc=, missing=, replace=, nlevel_cutoff=); | |
| /****************************************************/ | |
| /************ Parameter Validation Starts ***********/ | |
| /****************************************************/ | |
| /*check if the cross variable or variables in the varlist exists*/ | |
| /*also check if any variable name in input dataset conflicts with default var names in certain procedures*/ | |
| %let stop=N; | |
| %let allvar=%sysfunc(catx(%str( ),&varcross.,&varlist.)); | |
| %let varexcl=%str(count percent vart value rc category var overall chisq pct_row pct_col); | |
| %if %sysfunc(exist(&dsetin.)) %then %do; | |
| %let i=1; | |
| %do %while (%scan(&allvar, &i, " ") ^=%str()); | |
| %let varr=%scan(&allvar, &i); | |
| data _null_; | |
| dsid=open("&dsetin."); | |
| num1=varnum(dsid,"&varr."); | |
| if (num1=0) then do; | |
| file print; | |
| put #3 @10 "The variable &varr. not found in the data set &dsetin.!"; | |
| call symput("stop","Y"); | |
| end; | |
| run; | |
| %let j=1; | |
| %do %while (%scan(&varexcl, &j, " ") ^=%str()); | |
| %let varn=%scan(&varexcl, &j, " "); | |
| %if %upcase(&varr)=%upcase(&varn) %then %do; | |
| data _null_; | |
| file print; | |
| put #3 @10 "The variable name &varn. conflicts with the macro procedure, please rename!"; | |
| call symput("stop","Y"); | |
| run; | |
| %end; | |
| %let j=%eval(&j+1); | |
| %end; | |
| %let i=%eval(&i+1); | |
| %end; | |
| %if &stop.=Y %then %goto quit; | |
| %end; | |
| /*check if the input dataset exists*/ | |
| %else %do; | |
| data _null_; | |
| file print; | |
| put #3 @10 "The input data set &dsetin. does not exist. Please correct!"; | |
| run; | |
| %goto quit; | |
| %end; | |
| /*check if output dataset already exists if replacement option not allowed*/ | |
| %if %substr(%upcase(&replace.),1,1)~=Y %then %do; | |
| %if (%sysfunc(exist(&dsetout.))) %then %do; | |
| data _null_; | |
| file print; | |
| put #3 @10 "The output data set &dsetout. already exists. Please change the name or allow replacement!"; | |
| run; | |
| %goto quit; | |
| %end; | |
| %end; | |
| /*check levels for the variables*/ | |
| ods select none; | |
| ods output nlevels=n_levels; | |
| proc freq data=&dsetin nlevels; | |
| table &varcross &varlist; | |
| run; | |
| %let nvar=%eval(%sysfunc(countw(&varlist))+1); | |
| proc sql noprint; | |
| select nlevels, TableVar | |
| into :lvl1-:lvl&nvar, :vlist1-:vlist&nvar | |
| from n_levels; | |
| quit; | |
| ods select all; | |
| /*check if the cross variable only has one level*/ | |
| %do i=1 %to &nvar; | |
| %if &lvl1=1 %then %do; | |
| data _null_; | |
| file print; | |
| put #3 @10 "The cross variable &varcross. has only one level!"; | |
| run; | |
| %goto quit; | |
| %end; | |
| /*check if any of the variables has too many levels, determined by nlevel_cutoff*/ | |
| %if &&lvl&i>&nlevel_cutoff %then %do; | |
| data _null_; | |
| file print; | |
| put #3 @10 "The variable &&vlist&i. has too many levels!"; | |
| run; | |
| %goto quit; | |
| %end; | |
| %end; | |
| /****************************************************/ | |
| /************ Parameter Validation Ends *************/ | |
| /****************************************************/ | |
| /*Add in overall row at the top and the totals*/ | |
| ods select none; | |
| proc freq data=&dsetin; | |
| table &varcross / chisq &missing out=temp1; | |
| ods output OneWayChiSq=chisq; | |
| run; | |
| /*determine the type of the cross variable first in order to do next steps*/ | |
| proc contents data=&dsetin noprint out=vtype; run; | |
| proc sql noprint; | |
| select type into :typecross | |
| from vtype | |
| where upcase(name)=upcase("&varcross"); | |
| quit; | |
| %if &typecross=1 %then %do; | |
| data temp2; | |
| length value $ 50; | |
| set temp1 end=eof; | |
| value=catt(left(count)," (",left(put(percent, 8.1)),"%)"); *left function for count is unnecessary; | |
| var='Overall'; | |
| %if %length(&missing)~=0 %then %do; | |
| if missing(&varcross) then &varcross=9999; | |
| %end; | |
| keep var &varcross count value; | |
| run; | |
| %end; | |
| %else %do; | |
| data temp2; | |
| length value &varcross $ 50; | |
| set temp1 end=eof; | |
| value=catt(left(count)," (",left(put(percent, 8.1)),"%)"); *left function for count is unnecessary; | |
| var='Overall'; | |
| %if %length(&missing)~=0 %then %do; | |
| if missing(&varcross) then &varcross="missing"; | |
| %end; | |
| keep var &varcross count value; | |
| run; | |
| %end; | |
| /*Transpose data to better structure*/ | |
| proc transpose data=temp2 out=temp3 (drop = _name_ ) prefix=&varcross._; | |
| var value; | |
| id &varcross; | |
| run; | |
| /*Get total count*/ | |
| proc sql noprint; | |
| select count(*) into :tot_count | |
| from &dsetin; | |
| quit; | |
| /*Get P-Value from chisq table*/ | |
| proc sql noprint; | |
| select nvalue1 into :p_value | |
| from chisq | |
| where name1='P_PCHI'; | |
| quit; | |
| data &dsetout; | |
| set temp3; | |
| length vart $256. category $256. overall $256.; /*change length of all string variables to 256 to be consistent and safe*/ | |
| vart='Overall'; | |
| category='N(%)'; | |
| overall=left("&tot_count."||" (100.0%)"); | |
| chisq=&p_value; | |
| run; | |
| /*loop through variable list*/ | |
| %let i=1; | |
| %do %while (%scan(&varlist, &i, " ") ^=%str()); | |
| %let var=%scan(&varlist, &i, " "); | |
| data _null_; | |
| set &dsetin; | |
| call symput("var_fmt", vformat(&var)); | |
| run; | |
| /*Get frequency counts and chi-square stats*/ | |
| proc freq data=&dsetin; | |
| where not missing(&var.); | |
| tables &var.*&varcross./out=temp1 chisq outpct &missing; | |
| ods output chisq=chisq; | |
| run; | |
| /*get fisher's exact stats if a cell count is less than 5*/ | |
| /* proc sql noprint;*/ | |
| /* select min(count) into :min_count*/ | |
| /* from temp1;*/ | |
| /* quit;*/ | |
| /**/ | |
| /* proc freq data=&dsetin;*/ | |
| /* tables &var.*&varcross/exact &missing;*/ | |
| /* ods output FishersExact=exact;*/ | |
| /* run;*/ | |
| /*recode missing and concat into one variable*/ | |
| %if &typecross=1 %then %do; | |
| data tmp1; | |
| length value $ 50; | |
| set temp1 (where=( not missing(&var.))); | |
| value=catt(left(count)," (",left(put(pct_row, 8.1)),"%)"); | |
| %if %length(&missing)~=0 %then %do; | |
| if missing(&varcross) then &varcross=9999; | |
| %end; | |
| keep &var &varcross count value; | |
| run; | |
| data tmp2; | |
| length value $ 50; | |
| set temp1; | |
| value=catt(left(count)," (",left(put(pct_col, 8.1)),"%)"); | |
| %if %length(&missing)~=0 %then %do; | |
| if missing(&varcross) then &varcross=9999; | |
| %end; | |
| keep &var &varcross count value; | |
| run; | |
| %end; | |
| %else %do; | |
| data tmp1; | |
| length value &varcross $ 50; | |
| set temp1; | |
| value=catt(left(count)," (",left(put(pct_row, 8.1)),"%)"); | |
| %if %length(&missing)~=0 %then %do; | |
| if missing(&varcross) then &varcross="missing"; | |
| %end; | |
| keep &var &varcross count value; | |
| run; | |
| data tmp2; | |
| length value &varcross $ 50; | |
| set temp1; | |
| value=catt(left(count)," (",left(put(pct_col, 8.1)),"%)"); | |
| %if %length(&missing)~=0 %then %do; | |
| if missing(&varcross) then &varcross="missing"; | |
| %end; | |
| keep &var &varcross count value; | |
| run; | |
| %end; | |
| proc sort data=tmp1; | |
| by &var &varcross; | |
| run; | |
| proc sort data=tmp2; | |
| by &var &varcross; | |
| run; | |
| /*Transpose data to better structure*/ | |
| proc transpose data=tmp1 out=tmp3 (drop = _name_ ) prefix=&varcross._; | |
| by &var.; | |
| var value; | |
| id &varcross; | |
| run; | |
| proc transpose data=tmp2 out=tmp4 (drop = _name_ ) prefix=&varcross._; | |
| by &var.; | |
| var value; | |
| id &varcross; | |
| run; | |
| %if %substr(%upcase(&rc.),1,1)=R %then %do; | |
| data temp3; | |
| set tmp3; | |
| rc="Row"; | |
| run; | |
| %end; | |
| %else %if %substr(%upcase(&rc.),1,1)=C %then %do; | |
| data temp3; | |
| set tmp4; | |
| rc="Col"; | |
| run; | |
| %end; | |
| %else %do; | |
| data tmp5; | |
| set tmp3; | |
| rc="Row"; | |
| run; | |
| data tmp6; | |
| set tmp4; | |
| rc="Col"; | |
| run; | |
| data temp3; | |
| set tmp5 tmp6; | |
| run; | |
| proc sort data=temp3; | |
| by &var; | |
| run; | |
| %end; | |
| proc datasets nodetails nolist; | |
| delete tmp1-tmp6; | |
| quit; | |
| /*Get the overall counts for each dataset as well*/ | |
| proc freq data=&dsetin; | |
| where not missing(&var.); | |
| tables &var/out=overall1 &missing; | |
| run; | |
| data overall2; | |
| length overall $256.; | |
| set overall1; | |
| overall=catt(left(count)," (",left(put(percent, 8.1)),"%)"); | |
| keep &var overall; | |
| run; | |
| /*Merge in the overall column*/ | |
| data temp3a; | |
| merge temp3 overall2; | |
| by &var.; | |
| run; | |
| /*Add in the test stats*/ | |
| data temp4; | |
| length vart $256. category $256.; | |
| set temp3a; | |
| if _n_=1 then do; | |
| set chisq (where=(Statistic="Chi-Square") keep=prob statistic rename=(prob=chisq)); | |
| /*set exact (where=(name1="XP2_FISH") keep=Name1 cValue1 rename=(cValue1=fisher));*/ | |
| end; | |
| vart=vlabel(&var.); | |
| category=left(put(&var., &var_fmt)); | |
| drop &var. statistic name1; | |
| run; | |
| /* | |
| data temp4; | |
| length vart $256. category $256.; | |
| set temp3a; | |
| if _n_=1 then do; | |
| set chisq (where=(Statistic="Chi-Square") keep=prob statistic rename=(prob=chisq)); | |
| end; | |
| vart=vlabel(&var.); | |
| category=left(put(&var., &var_fmt)); | |
| drop &var. statistic; | |
| run; | |
| */ | |
| /*Append datasets*/ | |
| data &dsetout; | |
| set &dsetout temp4; | |
| run; | |
| /*drop temp tables so theres no accidents*/ | |
| proc datasets nodetails nolist; | |
| delete temp1-temp4 temp3a chisq /*exact*/; | |
| quit; | |
| /*Increment counter*/ | |
| %let i=%eval(&i+1); | |
| %end; | |
| data &dsetout; | |
| retain vart rc category overall; | |
| set &dsetout; | |
| length p_value $8. test $15.; | |
| by vart notsorted; | |
| if first.vart then do; | |
| test='Chi-Square'; | |
| p_value=left(put(chisq, 8.4)); | |
| seq=1; | |
| end; | |
| else seq+1; | |
| /* if seq=2 then do;*/ | |
| /* test="Fisher's Exact";*/ | |
| /* p_value=left(put(fisher, 8.4));*/ | |
| /* end;*/ | |
| drop chisq fisher seq; | |
| run; | |
| /*drop unnecessary tables*/ | |
| proc datasets nodetails nolist; | |
| delete overall1 overall2 n_levels vtype; | |
| quit; | |
| ods select all; | |
| %quit: | |
| %mend summary_table; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment