/*****************************************************************************/

/*****************************************************************************/ *SAS Code Impute Missing Values.sas
*Author: Abt SRBI *Created: 6/25/12 Purpose: This is SAS code that imputes missing data for raking variables in national and inland samples of the 316(b) stated preference (SP) survey. /*****************************************************************************/ /*****************************************************************************/

/* Assign a library to permanently store SAS dataset with imputed raking variables*/ libname impute ''; options nofmterr; run;

/*******************************************************************************/ /*** NATIONAL SAMPLE ***/ /*******************************************************************************/

/************************************************************/ /* Hot_Deck_Imputation.sas */ /* This is a SAS hot deck imputation program adapted from */ /* "Unweighted Sequential Hot Deck Imputation" developed by */ /* Barbara Lepidus Carlson and Linda S. Bandeh, and */ /* documented in "SAS Macros Useful in Imputing Missing */ /* Survey Data", Carlson, Cox & Bandeh, SUGI 20, April 1995 */ /* by Bruce Ellis, April 2007, Battelle Memorial Institute, */ /* to 1) consolidate Initial Value Routine, Serpentine */ /* sorting and imputation into a single */ /* stand-alone program,

*/ /* 2) support iterative execution for a series of */ /* imputations,

*/ /* 3) automatically save imputation flag variables, */ /* 4) support multiple missing values or a variable */ /* identifying cases to be imputed, */ /* 5) automatically generate some macro variables and */ /* simplify user input,

*/ /* 6) iteratively merge imputed variables and */ /* imputation flags to a single output file. */ /* 7) handles series of variables correctly. */ /* */ /************************************************************/

/******************************************************************************** *************/ /******************************************************************************** *************/ /* IMPUTATION 1.

GENDER */ /******************************************************************************** *************/ /******************************************************************************** *************/

/*********************************************************************/ /* USER INPUT */ /*********************************************************************/ /*plug in folder with your input file===>*/ LIBNAME LIB ''; Page 1/*input filename========================>*/ %LET INFILE = national;

/*output filename=======================>*/ %LET OUTFILE = national_i; /*variable uniquely identifies records==>*/ %LET ID = ID; /*IF YOU HAVE A VARIABLE IDENTIFYING RESPONDENTS (CODED 1) AND NON-*/ /*RESPONDENTS (CODED 0) REPLACE 'NONE' WITH THE VARIABLE NAME BELOW*/ /*optional response var (1=R 0=NR)======>*/ %LET RESPONSE = NONE; /*********************************************************************/ /* SKIP TO BOTTOM FOR MORE USER INPUT */ /*********************************************************************/ option ps=55 ls=180 formchar=' ' macrogen symbolgen mprint mlogic; /* MACROS to generate code if RESPONSE variable in use */ %MACRO RESPCDEA; %IF %UPCASE(&RESPONSE)=NONE %THEN %DO;MISSVAR ~in(&MVALUE) %END; %ELSE %DO;&RESPONSE=1 %END; %MEND RESPCDEA; %MACRO RESPCDEB; %IF %UPCASE(&RESPONSE)=NONE %THEN %DO;MISSVAR IN(&MVALUE) %END; %ELSE %DO;&RESPONSE=0 %END; %MEND RESPCDEB; data lib.&OUTFILE;set lib.&INFILE; %MACRO HOTDECK(MISSVAR,CLASS,SORTVAR,DIRECTN,MVALUE,IMPALL); /* find LSTCLASS from macro variable */ %LET LSTCLASS = &CLASS; %DO %WHILE (%INDEX(&LSTCLASS, %STR( ) ) GT 1); %LET POS = %INDEX(&LSTCLASS, %STR( ) ); %LET LSTCLASS = %SUBSTR(&LSTCLASS, &POS ); %END; /* establish initial values */ PROC SORT DATA=LIB.&OUTFILE; BY &CLASS &SORTVAR; DATA SOURCE2;SET LIB.&OUTFILE; BY &CLASS &SORTVAR; RESPIND=9;RETAIN RESPFLAG; IF FIRST.&LSTCLASS THEN RESPFLAG=0; ARRAY MISSVAR &MISSVAR;

DO OVER MISSVAR; IF %RESPCDEA & RESPFLAG=0 THEN DO; RESPIND=1;RESPFLAG=1;

END;
END;
RUN; PROC SORT DATA=SOURCE2; BY &CLASS RESPIND &SORTVAR; /* serpentine sorting */ PROC SURVEYSELECT DATA=SOURCE2 OUT=SOURCE3 METHOD=SYS RATE=1.0 NOPRINT; STRATA &CLASS; CONTROL &SORTVAR; /* generate macro variable with imputation flags */ %LET COUNT = 1; %LET FLAGVAR = ; %DO %UNTIL (&PART = %STR( ) ); %LET PART = %SCAN(&MISSVAR, &COUNT ); %LET COUNT = %EVAL( &COUNT + 1); %IF &PART NE %STR( ) %THEN %LET FLAGVAR = %STR(&FLAGVAR &PART._IF); %END; %IF %UPCASE(&DIRECTN)=BOTH OR %UPCASE(&DIRECTN)=PREV %THEN %LET START=1; %ELSE %LET START=2; %IF %UPCASE(&DIRECTN)=BOTH OR %UPCASE(&DIRECTN)=NEXT %THEN %LET XEND=2; %ELSE %LET XEND=1; %LOCAL J; %DO J=&START %TO &XEND;

PROC SORT %IF (%UPCASE(&DIRECTN)=BOTH AND &J=2) %THEN %DO;

DATA=HOTDECK1 %END; Page 2%ELSE %DO;

DATA=SOURCE3 (KEEP=&ID &CLASS &MISSVAR &SORTVAR %IF &RESPONSE NE NONE %THEN %DO; &RESPONSE %END; ) %END;

OUT=SORTFILE; BY &CLASS %IF &J=2 %THEN %DO; DESCENDING %END; &SORTVAR; /****************************************************************/ /* The following steps will allow you to find the first or last */ /* respondent within a sorted imputation class */ /****************************************************************/ DATA RESPIND&J (DROP=RESPFLAG XABORT);

SET SORTFILE END=EOF; BY &CLASS; RETAIN RESPFLAG XABORT 0; RESPIND&J=9; ARRAY MISSVAR &MISSVAR; CALL SYMPUT('NMISSV',LEFT(TRIM(DIM(MISSVAR)))); IF FIRST.&LSTCLASS THEN RESPFLAG=0; IF RESPFLAG EQ 0 THEN DO; RESPIND&J=1; DO OVER MISSVAR; IF %RESPCDEB THEN RESPIND&J=9; END;

END; IF RESPIND&J EQ 1 THEN RESPFLAG=1; IF LAST.&LSTCLASS AND RESPFLAG EQ 0 THEN DO; ERROR 'ERROR: AT LEAST ONE IMPUTATION CLASS WITH NO DONOR ' &LSTCLASS=; XABORT+1;

END; IF EOF & XABORT THEN ABORT;

RUN; PROC SORT DATA=RESPIND&J; BY &CLASS RESPIND&J %IF &J=2 %THEN %DO; DESCENDING %END; &SORTVAR; DATA HOTDECK&J (DROP=PRVR&J.R1-PRVR&J.R&NMISSV DONORID); SET RESPIND&J; BY &CLASS; RETAIN PRVR&J.R1-PRVR&J.R&NMISSV DONORID; ARRAY MISSVAR &MISSVAR; ARRAY IMPVAR&J IMPV&J.R1-IMPV&J.R&NMISSV; ARRAY PREVAR&J PRVR&J.R1-PRVR&J.R&NMISSV; IF FIRST.&LSTCLASS THEN DO; DO OVER PREVAR&J; PREVAR&J=.; END;

DONORID=.;
END; NONMISS=1;

DO OVER MISSVAR; IF %RESPCDEB THEN NONMISS=0;

END; IF NONMISS EQ 1 THEN DO;

DO OVER MISSVAR; IMPVAR&J=MISSVAR; PREVAR&J=MISSVAR;

END; DONORID=&ID; IRFLAG=0; DONOR&J=.;

END; ELSE IF NONMISS EQ 0 THEN DO;

DO OVER MISSVAR; IMPVAR&J=PREVAR&J;

END; IRFLAG=1; DONOR&J=DONORID;

END;
RUN; %END; /********************************************/ /* The next section randomly selects either */ /* the previous or next value imputed. */ /********************************************/ %IF %UPCASE(&DIRECTN)=BOTH %THEN %DO; DATA IMPUTED; SET HOTDECK2;Page 3ARRAY IMPVAR1R IMPV1R1-IMPV1R&NMISSV;

ARRAY IMPVAR2R IMPV2R1-IMPV2R&NMISSV; ARRAY DONOR DONOR1-DONOR&NMISSV; ARRAY MISSVAR &MISSVAR; RAN=MOD(INT(RANUNI(0)*10),2)+1; IF RAN EQ 1 THEN DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR1R; DONOR=DONOR1;

END;
END; CHOSE='PREV';

END; ELSE IF RAN EQ 2 THEN DO;

DO OVER MISSVAR; MISSVAR=IMPVAR2R; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR2R; DONOR=DONOR2;

END;
END; CHOSE='NEXT';

END;
RUN; %END; /********************************************************/ /* This section is for when only one direction is used. */ /********************************************************/ %ELSE %IF %UPCASE(&DIRECTN)=PREV OR %UPCASE(&DIRECTN)=NEXT %THEN %DO; DATA IMPUTED; SET HOTDECK&XEND; ARRAY IMPVAR1 IMPV1R1-IMPV1R&NMISSV; ARRAY IMPVAR2 IMPV2R1-IMPV2R&NMISSV; ARRAY DONOR DONOR1-DONOR&NMISSV; ARRAY MISSVAR &MISSVAR; %IF %UPCASE(&DIRECTN)=PREV %THEN %DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR1; DONOR=DONOR1;

END;
END; CHOSE='PREV'; %END; %ELSE %IF %UPCASE(&DIRECTN)=NEXT %THEN %DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR2; DONOR=DONOR2;

END;
END; CHOSE='NEXT'; %END;

RUN; /* generate imputation flags and eliminate working variables */ DATA IMPUTED(KEEP=&ID CHOSE &MISSVAR &FLAGVAR);

SET IMPUTED; ARRAY FLAGVAR &FLAGVAR; ARRAY DONOR DONOR1-DONOR&NMISSV;

DO OVER FLAGVAR;
FLAGVAR = DONOR;
END;
RUN; /* merge results back to original file */ PROC SORT DATA=LIB.&OUTFILE OUT=SOURCE1;BY &ID; PROC SORT DATA=IMPUTED;BY &ID; DATA LIB.&OUTFILE; MERGE SOURCE1(DROP=&MISSVAR) IMPUTED;BY &ID; Page 4RUN;

PROC FREQ;TABLES &MISSVAR &FLAGVAR / MISSING; %END; /******************************************************************/ /* The next section identifies donors used more than three times. */ /******************************************************************/ PROC FREQ DATA=LIB.&OUTFILE; %LET DONOR = %SCAN(&FLAGVAR, 1 ); WHERE ( %SCAN(&FLAGVAR, 1 ) > .); TABLES &DONOR/NOPRINT OUT=CHECKCNT;

PROC FREQ DATA=CHECKCNT;
TABLES COUNT; TITLE 'NUMBER OF TIMES DONORS WERE USED';RUN;

DATA CHECKCNT;SET CHECKCNT; DONOR = %SCAN(&FLAGVAR, 1 ); DATA TEMPFILE; SET LIB.&OUTFILE (KEEP=&ID &CLASS &SORTVAR &MISSVAR); DONOR=&ID;

PROC SORT DATA=TEMPFILE; BY DONOR; DATA MORTHAN3; MERGE CHECKCNT (IN=A) TEMPFILE; BY DONOR; IF A; IF DONOR NE . AND COUNT > 3; PROC PRINT DATA=MORTHAN3; VAR DONOR COUNT &MISSVAR &CLASS &SORTVAR; TITLE 'DONORS USED MORE THAN 3 TIMES';RUN; %MEND HOTDECK; /************************************************************************/ /* MORE USER INPUT */ /* */ /* INSERT APPROPRIATE VALUES FOR ITEMS 1 THROUGH 6 BELOW FOR EACH SET */ /* OF VARIABLES TO BE IMPUTED. COPY AND INSERT VALUES AS APPROPRIATE */ /* FOR EACH ADDITIONAL VARIABLE OR SET OF VARIABLES TO BE IMPUTED. */ /* */ /* FOR ITEMS 1 THRU 3, INSERT ONE VARIABLE FOLLOWED BY A COMMA, OR A */ /* LIST OF VARIABLES SEPARATED BY A SPACE, WITH A COMMA AFTER THE */ /* LAST VARIABLE. */ /* FOR ITEM 4 PLACE 'PREV', 'NEXT' OR 'BOTH', FOLLOWED BY A COMMA, */ /* TO CHOOSE THE METHOD TO SELECT THE DONOR.

*/ /* FOR ITEM 5 INSERT A NUMERIC VALUE OR A LIST OF NUMERIC VALUES */ /* (SEPARATED BY A SPACE) AND FOLLOWED BY A COMMA. A PERIOD CAN BE */ /* FOR THE SAS MISSING VALUE. */ /* FOR ITEM 6, INSERT 1 TO IMPUTE ALL VARIABLES IN THE ITEM 1 LIST */ /* WHETHER ALL ARE MISSING OR NOT. DO NOT FOLLOW ITEM 6 WITH A COMMA! */ /************************************************************************/ %HOTDECK( /*1)place variable(s) to be imputed here===>*/ gender , /*2)CLASSING or POST-STRATA variable(s)====>*/ region, /*3)place sorting variable(s) here=========>*/ age_i q15_i, /*4)select method: PREV, NEXT, or BOTH=====>*/ prev, /*5)list value(s) to treat as missing here=>*/ 99 , /*6)put 1 to impute all, 0 just the missing>*/ 0 ); ** Prepare imputed variables;

DATA IMPUTE.national_i2; SET IMPUTE.national_i;

** Rename imputed variables;

gender_i=gender; ** Keep only unique ID variable and the imputed variable;

KEEP id gender_i income age_i q15_i region;

RUN; Page 5** Check imputed variables; PROC FREQ data=IMPUTE.national_i2; TABLES gender_i; RUN; PROC CONTENTS data=IMPUTE.national_i2 varnum; RUN;

/******************************************************************************** *************/ /******************************************************************************** *************/ /* IMPUTATION 2. income

*/ /******************************************************************************** *************/ /******************************************************************************** *************/

/*********************************************************************/ /* USER INPUT */ /*********************************************************************/ /*plug in folder with your input file===>*/ LIBNAME LIB ''; /*input filename========================>*/ %LET INFILE = national_i2; /*output filename=======================>*/ %LET OUTFILE = national_i3; /*variable uniquely identifies records==>*/ %LET ID = id; /*IF YOU HAVE A VARIABLE IDENTIFYING RESPONDENTS (CODED 1) AND NON-*/ /*RESPONDENTS (CODED 0) REPLACE 'NONE' WITH THE VARIABLE NAME BELOW*/ /*optional response var (1=R 0=NR)======>*/ %LET RESPONSE = NONE; /*********************************************************************/ /* SKIP TO BOTTOM FOR MORE USER INPUT */ /*********************************************************************/ option ps=55 ls=180 formchar=' ' macrogen symbolgen mprint mlogic; /* MACROS to generate code if RESPONSE variable in use */ %MACRO RESPCDEA; %IF %UPCASE(&RESPONSE)=NONE %THEN %DO;MISSVAR ~in(&MVALUE) %END; %ELSE %DO;&RESPONSE=1 %END; %MEND RESPCDEA; %MACRO RESPCDEB; %IF %UPCASE(&RESPONSE)=NONE %THEN %DO;MISSVAR IN(&MVALUE) %END; %ELSE %DO;&RESPONSE=0 %END; %MEND RESPCDEB; data lib.&OUTFILE;set lib.&INFILE; %MACRO HOTDECK(MISSVAR,CLASS,SORTVAR,DIRECTN,MVALUE,IMPALL); /* find LSTCLASS from macro variable */ %LET LSTCLASS = &CLASS; %DO %WHILE (%INDEX(&LSTCLASS, %STR( ) ) GT 1); %LET POS = %INDEX(&LSTCLASS, %STR( ) ); %LET LSTCLASS = %SUBSTR(&LSTCLASS, &POS ); %END; /* establish initial values */ PROC SORT DATA=LIB.&OUTFILE; BY &CLASS &SORTVAR; DATA SOURCE2;SET LIB.&OUTFILE; BY &CLASS &SORTVAR; RESPIND=9;RETAIN RESPFLAG; IF FIRST.&LSTCLASS THEN RESPFLAG=0; ARRAY MISSVAR &MISSVAR;

DO OVER MISSVAR; IF %RESPCDEA & RESPFLAG=0 THEN DO; RESPIND=1;RESPFLAG=1;

END;
END;
RUN; PROC SORT DATA=SOURCE2; BY &CLASS RESPIND &SORTVAR; /* serpentine sorting */ PROC SURVEYSELECT DATA=SOURCE2 OUT=SOURCE3 METHOD=SYS RATE=1.0 NOPRINT; STRATA &CLASS; Page 6CONTROL &SORTVAR;

/* generate macro variable with imputation flags */ %LET COUNT = 1; %LET FLAGVAR = ; %DO %UNTIL (&PART = %STR( ) ); %LET PART = %SCAN(&MISSVAR, &COUNT ); %LET COUNT = %EVAL( &COUNT + 1); %IF &PART NE %STR( ) %THEN %LET FLAGVAR = %STR(&FLAGVAR &PART._IF); %END; %IF %UPCASE(&DIRECTN)=BOTH OR %UPCASE(&DIRECTN)=PREV %THEN %LET START=1; %ELSE %LET START=2; %IF %UPCASE(&DIRECTN)=BOTH OR %UPCASE(&DIRECTN)=NEXT %THEN %LET XEND=2; %ELSE %LET XEND=1; %LOCAL J; %DO J=&START %TO &XEND;

PROC SORT %IF (%UPCASE(&DIRECTN)=BOTH AND &J=2) %THEN %DO;

DATA=HOTDECK1 %END; %ELSE %DO; DATA=SOURCE3 (KEEP=&ID &CLASS &MISSVAR &SORTVAR %IF &RESPONSE NE NONE %THEN %DO; &RESPONSE %END; ) %END;

OUT=SORTFILE; BY &CLASS %IF &J=2 %THEN %DO; DESCENDING %END; &SORTVAR; /****************************************************************/ /* The following steps will allow you to find the first or last */ /* respondent within a sorted imputation class */ /****************************************************************/ DATA RESPIND&J (DROP=RESPFLAG XABORT);

SET SORTFILE END=EOF; BY &CLASS; RETAIN RESPFLAG XABORT 0; RESPIND&J=9; ARRAY MISSVAR &MISSVAR; CALL SYMPUT('NMISSV',LEFT(TRIM(DIM(MISSVAR)))); IF FIRST.&LSTCLASS THEN RESPFLAG=0; IF RESPFLAG EQ 0 THEN DO; RESPIND&J=1; DO OVER MISSVAR; IF %RESPCDEB THEN RESPIND&J=9; END;

END; IF RESPIND&J EQ 1 THEN RESPFLAG=1; IF LAST.&LSTCLASS AND RESPFLAG EQ 0 THEN DO; ERROR 'ERROR: AT LEAST ONE IMPUTATION CLASS WITH NO DONOR ' &LSTCLASS=; XABORT+1;

END; IF EOF & XABORT THEN ABORT;

RUN; PROC SORT DATA=RESPIND&J; BY &CLASS RESPIND&J %IF &J=2 %THEN %DO; DESCENDING %END; &SORTVAR; DATA HOTDECK&J (DROP=PRVR&J.R1-PRVR&J.R&NMISSV DONORID); SET RESPIND&J; BY &CLASS; RETAIN PRVR&J.R1-PRVR&J.R&NMISSV DONORID; ARRAY MISSVAR &MISSVAR; ARRAY IMPVAR&J IMPV&J.R1-IMPV&J.R&NMISSV; ARRAY PREVAR&J PRVR&J.R1-PRVR&J.R&NMISSV; IF FIRST.&LSTCLASS THEN DO; DO OVER PREVAR&J; PREVAR&J=.; END;

DONORID=.;
END; NONMISS=1;

DO OVER MISSVAR; IF %RESPCDEB THEN NONMISS=0;

END; IF NONMISS EQ 1 THEN DO;

DO OVER MISSVAR; Page 7IMPVAR&J=MISSVAR; PREVAR&J=MISSVAR;

END; DONORID=&ID; IRFLAG=0; DONOR&J=.;

END; ELSE IF NONMISS EQ 0 THEN DO;

DO OVER MISSVAR; IMPVAR&J=PREVAR&J;

END; IRFLAG=1; DONOR&J=DONORID;

END;
RUN; %END; /********************************************/ /* The next section randomly selects either */ /* the previous or next value imputed. */ /********************************************/ %IF %UPCASE(&DIRECTN)=BOTH %THEN %DO; DATA IMPUTED; SET HOTDECK2; ARRAY IMPVAR1R IMPV1R1-IMPV1R&NMISSV; ARRAY IMPVAR2R IMPV2R1-IMPV2R&NMISSV; ARRAY DONOR DONOR1-DONOR&NMISSV; ARRAY MISSVAR &MISSVAR; RAN=MOD(INT(RANUNI(0)*10),2)+1; IF RAN EQ 1 THEN DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR1R; DONOR=DONOR1;

END;
END; CHOSE='PREV';

END; ELSE IF RAN EQ 2 THEN DO;

DO OVER MISSVAR; MISSVAR=IMPVAR2R; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR2R; DONOR=DONOR2;

END;
END; CHOSE='NEXT';

END;
RUN; %END; /********************************************************/ /* This section is for when only one direction is used. */ /********************************************************/ %ELSE %IF %UPCASE(&DIRECTN)=PREV OR %UPCASE(&DIRECTN)=NEXT %THEN %DO; DATA IMPUTED; SET HOTDECK&XEND; ARRAY IMPVAR1 IMPV1R1-IMPV1R&NMISSV; ARRAY IMPVAR2 IMPV2R1-IMPV2R&NMISSV; ARRAY DONOR DONOR1-DONOR&NMISSV; ARRAY MISSVAR &MISSVAR; %IF %UPCASE(&DIRECTN)=PREV %THEN %DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR1; DONOR=DONOR1;

END;
END; CHOSE='PREV'; %END; %ELSE %IF %UPCASE(&DIRECTN)=NEXT %THEN %DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; Page 8MISSVAR=IMPVAR2;

DONOR=DONOR2;

END;
END; CHOSE='NEXT'; %END;

RUN; /* generate imputation flags and eliminate working variables */ DATA IMPUTED(KEEP=&ID CHOSE &MISSVAR &FLAGVAR);

SET IMPUTED; ARRAY FLAGVAR &FLAGVAR; ARRAY DONOR DONOR1-DONOR&NMISSV;

DO OVER FLAGVAR;
FLAGVAR = DONOR;
END;
RUN; /* merge results back to original file */ PROC SORT DATA=LIB.&OUTFILE OUT=SOURCE1;BY &ID; PROC SORT DATA=IMPUTED;BY &ID; DATA LIB.&OUTFILE; MERGE SOURCE1(DROP=&MISSVAR) IMPUTED;BY &ID;

RUN; PROC FREQ;TABLES &MISSVAR &FLAGVAR / MISSING; %END; /******************************************************************/ /* The next section identifies donors used more than three times. */ /******************************************************************/ PROC FREQ DATA=LIB.&OUTFILE; %LET DONOR = %SCAN(&FLAGVAR, 1 ); WHERE ( %SCAN(&FLAGVAR, 1 ) > .); TABLES &DONOR/NOPRINT OUT=CHECKCNT;

PROC FREQ DATA=CHECKCNT;
TABLES COUNT; TITLE 'NUMBER OF TIMES DONORS WERE USED';RUN;

DATA CHECKCNT;SET CHECKCNT; DONOR = %SCAN(&FLAGVAR, 1 ); DATA TEMPFILE; SET LIB.&OUTFILE (KEEP=&ID &CLASS &SORTVAR &MISSVAR); DONOR=&ID;

PROC SORT DATA=TEMPFILE; BY DONOR; DATA MORTHAN3; MERGE CHECKCNT (IN=A) TEMPFILE; BY DONOR; IF A; IF DONOR NE . AND COUNT > 3; PROC PRINT DATA=MORTHAN3; VAR DONOR COUNT &MISSVAR &CLASS &SORTVAR; TITLE 'DONORS USED MORE THAN 3 TIMES';RUN; %MEND HOTDECK; /************************************************************************/ /* MORE USER INPUT */ /* */ /* INSERT APPROPRIATE VALUES FOR ITEMS 1 THROUGH 6 BELOW FOR EACH SET */ /* OF VARIABLES TO BE IMPUTED. COPY AND INSERT VALUES AS APPROPRIATE */ /* FOR EACH ADDITIONAL VARIABLE OR SET OF VARIABLES TO BE IMPUTED. */ /* */ /* FOR ITEMS 1 THRU 3, INSERT ONE VARIABLE FOLLOWED BY A COMMA, OR A */ /* LIST OF VARIABLES SEPARATED BY A SPACE, WITH A COMMA AFTER THE */ /* LAST VARIABLE. */ /* FOR ITEM 4 PLACE 'PREV', 'NEXT' OR 'BOTH', FOLLOWED BY A COMMA, */ /* TO CHOOSE THE METHOD TO SELECT THE DONOR.

*/ /* FOR ITEM 5 INSERT A NUMERIC VALUE OR A LIST OF NUMERIC VALUES */ /* (SEPARATED BY A SPACE) AND FOLLOWED BY A COMMA. A PERIOD CAN BE */ Page 9/* FOR THE SAS MISSING VALUE. */ /* FOR ITEM 6, INSERT 1 TO IMPUTE ALL VARIABLES IN THE ITEM 1 LIST */ /* WHETHER ALL ARE MISSING OR NOT. DO NOT FOLLOW ITEM 6 WITH A COMMA! */ /************************************************************************/ %HOTDECK( /*1)place variable(s) to be imputed here===>*/ income, /*2)CLASSING or POST-STRATA variable(s)====>*/ region , /*3)place sorting variable(s) here=========>*/ gender_i age_i q15_i, /*4)select method: PREV, NEXT, or BOTH=====>*/ prev, /*5)list value(s) to treat as missing here=>*/ 99 , /*6)put 1 to impute all, 0 just the missing>*/ 0 ); ** Prepare imputed variables;

DATA IMPUTE.national_i4; SET IMPUTE.national_i3;

** Rename imputed variables;

income_i=income; ** Keep only CASEID and the imputed variables;

KEEP id gender_i age_i income_i;

RUN; ** Check imputed variables;

PROC FREQ data=IMPUTE.national_i4; TABLES income_i; RUN; PROC CONTENTS data=IMPUTE.national_i4; RUN;

DATA IMPUTE.national_imputed; SET IMPUTE.national_i4; KEEP ID income_i gender_i;

RUN;

/*******************************************************************************/ /*** INLAND SAMPLE ***/ /*******************************************************************************/

/************************************************************/ /* Hot_Deck_Imputation.sas */ /* This is a SAS hot deck imputation program adapted from */ /* "Unweighted Sequential Hot Deck Imputation" developed by */ /* Barbara Lepidus Carlson and Linda S. Bandeh, and */ /* documented in "SAS Macros Useful in Imputing Missing */ /* Survey Data", Carlson, Cox & Bandeh, SUGI 20, April 1995 */ /* by Bruce Ellis, April 2007, Battelle Memorial Institute, */ /* to 1) consolidate Initial Value Routine, Serpentine */ /* sorting and imputation into a single */ /* stand-alone program,

*/ /* 2) support iterative execution for a series of */ /* imputations,

*/ /* 3) automatically save imputation flag variables, */ /* 4) support multiple missing values or a variable */ /* identifying cases to be imputed, */ /* 5) automatically generate some macro variables and */ /* simplify user input,

*/ /* 6) iteratively merge imputed variables and */ /* imputation flags to a single output file. */ /* 7) handles series of variables correctly. */ /* */ /************************************************************/

/******************************************************************************** *************/ /******************************************************************************** Page 10*************/

/* IMPUTATION 1. AGE GENDER */ /******************************************************************************** *************/ /******************************************************************************** *************/

/*********************************************************************/ /* USER INPUT */ /*********************************************************************/ /*plug in folder with your input file===>*/ LIBNAME LIB ''; /*input filename========================>*/ %LET INFILE = INLAND; /*output filename=======================>*/ %LET OUTFILE = data4_i; /*variable uniquely identifies records==>*/ %LET ID = ID; /*IF YOU HAVE A VARIABLE IDENTIFYING RESPONDENTS (CODED 1) AND NON-*/ /*RESPONDENTS (CODED 0) REPLACE 'NONE' WITH THE VARIABLE NAME BELOW*/ /*optional response var (1=R 0=NR)======>*/ %LET RESPONSE = NONE; /*********************************************************************/ /* SKIP TO BOTTOM FOR MORE USER INPUT */ /*********************************************************************/ option ps=55 ls=180 formchar=' ' macrogen symbolgen mprint mlogic; /* MACROS to generate code if RESPONSE variable in use */ %MACRO RESPCDEA; %IF %UPCASE(&RESPONSE)=NONE %THEN %DO;MISSVAR ~in(&MVALUE) %END; %ELSE %DO;&RESPONSE=1 %END; %MEND RESPCDEA; %MACRO RESPCDEB; %IF %UPCASE(&RESPONSE)=NONE %THEN %DO;MISSVAR IN(&MVALUE) %END; %ELSE %DO;&RESPONSE=0 %END; %MEND RESPCDEB; data lib.&OUTFILE;set lib.&INFILE; %MACRO HOTDECK(MISSVAR,CLASS,SORTVAR,DIRECTN,MVALUE,IMPALL); /* find LSTCLASS from macro variable */ %LET LSTCLASS = &CLASS; %DO %WHILE (%INDEX(&LSTCLASS, %STR( ) ) GT 1); %LET POS = %INDEX(&LSTCLASS, %STR( ) ); %LET LSTCLASS = %SUBSTR(&LSTCLASS, &POS ); %END; /* establish initial values */ PROC SORT DATA=LIB.&OUTFILE; BY &CLASS &SORTVAR; DATA SOURCE2;SET LIB.&OUTFILE; BY &CLASS &SORTVAR; RESPIND=9;RETAIN RESPFLAG; IF FIRST.&LSTCLASS THEN RESPFLAG=0; ARRAY MISSVAR &MISSVAR;

DO OVER MISSVAR; IF %RESPCDEA & RESPFLAG=0 THEN DO; RESPIND=1;RESPFLAG=1;

END;
END;
RUN; PROC SORT DATA=SOURCE2; BY &CLASS RESPIND &SORTVAR; /* serpentine sorting */ PROC SURVEYSELECT DATA=SOURCE2 OUT=SOURCE3 METHOD=SYS RATE=1.0 NOPRINT; STRATA &CLASS; CONTROL &SORTVAR; /* generate macro variable with imputation flags */ %LET COUNT = 1; %LET FLAGVAR = ; %DO %UNTIL (&PART = %STR( ) ); %LET PART = %SCAN(&MISSVAR, &COUNT ); %LET COUNT = %EVAL( &COUNT + 1); %IF &PART NE %STR( ) %THEN %LET FLAGVAR = %STR(&FLAGVAR &PART._IF); Page 11%END;

%IF %UPCASE(&DIRECTN)=BOTH OR %UPCASE(&DIRECTN)=PREV %THEN %LET START=1; %ELSE %LET START=2; %IF %UPCASE(&DIRECTN)=BOTH OR %UPCASE(&DIRECTN)=NEXT %THEN %LET XEND=2; %ELSE %LET XEND=1; %LOCAL J; %DO J=&START %TO &XEND;

PROC SORT %IF (%UPCASE(&DIRECTN)=BOTH AND &J=2) %THEN %DO;

DATA=HOTDECK1 %END; %ELSE %DO; DATA=SOURCE3 (KEEP=&ID &CLASS &MISSVAR &SORTVAR %IF &RESPONSE NE NONE %THEN %DO; &RESPONSE %END; ) %END;

OUT=SORTFILE; BY &CLASS %IF &J=2 %THEN %DO; DESCENDING %END; &SORTVAR; /****************************************************************/ /* The following steps will allow you to find the first or last */ /* respondent within a sorted imputation class */ /****************************************************************/ DATA RESPIND&J (DROP=RESPFLAG XABORT);

SET SORTFILE END=EOF; BY &CLASS; RETAIN RESPFLAG XABORT 0; RESPIND&J=9; ARRAY MISSVAR &MISSVAR; CALL SYMPUT('NMISSV',LEFT(TRIM(DIM(MISSVAR)))); IF FIRST.&LSTCLASS THEN RESPFLAG=0; IF RESPFLAG EQ 0 THEN DO; RESPIND&J=1; DO OVER MISSVAR; IF %RESPCDEB THEN RESPIND&J=9; END;

END; IF RESPIND&J EQ 1 THEN RESPFLAG=1; IF LAST.&LSTCLASS AND RESPFLAG EQ 0 THEN DO; ERROR 'ERROR: AT LEAST ONE IMPUTATION CLASS WITH NO DONOR ' &LSTCLASS=; XABORT+1;

END; IF EOF & XABORT THEN ABORT;

RUN; PROC SORT DATA=RESPIND&J; BY &CLASS RESPIND&J %IF &J=2 %THEN %DO; DESCENDING %END; &SORTVAR; DATA HOTDECK&J (DROP=PRVR&J.R1-PRVR&J.R&NMISSV DONORID); SET RESPIND&J; BY &CLASS; RETAIN PRVR&J.R1-PRVR&J.R&NMISSV DONORID; ARRAY MISSVAR &MISSVAR; ARRAY IMPVAR&J IMPV&J.R1-IMPV&J.R&NMISSV; ARRAY PREVAR&J PRVR&J.R1-PRVR&J.R&NMISSV; IF FIRST.&LSTCLASS THEN DO; DO OVER PREVAR&J; PREVAR&J=.; END;

DONORID=.;
END; NONMISS=1;

DO OVER MISSVAR; IF %RESPCDEB THEN NONMISS=0;

END; IF NONMISS EQ 1 THEN DO;

DO OVER MISSVAR; IMPVAR&J=MISSVAR; PREVAR&J=MISSVAR;

END; DONORID=&ID; IRFLAG=0; DONOR&J=.;

END; ELSE IF NONMISS EQ 0 THEN DO;

DO OVER MISSVAR; Page 12IMPVAR&J=PREVAR&J;

END; IRFLAG=1; DONOR&J=DONORID;

END;
RUN; %END; /********************************************/ /* The next section randomly selects either */ /* the previous or next value imputed. */ /********************************************/ %IF %UPCASE(&DIRECTN)=BOTH %THEN %DO; DATA IMPUTED; SET HOTDECK2; ARRAY IMPVAR1R IMPV1R1-IMPV1R&NMISSV; ARRAY IMPVAR2R IMPV2R1-IMPV2R&NMISSV; ARRAY DONOR DONOR1-DONOR&NMISSV; ARRAY MISSVAR &MISSVAR; RAN=MOD(INT(RANUNI(0)*10),2)+1; IF RAN EQ 1 THEN DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR1R; DONOR=DONOR1;

END;
END; CHOSE='PREV';

END; ELSE IF RAN EQ 2 THEN DO;

DO OVER MISSVAR; MISSVAR=IMPVAR2R; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR2R; DONOR=DONOR2;

END;
END; CHOSE='NEXT';

END;
RUN; %END; /********************************************************/ /* This section is for when only one direction is used. */ /********************************************************/ %ELSE %IF %UPCASE(&DIRECTN)=PREV OR %UPCASE(&DIRECTN)=NEXT %THEN %DO; DATA IMPUTED; SET HOTDECK&XEND; ARRAY IMPVAR1 IMPV1R1-IMPV1R&NMISSV; ARRAY IMPVAR2 IMPV2R1-IMPV2R&NMISSV; ARRAY DONOR DONOR1-DONOR&NMISSV; ARRAY MISSVAR &MISSVAR; %IF %UPCASE(&DIRECTN)=PREV %THEN %DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR1; DONOR=DONOR1;

END;
END; CHOSE='PREV'; %END; %ELSE %IF %UPCASE(&DIRECTN)=NEXT %THEN %DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR2; DONOR=DONOR2;

END;
END; CHOSE='NEXT'; %END;

RUN; /* generate imputation flags and eliminate working variables */ Page 13DATA IMPUTED(KEEP=&ID CHOSE &MISSVAR &FLAGVAR);

SET IMPUTED; ARRAY FLAGVAR &FLAGVAR; ARRAY DONOR DONOR1-DONOR&NMISSV;

DO OVER FLAGVAR;
FLAGVAR = DONOR;
END;
RUN; /* merge results back to original file */ PROC SORT DATA=LIB.&OUTFILE OUT=SOURCE1;BY &ID; PROC SORT DATA=IMPUTED;BY &ID; DATA LIB.&OUTFILE; MERGE SOURCE1(DROP=&MISSVAR) IMPUTED;BY &ID;

RUN; PROC FREQ;TABLES &MISSVAR &FLAGVAR / MISSING; %END; /******************************************************************/ /* The next section identifies donors used more than three times. */ /******************************************************************/ PROC FREQ DATA=LIB.&OUTFILE; %LET DONOR = %SCAN(&FLAGVAR, 1 ); WHERE ( %SCAN(&FLAGVAR, 1 ) > .); TABLES &DONOR/NOPRINT OUT=CHECKCNT;

PROC FREQ DATA=CHECKCNT;
TABLES COUNT; TITLE 'NUMBER OF TIMES DONORS WERE USED';RUN;

DATA CHECKCNT;SET CHECKCNT; DONOR = %SCAN(&FLAGVAR, 1 ); DATA TEMPFILE; SET LIB.&OUTFILE (KEEP=&ID &CLASS &SORTVAR &MISSVAR); DONOR=&ID;

PROC SORT DATA=TEMPFILE; BY DONOR; DATA MORTHAN3; MERGE CHECKCNT (IN=A) TEMPFILE; BY DONOR; IF A; IF DONOR NE . AND COUNT > 3; PROC PRINT DATA=MORTHAN3; VAR DONOR COUNT &MISSVAR &CLASS &SORTVAR; TITLE 'DONORS USED MORE THAN 3 TIMES';RUN; %MEND HOTDECK; /************************************************************************/ /* MORE USER INPUT */ /* */ /* INSERT APPROPRIATE VALUES FOR ITEMS 1 THROUGH 6 BELOW FOR EACH SET */ /* OF VARIABLES TO BE IMPUTED. COPY AND INSERT VALUES AS APPROPRIATE */ /* FOR EACH ADDITIONAL VARIABLE OR SET OF VARIABLES TO BE IMPUTED. */ /* */ /* FOR ITEMS 1 THRU 3, INSERT ONE VARIABLE FOLLOWED BY A COMMA, OR A */ /* LIST OF VARIABLES SEPARATED BY A SPACE, WITH A COMMA AFTER THE */ /* LAST VARIABLE. */ /* FOR ITEM 4 PLACE 'PREV', 'NEXT' OR 'BOTH', FOLLOWED BY A COMMA, */ /* TO CHOOSE THE METHOD TO SELECT THE DONOR.

*/ /* FOR ITEM 5 INSERT A NUMERIC VALUE OR A LIST OF NUMERIC VALUES */ /* (SEPARATED BY A SPACE) AND FOLLOWED BY A COMMA. A PERIOD CAN BE */ /* FOR THE SAS MISSING VALUE. */ /* FOR ITEM 6, INSERT 1 TO IMPUTE ALL VARIABLES IN THE ITEM 1 LIST */ /* WHETHER ALL ARE MISSING OR NOT. DO NOT FOLLOW ITEM 6 WITH A COMMA! */ /************************************************************************/ %HOTDECK( /*1)place variable(s) to be imputed here===>*/ age gender , Page 14/*2)CLASSING or POST-STRATA variable(s)====>*/ region,

/*3)place sorting variable(s) here=========>*/ q15, /*4)select method: PREV, NEXT, or BOTH=====>*/ prev, /*5)list value(s) to treat as missing here=>*/ 99 , /*6)put 1 to impute all, 0 just the missing>*/ 0 ); ** Prepare imputed variables;

DATA impute.data4_i2; SET impute.data4_i;

** Rename imputed variables;

age_i = age; gender_i=gender; ** Keep only unique ID variable and the imputed variable;

KEEP id age_i gender_i income white educ income region;

RUN;

** Check imputed variables;

PROC FREQ data=impute.data4_i2; TABLES age_i gender_i; RUN; PROC CONTENTS data=impute.data4_i2 varnum; RUN;

/******************************************************************************** *************/ /******************************************************************************** *************/ /* IMPUTATION 2. educ race */ /******************************************************************************** *************/ /******************************************************************************** *************/

/*********************************************************************/ /* USER INPUT */ /*********************************************************************/ /*plug in folder with your input file===>*/ LIBNAME LIB ''; /*input filename========================>*/ %LET INFILE = data4_i2; /*output filename=======================>*/ %LET OUTFILE = data4_i3; /*variable uniquely identifies records==>*/ %LET ID = id; /*IF YOU HAVE A VARIABLE IDENTIFYING RESPONDENTS (CODED 1) AND NON-*/ /*RESPONDENTS (CODED 0) REPLACE 'NONE' WITH THE VARIABLE NAME BELOW*/ /*optional response var (1=R 0=NR)======>*/ %LET RESPONSE = NONE; /*********************************************************************/ /* SKIP TO BOTTOM FOR MORE USER INPUT */ /*********************************************************************/ option ps=55 ls=180 formchar=' ' macrogen symbolgen mprint mlogic; /* MACROS to generate code if RESPONSE variable in use */ %MACRO RESPCDEA; %IF %UPCASE(&RESPONSE)=NONE %THEN %DO;MISSVAR ~in(&MVALUE) %END; %ELSE %DO;&RESPONSE=1 %END; %MEND RESPCDEA; %MACRO RESPCDEB; %IF %UPCASE(&RESPONSE)=NONE %THEN %DO;MISSVAR IN(&MVALUE) %END; %ELSE %DO;&RESPONSE=0 %END; %MEND RESPCDEB; data lib.&OUTFILE;set lib.&INFILE; %MACRO HOTDECK(MISSVAR,CLASS,SORTVAR,DIRECTN,MVALUE,IMPALL); /* find LSTCLASS from macro variable */ %LET LSTCLASS = &CLASS; %DO %WHILE (%INDEX(&LSTCLASS, %STR( ) ) GT 1); %LET POS = %INDEX(&LSTCLASS, %STR( ) ); %LET LSTCLASS = %SUBSTR(&LSTCLASS, &POS ); %END; /* establish initial values */ PROC SORT DATA=LIB.&OUTFILE; BY &CLASS &SORTVAR; DATA SOURCE2;SET LIB.&OUTFILE; BY &CLASS &SORTVAR; Page 15RESPIND=9;RETAIN RESPFLAG;

IF FIRST.&LSTCLASS THEN RESPFLAG=0; ARRAY MISSVAR &MISSVAR;

DO OVER MISSVAR; IF %RESPCDEA & RESPFLAG=0 THEN DO; RESPIND=1;RESPFLAG=1;

END;
END;
RUN; PROC SORT DATA=SOURCE2; BY &CLASS RESPIND &SORTVAR; /* serpentine sorting */ PROC SURVEYSELECT DATA=SOURCE2 OUT=SOURCE3 METHOD=SYS RATE=1.0 NOPRINT; STRATA &CLASS; CONTROL &SORTVAR; /* generate macro variable with imputation flags */ %LET COUNT = 1; %LET FLAGVAR = ; %DO %UNTIL (&PART = %STR( ) ); %LET PART = %SCAN(&MISSVAR, &COUNT ); %LET COUNT = %EVAL( &COUNT + 1); %IF &PART NE %STR( ) %THEN %LET FLAGVAR = %STR(&FLAGVAR &PART._IF); %END; %IF %UPCASE(&DIRECTN)=BOTH OR %UPCASE(&DIRECTN)=PREV %THEN %LET START=1; %ELSE %LET START=2; %IF %UPCASE(&DIRECTN)=BOTH OR %UPCASE(&DIRECTN)=NEXT %THEN %LET XEND=2; %ELSE %LET XEND=1; %LOCAL J; %DO J=&START %TO &XEND;

PROC SORT %IF (%UPCASE(&DIRECTN)=BOTH AND &J=2) %THEN %DO;

DATA=HOTDECK1 %END; %ELSE %DO; DATA=SOURCE3 (KEEP=&ID &CLASS &MISSVAR &SORTVAR %IF &RESPONSE NE NONE %THEN %DO; &RESPONSE %END; ) %END;

OUT=SORTFILE; BY &CLASS %IF &J=2 %THEN %DO; DESCENDING %END; &SORTVAR; /****************************************************************/ /* The following steps will allow you to find the first or last */ /* respondent within a sorted imputation class */ /****************************************************************/ DATA RESPIND&J (DROP=RESPFLAG XABORT);

SET SORTFILE END=EOF; BY &CLASS; RETAIN RESPFLAG XABORT 0; RESPIND&J=9; ARRAY MISSVAR &MISSVAR; CALL SYMPUT('NMISSV',LEFT(TRIM(DIM(MISSVAR)))); IF FIRST.&LSTCLASS THEN RESPFLAG=0; IF RESPFLAG EQ 0 THEN DO; RESPIND&J=1; DO OVER MISSVAR; IF %RESPCDEB THEN RESPIND&J=9; END;

END; IF RESPIND&J EQ 1 THEN RESPFLAG=1; IF LAST.&LSTCLASS AND RESPFLAG EQ 0 THEN DO; ERROR 'ERROR: AT LEAST ONE IMPUTATION CLASS WITH NO DONOR ' &LSTCLASS=; XABORT+1;

END; IF EOF & XABORT THEN ABORT;

RUN; PROC SORT DATA=RESPIND&J; BY &CLASS RESPIND&J %IF &J=2 %THEN %DO; DESCENDING %END; &SORTVAR; DATA HOTDECK&J (DROP=PRVR&J.R1-PRVR&J.R&NMISSV DONORID); SET RESPIND&J; BY &CLASS; Page 16RETAIN PRVR&J.R1-PRVR&J.R&NMISSV DONORID;

ARRAY MISSVAR &MISSVAR; ARRAY IMPVAR&J IMPV&J.R1-IMPV&J.R&NMISSV; ARRAY PREVAR&J PRVR&J.R1-PRVR&J.R&NMISSV; IF FIRST.&LSTCLASS THEN DO; DO OVER PREVAR&J; PREVAR&J=.; END;

DONORID=.;
END; NONMISS=1;

DO OVER MISSVAR; IF %RESPCDEB THEN NONMISS=0;

END; IF NONMISS EQ 1 THEN DO;

DO OVER MISSVAR; IMPVAR&J=MISSVAR; PREVAR&J=MISSVAR;

END; DONORID=&ID; IRFLAG=0; DONOR&J=.;

END; ELSE IF NONMISS EQ 0 THEN DO;

DO OVER MISSVAR; IMPVAR&J=PREVAR&J;

END; IRFLAG=1; DONOR&J=DONORID;

END;
RUN; %END; /********************************************/ /* The next section randomly selects either */ /* the previous or next value imputed. */ /********************************************/ %IF %UPCASE(&DIRECTN)=BOTH %THEN %DO; DATA IMPUTED; SET HOTDECK2; ARRAY IMPVAR1R IMPV1R1-IMPV1R&NMISSV; ARRAY IMPVAR2R IMPV2R1-IMPV2R&NMISSV; ARRAY DONOR DONOR1-DONOR&NMISSV; ARRAY MISSVAR &MISSVAR; RAN=MOD(INT(RANUNI(0)*10),2)+1; IF RAN EQ 1 THEN DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR1R; DONOR=DONOR1;

END;
END; CHOSE='PREV';

END; ELSE IF RAN EQ 2 THEN DO;

DO OVER MISSVAR; MISSVAR=IMPVAR2R; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR2R; DONOR=DONOR2;

END;
END; CHOSE='NEXT';

END;
RUN; %END; /********************************************************/ /* This section is for when only one direction is used. */ /********************************************************/ %ELSE %IF %UPCASE(&DIRECTN)=PREV OR %UPCASE(&DIRECTN)=NEXT %THEN %DO; DATA IMPUTED; SET HOTDECK&XEND; ARRAY IMPVAR1 IMPV1R1-IMPV1R&NMISSV; ARRAY IMPVAR2 IMPV2R1-IMPV2R&NMISSV;Page 17ARRAY DONOR DONOR1-DONOR&NMISSV;

ARRAY MISSVAR &MISSVAR; %IF %UPCASE(&DIRECTN)=PREV %THEN %DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR1; DONOR=DONOR1;

END;
END; CHOSE='PREV'; %END; %ELSE %IF %UPCASE(&DIRECTN)=NEXT %THEN %DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR2; DONOR=DONOR2;

END;
END; CHOSE='NEXT'; %END;

RUN; /* generate imputation flags and eliminate working variables */ DATA IMPUTED(KEEP=&ID CHOSE &MISSVAR &FLAGVAR);

SET IMPUTED; ARRAY FLAGVAR &FLAGVAR; ARRAY DONOR DONOR1-DONOR&NMISSV;

DO OVER FLAGVAR;
FLAGVAR = DONOR;
END;
RUN; /* merge results back to original file */ PROC SORT DATA=LIB.&OUTFILE OUT=SOURCE1;BY &ID; PROC SORT DATA=IMPUTED;BY &ID; DATA LIB.&OUTFILE; MERGE SOURCE1(DROP=&MISSVAR) IMPUTED;BY &ID;

RUN; PROC FREQ;TABLES &MISSVAR &FLAGVAR / MISSING; %END; /******************************************************************/ /* The next section identifies donors used more than three times. */ /******************************************************************/ PROC FREQ DATA=LIB.&OUTFILE; %LET DONOR = %SCAN(&FLAGVAR, 1 ); WHERE ( %SCAN(&FLAGVAR, 1 ) > .); TABLES &DONOR/NOPRINT OUT=CHECKCNT;

PROC FREQ DATA=CHECKCNT;
TABLES COUNT; TITLE 'NUMBER OF TIMES DONORS WERE USED';RUN;

DATA CHECKCNT;SET CHECKCNT; DONOR = %SCAN(&FLAGVAR, 1 ); DATA TEMPFILE; SET LIB.&OUTFILE (KEEP=&ID &CLASS &SORTVAR &MISSVAR); DONOR=&ID;

PROC SORT DATA=TEMPFILE; BY DONOR; DATA MORTHAN3; MERGE CHECKCNT (IN=A) TEMPFILE; BY DONOR; IF A; IF DONOR NE . AND COUNT > 3; PROC PRINT DATA=MORTHAN3; VAR DONOR COUNT &MISSVAR &CLASS &SORTVAR; TITLE 'DONORS USED MORE THAN 3 TIMES';RUN; %MEND HOTDECK; /************************************************************************/ /* MORE USER INPUT */ /* */ /* INSERT APPROPRIATE VALUES FOR ITEMS 1 THROUGH 6 BELOW FOR EACH SET */ /* OF VARIABLES TO BE IMPUTED. COPY AND INSERT VALUES AS APPROPRIATE */ /* FOR EACH ADDITIONAL VARIABLE OR SET OF VARIABLES TO BE IMPUTED. */ /* Page 18*/

/* FOR ITEMS 1 THRU 3, INSERT ONE VARIABLE FOLLOWED BY A COMMA, OR A */ /* LIST OF VARIABLES SEPARATED BY A SPACE, WITH A COMMA AFTER THE */ /* LAST VARIABLE. */ /* FOR ITEM 4 PLACE 'PREV', 'NEXT' OR 'BOTH', FOLLOWED BY A COMMA, */ /* TO CHOOSE THE METHOD TO SELECT THE DONOR.

*/ /* FOR ITEM 5 INSERT A NUMERIC VALUE OR A LIST OF NUMERIC VALUES */ /* (SEPARATED BY A SPACE) AND FOLLOWED BY A COMMA. A PERIOD CAN BE */ /* FOR THE SAS MISSING VALUE. */ /* FOR ITEM 6, INSERT 1 TO IMPUTE ALL VARIABLES IN THE ITEM 1 LIST */ /* WHETHER ALL ARE MISSING OR NOT. DO NOT FOLLOW ITEM 6 WITH A COMMA! */ /************************************************************************/ %HOTDECK( /*1)place variable(s) to be imputed here===>*/ educ white , /*2)CLASSING or POST-STRATA variable(s)====>*/ region , /*3)place sorting variable(s) here=========>*/ gender_i age_i, /*4)select method: PREV, NEXT, or BOTH=====>*/ prev, /*5)list value(s) to treat as missing here=>*/ 99 , /*6)put 1 to impute all, 0 just the missing>*/ 0 ); ** Prepare imputed variables;

DATA impute.data4_i4; SET impute.data4_i3;

** Rename imputed variables;

educ_i =educ; white_i=white; ** Keep only CASEID and the imputed variables;

KEEP id region gender_i age_i educ_i white_i income;

RUN; ** Check imputed variables;

PROC FREQ data=impute.data4_i4; TABLES educ_i white_i; RUN; PROC CONTENTS data=impute.data4_i4; RUN;

/******************************************************************************** *************/ /******************************************************************************** *************/ /* IMPUTATION 3. income

*/ /******************************************************************************** *************/ /******************************************************************************** *************/

/*********************************************************************/ /* USER INPUT */ /*********************************************************************/ /*plug in folder with your input file===>*/ LIBNAME LIB ''; /*input filename========================>*/ %LET INFILE = data4_i4; /*output filename=======================>*/ %LET OUTFILE = data4_i5; /*variable uniquely identifies records==>*/ %LET ID = id; /*IF YOU HAVE A VARIABLE IDENTIFYING RESPONDENTS (CODED 1) AND NON-*/ /*RESPONDENTS (CODED 0) REPLACE 'NONE' WITH THE VARIABLE NAME BELOW*/ /*optional response var (1=R 0=NR)======>*/ %LET RESPONSE = NONE; /*********************************************************************/ /* SKIP TO BOTTOM FOR MORE USER INPUT */ /*********************************************************************/ option ps=55 ls=180 formchar=' ' macrogen symbolgen mprint mlogic; /* MACROS to generate code if RESPONSE variable in use */ Page 19%MACRO RESPCDEA;

%IF %UPCASE(&RESPONSE)=NONE %THEN %DO;MISSVAR ~in(&MVALUE) %END; %ELSE %DO;&RESPONSE=1 %END; %MEND RESPCDEA; %MACRO RESPCDEB; %IF %UPCASE(&RESPONSE)=NONE %THEN %DO;MISSVAR IN(&MVALUE) %END; %ELSE %DO;&RESPONSE=0 %END; %MEND RESPCDEB; data lib.&OUTFILE;set lib.&INFILE; %MACRO HOTDECK(MISSVAR,CLASS,SORTVAR,DIRECTN,MVALUE,IMPALL); /* find LSTCLASS from macro variable */ %LET LSTCLASS = &CLASS; %DO %WHILE (%INDEX(&LSTCLASS, %STR( ) ) GT 1); %LET POS = %INDEX(&LSTCLASS, %STR( ) ); %LET LSTCLASS = %SUBSTR(&LSTCLASS, &POS ); %END; /* establish initial values */ PROC SORT DATA=LIB.&OUTFILE; BY &CLASS &SORTVAR; DATA SOURCE2;SET LIB.&OUTFILE; BY &CLASS &SORTVAR; RESPIND=9;RETAIN RESPFLAG; IF FIRST.&LSTCLASS THEN RESPFLAG=0; ARRAY MISSVAR &MISSVAR;

DO OVER MISSVAR; IF %RESPCDEA & RESPFLAG=0 THEN DO; RESPIND=1;RESPFLAG=1;

END;
END;
RUN; PROC SORT DATA=SOURCE2; BY &CLASS RESPIND &SORTVAR; /* serpentine sorting */ PROC SURVEYSELECT DATA=SOURCE2 OUT=SOURCE3 METHOD=SYS RATE=1.0 NOPRINT; STRATA &CLASS; CONTROL &SORTVAR; /* generate macro variable with imputation flags */ %LET COUNT = 1; %LET FLAGVAR = ; %DO %UNTIL (&PART = %STR( ) ); %LET PART = %SCAN(&MISSVAR, &COUNT ); %LET COUNT = %EVAL( &COUNT + 1); %IF &PART NE %STR( ) %THEN %LET FLAGVAR = %STR(&FLAGVAR &PART._IF); %END; %IF %UPCASE(&DIRECTN)=BOTH OR %UPCASE(&DIRECTN)=PREV %THEN %LET START=1; %ELSE %LET START=2; %IF %UPCASE(&DIRECTN)=BOTH OR %UPCASE(&DIRECTN)=NEXT %THEN %LET XEND=2; %ELSE %LET XEND=1; %LOCAL J; %DO J=&START %TO &XEND;

PROC SORT %IF (%UPCASE(&DIRECTN)=BOTH AND &J=2) %THEN %DO;

DATA=HOTDECK1 %END; %ELSE %DO; DATA=SOURCE3 (KEEP=&ID &CLASS &MISSVAR &SORTVAR %IF &RESPONSE NE NONE %THEN %DO; &RESPONSE %END; ) %END;

OUT=SORTFILE; BY &CLASS %IF &J=2 %THEN %DO; DESCENDING %END; &SORTVAR; /****************************************************************/ /* The following steps will allow you to find the first or last */ /* respondent within a sorted imputation class */ /****************************************************************/ DATA RESPIND&J (DROP=RESPFLAG XABORT); Page 20SET SORTFILE END=EOF;

BY &CLASS; RETAIN RESPFLAG XABORT 0; RESPIND&J=9; ARRAY MISSVAR &MISSVAR; CALL SYMPUT('NMISSV',LEFT(TRIM(DIM(MISSVAR)))); IF FIRST.&LSTCLASS THEN RESPFLAG=0; IF RESPFLAG EQ 0 THEN DO; RESPIND&J=1; DO OVER MISSVAR; IF %RESPCDEB THEN RESPIND&J=9; END;

END; IF RESPIND&J EQ 1 THEN RESPFLAG=1; IF LAST.&LSTCLASS AND RESPFLAG EQ 0 THEN DO; ERROR 'ERROR: AT LEAST ONE IMPUTATION CLASS WITH NO DONOR ' &LSTCLASS=; XABORT+1;

END; IF EOF & XABORT THEN ABORT;

RUN; PROC SORT DATA=RESPIND&J; BY &CLASS RESPIND&J %IF &J=2 %THEN %DO; DESCENDING %END; &SORTVAR; DATA HOTDECK&J (DROP=PRVR&J.R1-PRVR&J.R&NMISSV DONORID); SET RESPIND&J; BY &CLASS; RETAIN PRVR&J.R1-PRVR&J.R&NMISSV DONORID; ARRAY MISSVAR &MISSVAR; ARRAY IMPVAR&J IMPV&J.R1-IMPV&J.R&NMISSV; ARRAY PREVAR&J PRVR&J.R1-PRVR&J.R&NMISSV; IF FIRST.&LSTCLASS THEN DO; DO OVER PREVAR&J; PREVAR&J=.; END;

DONORID=.;
END; NONMISS=1;

DO OVER MISSVAR; IF %RESPCDEB THEN NONMISS=0;

END; IF NONMISS EQ 1 THEN DO;

DO OVER MISSVAR; IMPVAR&J=MISSVAR; PREVAR&J=MISSVAR;

END; DONORID=&ID; IRFLAG=0; DONOR&J=.;

END; ELSE IF NONMISS EQ 0 THEN DO;

DO OVER MISSVAR; IMPVAR&J=PREVAR&J;

END; IRFLAG=1; DONOR&J=DONORID;

END;
RUN; %END; /********************************************/ /* The next section randomly selects either */ /* the previous or next value imputed. */ /********************************************/ %IF %UPCASE(&DIRECTN)=BOTH %THEN %DO; DATA IMPUTED; SET HOTDECK2; ARRAY IMPVAR1R IMPV1R1-IMPV1R&NMISSV; ARRAY IMPVAR2R IMPV2R1-IMPV2R&NMISSV; ARRAY DONOR DONOR1-DONOR&NMISSV; ARRAY MISSVAR &MISSVAR; RAN=MOD(INT(RANUNI(0)*10),2)+1; IF RAN EQ 1 THEN DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR1R; DONOR=DONOR1;

END; Page 21END;

CHOSE='PREV';

END; ELSE IF RAN EQ 2 THEN DO;

DO OVER MISSVAR; MISSVAR=IMPVAR2R; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR2R; DONOR=DONOR2;

END;
END; CHOSE='NEXT';

END;
RUN; %END; /********************************************************/ /* This section is for when only one direction is used. */ /********************************************************/ %ELSE %IF %UPCASE(&DIRECTN)=PREV OR %UPCASE(&DIRECTN)=NEXT %THEN %DO; DATA IMPUTED; SET HOTDECK&XEND; ARRAY IMPVAR1 IMPV1R1-IMPV1R&NMISSV; ARRAY IMPVAR2 IMPV2R1-IMPV2R&NMISSV; ARRAY DONOR DONOR1-DONOR&NMISSV; ARRAY MISSVAR &MISSVAR; %IF %UPCASE(&DIRECTN)=PREV %THEN %DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR1; DONOR=DONOR1;

END;
END; CHOSE='PREV'; %END; %ELSE %IF %UPCASE(&DIRECTN)=NEXT %THEN %DO;

DO OVER MISSVAR; IF &IMPALL EQ 1 OR (&IMPALL=0 AND %RESPCDEB ) THEN DO; MISSVAR=IMPVAR2; DONOR=DONOR2;

END;
END; CHOSE='NEXT'; %END;

RUN; /* generate imputation flags and eliminate working variables */ DATA IMPUTED(KEEP=&ID CHOSE &MISSVAR &FLAGVAR);

SET IMPUTED; ARRAY FLAGVAR &FLAGVAR; ARRAY DONOR DONOR1-DONOR&NMISSV;

DO OVER FLAGVAR;
FLAGVAR = DONOR;
END;
RUN; /* merge results back to original file */ PROC SORT DATA=LIB.&OUTFILE OUT=SOURCE1;BY &ID; PROC SORT DATA=IMPUTED;BY &ID; DATA LIB.&OUTFILE; MERGE SOURCE1(DROP=&MISSVAR) IMPUTED;BY &ID;

RUN; PROC FREQ;TABLES &MISSVAR &FLAGVAR / MISSING; %END; /******************************************************************/ /* The next section identifies donors used more than three times. */ /******************************************************************/ PROC FREQ DATA=LIB.&OUTFILE; %LET DONOR = %SCAN(&FLAGVAR, 1 ); WHERE ( %SCAN(&FLAGVAR, 1 ) > .); TABLES &DONOR/NOPRINT OUT=CHECKCNT;

PROC FREQ DATA=CHECKCNT; Page 22TABLES COUNT;

TITLE 'NUMBER OF TIMES DONORS WERE USED';RUN;

DATA CHECKCNT;SET CHECKCNT; DONOR = %SCAN(&FLAGVAR, 1 ); DATA TEMPFILE; SET LIB.&OUTFILE (KEEP=&ID &CLASS &SORTVAR &MISSVAR); DONOR=&ID;

PROC SORT DATA=TEMPFILE; BY DONOR; DATA MORTHAN3; MERGE CHECKCNT (IN=A) TEMPFILE; BY DONOR; IF A; IF DONOR NE . AND COUNT > 3; PROC PRINT DATA=MORTHAN3; VAR DONOR COUNT &MISSVAR &CLASS &SORTVAR; TITLE 'DONORS USED MORE THAN 3 TIMES';RUN; %MEND HOTDECK; /************************************************************************/ /* MORE USER INPUT */ /* */ /* INSERT APPROPRIATE VALUES FOR ITEMS 1 THROUGH 6 BELOW FOR EACH SET */ /* OF VARIABLES TO BE IMPUTED. COPY AND INSERT VALUES AS APPROPRIATE */ /* FOR EACH ADDITIONAL VARIABLE OR SET OF VARIABLES TO BE IMPUTED. */ /* */ /* FOR ITEMS 1 THRU 3, INSERT ONE VARIABLE FOLLOWED BY A COMMA, OR A */ /* LIST OF VARIABLES SEPARATED BY A SPACE, WITH A COMMA AFTER THE */ /* LAST VARIABLE. */ /* FOR ITEM 4 PLACE 'PREV', 'NEXT' OR 'BOTH', FOLLOWED BY A COMMA, */ /* TO CHOOSE THE METHOD TO SELECT THE DONOR.

*/ /* FOR ITEM 5 INSERT A NUMERIC VALUE OR A LIST OF NUMERIC VALUES */ /* (SEPARATED BY A SPACE) AND FOLLOWED BY A COMMA. A PERIOD CAN BE */ /* FOR THE SAS MISSING VALUE. */ /* FOR ITEM 6, INSERT 1 TO IMPUTE ALL VARIABLES IN THE ITEM 1 LIST */ /* WHETHER ALL ARE MISSING OR NOT. DO NOT FOLLOW ITEM 6 WITH A COMMA! */ /************************************************************************/ %HOTDECK( /*1)place variable(s) to be imputed here===>*/ income , /*2)CLASSING or POST-STRATA variable(s)====>*/ region , /*3)place sorting variable(s) here=========>*/ gender_i age_i educ_i white_i , /*4)select method: PREV, NEXT, or BOTH=====>*/ prev, /*5)list value(s) to treat as missing here=>*/ 99 , /*6)put 1 to impute all, 0 just the missing>*/ 0 ); ** Prepare imputed variables;

DATA impute.data4_i6; SET impute.data4_i5;

** Rename imputed variables;

income_i=income; ** Keep only CASEID and the imputed variables;

KEEP id gender_i age_i educ_i white_i income_i;

RUN; ** Check imputed variables;

PROC FREQ data=impute.data4_i6; TABLES income_i; RUN; PROC CONTENTS data=impute.data4_i6; RUN;

data impute.inland_imputed; set impute.data4_i6; keep id gender_i age_i educ_i white_i income_i; run;

Page 23