/*------------------------------------------------------------------- */ /* SAS Functions by Example */ /* by Ron Cody */ /* Copyright(c) 2004 by SAS Institute Inc., Cary, NC, USA */ /* SAS Publications order # 59343 */ /* ISBN 1-59047-378-7 */ /*-------------------------------------------------------------------*/ /* */ /* This material is provided "as is" by SAS Institute Inc. There */ /* are no warranties, expressed or implied, as to merchantability or */ /* fitness for a particular purpose regarding the materials or code */ /* contained herein. The Institute is not responsible for errors */ /* in this material as it now exists or will exist, nor does the */ /* Institute provide technical support for it. */ /* */ /*-------------------------------------------------------------------*/ /* Questions or problem reports concerning this material may be */ /* addressed to the author: */ /* */ /* SAS Institute Inc. */ /* Books by Users */ /* Attn: Ron Cody */ /* SAS Campus Drive */ /* Cary, NC 27513 */ /* */ /* */ /* If you prefer, you can send email to:
[email protected] */ /* Use this for subject field: */ /* Comments for Ron Cody */ /* */ /*-------------------------------------------------------------------*/
***Programs used in "Functions by Example." ; Program 1.1: How SAS determines storage lengths of character variables SUBSTRN(STRING,2,5) =" @25 RESULT / "SUBSTRN(STRING,-1,4) =" @25 SUB1 / "SUBSTRN(STRING,3,0) =" @25 SUB2 / "SUBSTRN(STRING,7,5) =" @25 SUB3 / "SUBSTRN(STRING,0,2) =" @25 SUB4; RUN; Program 1.26: Demonstrating the three concatenation call routines ***Primary functions: CALL CATS, CALL CATT, CALL CATX; ); LENGTH X3 4; INPUT @1 SUBJ $CHAR3. @4 DOB MMDDYY10. @14 (X1-X3) (1.) @17 F_NAME $CHAR10. @27 L_NAME $CHAR15.; LABEL SUBJ = 'Subject' DOB = 'Date of Birth' X1 = 'The first X' X2 = 'Second X' X3 = 'Third X' F_NAME = 'First Name' L_NAME = 'Last Name'; FORMAT DOB MMDDYY10. X3 ROMAN.; ANY =" @11 ANY / "NLOBS =" @11 NLOBS / "NVARS =" @11 NVARS / "LABEL =" @11 LABEL / "ENGINE =" @11 ENGINE / "CHARSET =" @11 CHARSET / "DSN =" @11 DSN / "RC =" @11 RC; RUN; Program 14.4: Determining the format, label, and length attributes of a variable using the VARFMT, VARLEN, and VARNUM functions ***Primary functions: OPEN, VARFMT, VARNUM, VARLABEL, VARLEN, CLOSE; DATA _NULL_; LENGTH FORMAT LABEL $ 32; DSID = OPEN("TEST"); ORDER = VARNUM(DSID,"X3"); FORMAT = VARFMT(DSID,ORDER); LABEL = VARLABEL(DSID,ORDER); LENGTH = VARLEN(DSID,ORDER); RC = CLOSE(DSID); PUT ORDER= FORMAT= LABEL= LENGTH=; RUN; Program 14.5: Determining a variable name, given its position in a SAS data set and vice versa ***Primary functions: OPEN, VARNAME, VARNUM, CLOSE; DATA _NULL_; ID = OPEN("TEST"); VAR_NAME = VARNAME(ID,1); VAR_POS = VARNUM(ID,"DOB"); RC = CLOSE(ID); PUT "The name of the 1st variable in data set test is: " VAR_NAME / "The position of variable DOB is: " VAR_POS; RUN; Program 14.6: A general-purpose macro to display data set attributes such as number of observations, number of variables, variable list, variable type, formats, labels, etc. ***Primary functions: EXIST, OPEN, ATTRN, ATTRC, VARNAME, VARTYPE, VARLEN, VARFMT, VARLABEL, and CLOSE; %MACRO DSN_INFO(DSN); DATA _NULL_; FILE PRINT; IF NOT EXIST("&DSN") THEN DO; PUT "Data set &DSN does not exist"; STOP; END; DSID = OPEN("&DSN","I"); NVARS = ATTRN(DSID,"NVARS"); NOBS = ATTRN(DSID,"NLOBS"); CHARSET = ATTRC(DSID,"CHARSET"); ENGINE = ATTRC(DSID,"ENGINE"); ENCRYPT = ATTRC(DSID,"ENCRYPT"); LABEL = ATTRC(DSID,"LABEL"); SORT = ATTRC(DSID,"SORTEDBY");
PUT "Information for Data Set &DSN" / 72*'-' // ; IF LABEL NE " " then PUT "Data set Label is: " LABEL; PUT "Data set created with engine: " ENGINE / "Character set used: " CHARSET; IF ENCRYPT = "YES" then PUT "Data set is encrypted"; ELSE PUT "Data set is not encrypted"; IF SORT = " " then PUT "Data set is not sorted"; ELSE PUT "Data set is sorted by: " SORT /; PUT "Number of Observations: " NOBS / "Number of Variables : " NVARS / 72*'-' / "***** Variable Information *****" // @1 "Variable Name" @20 "Type" @26 "Length" @34 "Format" @47 "Label"/ 72*'-'; DO I = 1 TO NVARS; NAME = VARNAME(DSID,I); TYPE = VARTYPE(DSID,I); IF TYPE = "C" THEN TYPE = "Char"; ELSE IF TYPE = "N" THEN TYPE = "Num"; LENGTH = VARLEN(DSID,I); FMT = VARFMT(DSID,I); LABEL = VARLABEL(DSID,I); PUT @1 NAME @20 TYPE @26 LENGTH @34 FMT @47 LABEL; END; RC = CLOSE(DSID); RUN; %MEND DSN_INFO; Program 15.1: Creating a test data set for use with most of the V functions PROC FORMAT; VALUE YESNO 1='YES' 0='NO'; INVALUE READ 0-5 = 777 6-9 = 888 OTHER = 999; RUN; DATA VAR; INFORMAT CHAR $CHAR1. Y READ2. Z 3.2 MONEY DOLLAR5.; INPUT @1 X 1. @2 CHAR $CHAR1. @3 Y READ2. @5 Z 3.2 @8 MONEY DOLLAR5. @13 (A1-A3) (1.) @16 DATE MMDDYY10.; FORMAT X YESNO. MONEY DOLLAR8.2 Z 7.4; LABEL X = 'The X variable' Y = 'Pain Scale' MONEY = 'Housing Cost'; ARRAY OSCAR[3] A1-A3; DATALINES; 1B 31231,765987 0N 86549,123234 ; Program 15.2: Determining a variable's type (numeric or character) using VTYPE and VTYPEX ***Primary functions: VTYPE, VTYPEX; DATA CHAR_NUM; SET VAR; TYPE_X = VTYPE(X); TYPE_CHAR = VTYPE(CHAR); TYPE_A1 = VTYPEX('A' || PUT(3-2,1.)); RUN; PROC PRINT DATA=CHAR_NUM NOOBS; TITLE "Listing of Data Set CHAR_NUM"; RUN; Program 15.3: Determining the storage length of character and numeric variables ***Primary function: VLENGTH; DATA _NULL_; LENGTH Y 4 NAME $ 20; X = 123; y = 123; CHAR = 'ABC'; LONG = 'This is a long character variable'; PAD = 'A '; ***A followed by 4 blanks; NAME = 'Frank'; L_X = VLENGTH(X); L_Y = VLENGTH(Y); L_CHAR = VLENGTH(CHAR); L_LONG = VLENGTH(LONG); L_PAD = VLENGTH(PAD); L_NAME = VLENGTH(NAME); PUT L_X = / L_Y = / L_CHAR = / L_LONG = / L_PAD = / L_NAME = ; RUN; Program 15.4: Program to replace all numeric values of 999 with a SAS missing value and to provide a list of variable names where the changes were made ***Primary functions: VNAME, SYMPUTX ***Other functions: DIM; ***This data step determines the number of numeric variables in the data set and assigns it to a macro variable (N); DATA _NULL_; SET MISS; ARRAY NUMS[*] _NUMERIC_; CALL SYMPUTX('N',DIM(NUMS)); STOP; RUN; DATA _NULL_; SET MISS END=LAST; FILE PRINT; ARRAY NUMS[*] _NUMERIC_; LENGTH NAMES1-NAMES&N $ 32; ARRAY NAMES[&N]; ARRAY HOWMANY[&N]; RETAIN NAMES1-NAMES&N; DO I = 1 TO DIM(NUMS); IF NUMS[I] = 999 THEN DO; NUMS[I] = .; NAMES[I] = VNAME(NUMS[I]); HOWMANY[I] + 1; END; END; IF LAST THEN DO I = 1 TO &N; IF NAMES[I] NE ' ' THEN PUT HOWMANY[I] "Values of 999 converted to missing for variable " NAMES[I]; END; RUN; Program 15.5: Writing a general purpose macro to replace all occurrences of a particular value (such as 999) in a SAS missing value in a SAS data set and produce a report showing the number of replacements for each variable ***Primary functions: VNAME, CALL SYMPUT ***Other functions: DIM, LEFT, PUT; %MACRO REPLACE_MISSING ( DSN, /* the SAS data set name */ MISS_VALUE /* the value of the missing value */ ); DATA _NULL_; SET &DSN; ARRAY NUMS[*] _NUMERIC_; N_NUMBERS = LEFT(PUT(DIM(NUMS),5.)); CALL SYMPUT('N',N_NUMBERS); STOP; RUN; DATA _NULL_; SET &DSN END=LAST; FILE PRINT; ARRAY NUMS[*] _NUMERIC_; ARRAY NAMES[&N] $ 32 _TEMPORARY_ ; ARRAY HOWMANY[&N] _TEMPORARY_; DO I = 1 TO DIM(NUMS); IF NUMS[I] = &MISS_VALUE THEN DO; NUMS[I] = .; NAMES[I] = VNAME(NUMS[I]); HOWMANY[I] + 1; END; END; IF LAST THEN DO I = 1 TO &N; IF NAMES[I] NE ' ' THEN PUT HOWMANY[I] "Values of &MISS_VALUE converted to missing for variable " NAMES[I]; END; RUN; %MEND REPlACE_MISSING; Program 15.6: Determining the name, type, and length of all variables in a SAS data set ***Primary function: CALL VNEXT; DATA VAR_INFO; IF 0 THEN SET VAR; LENGTH VAR_NAME $ 32 VAR_TYPE $ 1; DO UNTIL (VAR_NAME = ' '); CALL VNEXT(VAR_NAME,VAR_TYPE,VAR_LENGTH); IF VAR_NAME NE ' ' THEN OUTPUT; END; KEEP VAR_:; RUN; PROC PRINT DATA=VAR_INFO NOOBS; TITLE "Listing of Data Set VAR_INFO"; RUN; Program 15.7: Demonstrating a variety of SAS V functions ***Primary functions: VFORMAT, VFORMATD, VFORMATN, VFORMATW, VINFORMAT, ***VINFORMATD, VINFORMATN, VINFORMATW, VLABEL; DATA VFUNC; SET VAR(OBS=1); LENGTH VFORMAT_X VFORMAT_Y VFORMAT_CHAR VFORMAT_MONEY VFORMATN_X VFORMATN_Y VFORMATN_CHAR VFORMATN_MONEY $8. VINFORMAT_X VINFORMAT_Y VINFORMAT_CHAR VINFORMAT_MONEY VINFORMATN_X VINFORMATN_Y VINFORMATN_CHAR VINFORMATN_MONEY $8. VLABEL_X VLABEL_Y VLABEL_CHAR VLABEL_MONEY $ 40; ***Format functions; VFORMAT_X = VFORMAT(X); VFORMAT_Y = VFORMAT(Y); VFORMAT_CHAR = VFORMAT(CHAR); VFORMAT_MONEY = VFORMAT(MONEY); VFORMATN_X = VFORMATN(X); VFORMATN_Y = VFORMATN(Y); VFORMATN_CHAR = VFORMATN(CHAR); VFORMATN_MONEY = VFORMATN(MONEY); VFORMATW_X = VFORMATW(X); VFORMATW_Y = VFORMATW(Y); VFORMATW_CHAR = VFORMATW(CHAR); VFORMATW_MONEY = VFORMATW(MONEY); VFORMATD_X = VFORMATD(X); VFORMATD_Y = VFORMATD(Y); VFORMATD_CHAR = VFORMATD(CHAR); VFORMATD_MONEY = VFORMATD(MONEY); ***Informat functions; VINFORMAT_X = VINFORMAT(X); VINFORMAT_Y = VINFORMAT(Y); VINFORMAT_CHAR = VINFORMAT(CHAR); VINFORMAT_MONEY = VINFORMAT(MONEY); VINFORMATN_X = VINFORMATN(X); VINFORMATN_Y = VINFORMATN(Y); VINFORMATN_CHAR = VINFORMATN(CHAR); VINFORMATN_MONEY = VINFORMATN(MONEY); VINFORMATW_X = VINFORMATW(X); VINFORMATW_Y = VINFORMATW(Y); VINFORMATW_CHAR = VINFORMATW(CHAR); VINFORMATW_MONEY = VINFORMATW(MONEY); VINFORMATD_X = VINFORMATD(X); VINFORMATD_Y = VINFORMATD(Y); VINFORMATD_CHAR = VINFORMATD(CHAR); VINFORMATD_MONEY = VINFORMATD(MONEY); ***Label information; VLABEL_X = VLABEL(X); VLABEL_Y = VLABEL(Y); VLABEL_CHAR = VLABEL(CHAR); VLABEL_MONEY = VLABEL(MONEY); RUN; PROC PRINT DATA=VFUNC NOOBS HEADING=H; TITLE "Listing of Data Set VFUNC"; VAR V:; RUN; Program 16.1: Demonstrating the bitwise logical functions ***Primary functions: BAND, BNOT, BOR, BXOR; DATA _NULL_; TITLE "Demonstrating the Bitwise Logical Functions"; FILE PRINT; INPUT @1 X BINARY4. / @1 Y BINARY4. / @1 AFRAID BINARY8.; AND = BAND(X,Y); NOT = BNOT(AFRAID); ***Get it, Be not afraid?; OR = BOR(X,Y); XOR = BXOR(X,Y); FORMAT X Y AND OR XOR BINARY4. AFRAID NOT BINARY8.; PUT X= Y= AFRAID= / 60*'-' // AND= OR= XOR= NOT=; DATALINES; 0101 1100 11110000 ; Program 16.2: Enciphering and deciphering text using a key ***Primary functions: BXOR, RANK, BYTE ***Other functions: SUBSTR (used on both sides of the equal sign), DIM; DATA ENCODE; ARRAY L[5] $ 1; ARRAY NUM[5]; ARRAY XOR[5]; RETAIN KEY 173; INPUT STRING $CHAR5.; DO I = 1 TO DIM(L); L[I] = SUBSTR(STRING,I,1); NUM[I] = RANK(L[I]); XOR[I] = BXOR(NUM[I],KEY); END; KEEP XOR1-XOR5; DATALINES; ABCDE Help ; PROC PRINT DATA=ENCODE NOOBS; TITLE "Encoded Message"; VAR XOR1-XOR5; RUN; DATA DECODE; ARRAY L[5] $ 1; ARRAY NUM[5]; ARRAY XOR[5]; RETAIN KEY 173; LENGTH STRING $ 5; SET ENCODE; DO I = 1 TO DIM(L); NUM[I] = BXOR(XOR[I],KEY); L[I] = BYTE(NUM[I]); SUBSTR(STRING,I,1) = L[I]; END; DROP I; RUN; PROC PRINT DATA=DECODE NOOBS; TITLE "Decoding Output"; VAR STRING; RUN; Program 16.3: Writing general purpose encrypting and decrypting macros ***Primary functions: BXOR, RANK, BYTE ***Other functions: SUBSTR (used on the left-hand side of the equal sign), DIM, RANUNI; %MACRO ENCODE(DSN, /* Name of the SAS data set to hold the encrypted message */ FILE_NAME, /* The name of the raw data file that holds the plain text */ KEY /* A number of your choice which will be the seed for the random number generator. A large number is preferable */ ); %LET LEN = 80; DATA &DSN; ARRAY L[&LEN] $ 1 _TEMPORARY_; /* Each element holds a character of plain text */ ARRAY NUM[&LEN] _TEMPORARY_; /* A numerical equivalent for each letter */ ARRAY XOR[&LEN]; /* The coded value of each letter */ RETAIN KEY &KEY; INFILE "&FILE_NAME" PAD; INPUT STRING $CHAR&LEN..; DO I = 1 TO DIM(L); L[I] = SUBSTR(STRING,I,1); NUM[I] = RANK(L[I]); XOR[I] = BXOR(NUM[I],RANUNI(KEY)); END; KEEP XOR1-XOR&LEN; RUN; %MEND ENCODE; %MACRO DECODE(DSN, /* Name of the SAS data set to hold the encrypted message */ KEY /* A number that must match the key of the enciphered message */ ); %LET LEN = 80; DATA DECODE; ARRAY L[&LEN] $ 1 _TEMPORARY_; ARRAY NUM[&LEN] _TEMPORARY_; ARRAY XOR[&LEN]; RETAIN KEY &KEY; LENGTH STRING $ &LEN; SET &DSN; DO I = 1 TO DIM(L); NUM[I] = BXOR(XOR[I],RANUNI(KEY)); L[I] = BYTE(NUM[I]); SUBSTR(STRING,I,1) = L[I]; END; DROP I; RUN; PROC PRINT DATA=DECODE NOOBS; TITLE "Decoding Output"; VAR STRING; RUN; %MEND DECODE;