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

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

*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 3
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 */

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 4
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===>*/ 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 6
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;

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 7
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;

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 8
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.

*/

/*

*/

/* 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 12
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;

MISSVAR=IMPVAR2;

DONOR=DONOR2;

END;

END;

CHOSE='NEXT';

%END;

RUN;

/* generate imputation flags and eliminate working variables */

Page 13
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

*/

/* 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 15
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);

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 16
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;

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 17
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;

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 20
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;

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 21
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;

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 22
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===>*/ 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