Arrays in SAS

Arrays are nothing but a collection of elements of same type. We all are aware of Arrays in various object oriented languages, but unlike in SAS, Arrays doesnot hold any value. SAS uses arrays as reference variable in programming. 

Syntax of Arrays: Array in SAS is declared as  
  
                         array man{10} man1 man2 man3 . . . . . man10;

Array names follow same rules as for SAS variable names. Name is followed by a bracket {} or [] & value inside bracket is a subscript.

A SAS array must contain all character or numeric variable. A character array can be declared as 

                          array  month{12} $3. JAN-DEC;

Usage of Array: 
1).  Arrays can be used to set missing values for any other values like 999 ,0 ,???, NA etc.
2). Converting all character values to lower case, upper case, or propercase.
3). To Create new variables.


_character_ is used with array if we don't know the names of character type to be included in array.

                         array chars{*}  $10. _character_;

A ' * ' is used when we need SAS to calculate the no. of elements in array. Like we mentioned in above array declaration, in which a charcter array is  created.

dim(chars) is used to calculate no. of elements to be passed as an argument in some loop or conditional statement.

If we don't mention any element names in array then SAS consider array name with subscript as element name. For example suppose an array for employees is created.

                         array employee {10} 

Then its elements will be    employee1, employee2, employee3, employee4.... employee10.


                                                                            

Character & Numeric Functions (Part-2)

SAS has another set of very important set of functions, which are used in preparation of data. These functions are numeric functions. Below is a table for some of the numeric function.

Numeric Functions:

Function Application
Int (Variable_name) Give integer part of variable
Round(Variable_name, rounding off value) An rounded value of variable is returned
missing(Variable_name) Return a '1' in case of variable has no value
n( of var1-var2) Return no. of observations
n( variable1_name, variable2_name, ...) Return count of non missing variable in same observations
min( of var1-varn) return min. of variable values
max( of var1-varn) return max. of variable values
mean(of var1-varn) return mean of variable values
nmiss(of var1-varn) return count of missing of variable values
largest( n, of var1-var10) return nth order largest value like for n =2 second largest, n =3 third largest
abs(Variable_name) Will return absolute value
sqrt(variable_name) Will return square root of variable value
exp(variable_name) Will return exponential
Lagn(variable) Will return nth lagged value of variable
Ranuni (0) Generates a random value between 0 & 1, which is based on unform distribution
diffn(variable_name) Will return a difference of current value & nth lagged value for the variable
Now lets consider an example of a sales data of a grocery store as mentioned below.

 item_id          Cost         MRP       Disc.        
GR_012          32.05      41.54       5.05%
GR_121         29.45       32.45      6.97%
CP_312         12.21        15.65      7.23%
DN_121         6.43         7.12        1.05%

Now shopkeeper wants to calculate profit margin if he sells item on MRP & gives discount mentioned in the data. We will have to use a combination of character & numeric variable in order to achieve client's requirement.

Let's first bring this data into SAS.

DATA ITEM_DATA;
INPUT ITEM_ID$ COST MRP DISC$;
DATALINES;
GR_012 32.05 41.54 5.05%
GR_121 29.45 32.45 6.97%
CP_312 12.21 15.65 7.23%
DN_121 6.43 7.12 1.05%
;
RUN;

                                                                                                                                        
DATA ITEM_DATA1;                                                                                                                        
SET ITEM_DATA (RENAME=(DISC = DISCOUNT));                                                               
DISC = INPUT(COMPRESS(DISCOUNT,'%'),8.);                                                         
SELL_PRICE = (1- DISC/100)*MRP;                                                                                    
MARGIN = ROUND((SELL_PRICE - COST)*100/SELL_PRICE, 0.01); 
MARGIN_IN_PERCENT = CAT(PUT(MARGIN, 5.2), '%');                                                        
DROP DISCOUNT;                                                                                                                          
RUN;

There are so many things happening here in this code. I will explain all of them, one at a time. 
So as i said earlier also, SAS gives user a method to change a character variable into a numeric variable. Here we have discounts as character datatype. In order to calculate Selling price we have to convert discount into a numeric variable. An input function can be used to do so. We have used rename option to recover original name of the variable. An input statement has a character variable passed into first argument & then a format length modifier, which will tell SAS, in what format do we need this value. Here 8. is a numeric format having length of 8 bytes. We have also used a compress function to remove '%' sign from the discount variable. 

Now once we have disc in numeric format we can make calculations for Margin, Since most of the time we need a percent upto 2 decimal value we have used a Round function to convert a value into 2 decimal format.

To understand a numeric to character conversion, we have used margin variable & used a PUT function which will convert Margin_in_percent as character value. For future calculation purpose we can use Margin, which is still a numeric value.







Character & Numeric Functions (Part-1)

SAS has several pre-defined character as well as numeric functions, which are used on the variable values to generate some required outputs. Lets understand this with an example. A retail company wants to know about customer contact details based on their address. But the problem is they have single field data for customer address. Now an address may contain Name, Place, Telephone no., City, State, and PIN Code. One needs to seperate these values under different heads or variables. 
This can be performed using various character functions in SAS.


CUST_ID       ADD

110                 AJAY SINGH SEC.105, NOIDA 988134532
112                 BABITA KUMARI NAYI GALI DELHI 893324511 
113                 SATVINDER GURUGRAM 01244321912

Looking at above address data one might use various combination of SAS character or Numeric functions to get valuable information out of it. But before we write any code, lets first get introduced with SAS Character functions & SAS numeric functions.

Character Functions:
Below is a table mentioning some important character functions. I will explain some of them in detail.

Sr. No. Function  Application
1   Length (string-name) Retruns length of a character string not counting trailing blank.
2   Lengthn (string-name) Similar to Length function, except it returns, a '0', if a blank value.
3   Lengthc( string-name) Return storage length of string.
4   Upcase (string) Converts whole string to uppercase
5   Lowcase (string) Converts whole string to lowcase
6   Propcase(string) Converts whole string to sentance case
7   Compbl (string) Converts 2 or more blank to single blank between two words
8   Compress (string) removes blank or character specified
9   CAT(string1, string2) It joins 2 string & output string length - 200, if variable length is not declared
10   CATS(string1, string2) removes leading & trailing blank before  joining 2 strings
11   CATX('seperator', string1, string2) removes leading & trailing blank, & then put a seperator b/w 2 strings
12   Left(string) removes leading blanks
13   Trim(string) removes trailing blanks
14   Strip(string) removes all blanks
15   Find(string, 'find-string', modifier) Returns the position of find-string in string, if modifier is mentioned it will make string case irrelevant.
16   Findc(string, 'characters') Returns the position of first individual character provided in the 'characters'. It looks for individual character unlike 'FIND' function which searches for string.
17   Findw(string, find-string,delimiter, modifier) Finds word not string, rest is similar to Find function
18   Anydigit() Position of first digit is returned, if not then 0 is returned
19   Anypunct() Position of punctuation is returned, if not then 0 is returned
20   Anyspace() Position of blank space is returned
21   Anyalpha() Position of first alphabet is returned, if not then 0 is returned
22   Notdigit() If argument contains anything other than digit, then position of letter which is not digit is returned. If all digit then '0' is returned.
23   Notalpha() If argument contains anything other than alphabet, then position of letter which is not alphabet is returned. If all alphabet then '0' is returned.
24   Notalnum() If argument contains anything other than alphanumeric, then position of letter which is not alphabet is returned. If all alphanumeric then '0' is returned.
25   Verify(string-name, 'valid characters') Returns a '0' if any valid characters are there, & if not then position of first invalid character is returned.
26   substr(string-name, position, length) Returns a part of string declared as first argument, having first character mentioned as second argument as position, & length of string as third argument
27   scan(string-name,word position, delim) Used to extract word from a string, second argument tells which position word to extract.
28   compare(string1, string2, modifier) Used to compare 2 string, retuns a 0 if both strings are matched. If a modifier ';' is mentioned, then longer string is stripped to smaller string length & then compared.
29   Translate(string,'to replace letter', 'replaced by letter') It replaces letters in the string  by the third argument letters.
30   Transword(string, replace word, replaced by word) It replaces words in the string  by the third argument words unlike Translate which replaces only letters.


Compress function: A compress function is used to remove blanks and a set of characters or punctuation or numerals, mentioned as in second argument. There is a third argument also, which is called as function modifier & produce different results as per requirement. A list of compress function modifier is mentioned below.

Compress function syntax is

COMPRESS ( STRING-NAME, ' TO BE REMOVED CHARACTERS', MODIFIER)

Modifier Action
a Remove all Characters
d Remove all digits
i ignore string case
k retain all items listed in second argument
s blank space, tab to be added in second argument
p Remove all Punctuations

Lets apply compress function to above example & try to take out contact nos. from address.

/* First let's create a dataset CUST_DATA as mentioned in above example.*/

DATA CUST_DATA;                                                                                                                      
LENGTH ADD$ 50.;                                                                                                                      
INPUT CUST_ID  ADD$ &;                                                                                                                
DATALINES;                                                                                                                            
110 AJAY SINGH SEC.105, NOIDA 988134532
112 BABITA KUMARI NAYI GALI DELHI 893324511                                                              
113 SATVINDER GURUGRAM 01244321912                                                                                  
;                                                                                                                                  
RUN;



DATA CUST_CONT;
SET CUST_DATA;
CONTACT = COMPRESS(ADD, '1234567890', 'k');
DROP ADD;
RUN;

We will get below dataset. Which has only numbers under variable "Contact".





For customer id 110 contact no. obtained is not correct. It has some part of address in it. We have to modify our code, so that it contains only contact no. Also a point to notice is that contact no. is still a character variable.(Notice, contact no. is left aligned.) We can easily convert a character string of numbers into a numeric variable using PUT function, and vice-versa using an INPUT function. I will describe the same in detail.

Suppose if we want to take out first name of customer from the address variable. we can do so by using a combination of character function.
First we are interested in finding the first space after the first name. Below mentioned code can give the same.

DATA SPACE;
SET CUST_DATA;
n = FINDC(ADD, ' ');
RUN;

Now we know the length of string that we need to take out from ADD  variable which is the first name of customer.

Using a SUBSTR function we can take out the string of a particular length.
Again adding a substring function step in above code, mentioning the length of first names, i.e. n-1.

DATA SPACE;
SET CUST_DATA;
n = FINDC(ADD, ' ');
FIRST_NAME = SUBSTR(ADD, 1, n-1);
DROP n ADD;
RUN;

Below is the output for this program.



Remember you can combine & code for contact no. & first name in single program, Here i am writing seperate codes for better understanding of the codes.

Next we will try to take out city from the address field. We know city of residence for customer is just before his contact no. & will leverage this information to take out customer's current location from add variable.

Lets first remove all the trailing blanks from the Add observation, since all observations are of different length.

This can be achieved using LEFT & TRIM function.

ADD1 = LEFT(TRIM(ADD));

Now we will use a SCAN function to look for second word, but this time from right side.

CITY = SCAN(ADD, -2);

Combining all the information & codes above.
                                                                                                                                 
DATA CUST_DATA_REFINED;                                                                                                    
SET CUST_DATA;                                                                                                                      
ADD1 = LEFT(TRIM(ADD));                                                                                                          
n= FINDC(ADD, '  ');                                                                                                    
FIRST_NAME = SUBSTR(ADD, 1, n-1);                                                                                    
CITY = SCAN(ADD1,-2);                                                                                                          
DROP  n ADD ADD1;                                                                                                                  
RUN;

We will have below dataset as output.



We can many combinations & ways of performing this task. The above codes are just one single interpretation of how to tackle character strings & taking out different information from a character string.




Conditional Statement & Loops in SAS

A Conditional statements checks for a condition or a set of condition & decide further flow of a program.  SAS, like any other programming language has some predefined functions which helps a coder to put a conditional check on the variables & then execute the code written inside conditional parentheses accordingly.

SAS has following condtional statements, which we are going to explain in detail & then compare-contrast them.

IF-Else-Then Statement: An If-else statement checks for the one or more than one conditions & 'IF' conditions are true 'THEN' SAS executes all the statements written within conditional parentheses. If condition is false then SAS will not execute Then statements & go straight to 'ELSE' statement.
Syntax of If-else-then statement is 

IF CONDITION THEN <EXECUTE THIS>;
ELSE <EXECUTE THIS>;

Writing an 'ELSE' statement is not necessary, if we don't need to test any other conditions.

Sometimes, it is required to test more than 1 conditions in a single statement & we have to check them simultaneously. In such cases we can use operators like 'AND', 'OR', 'IN' etc. This is the right time to introduce all operators of SAS & their applications. 

Operators: These are used to check more than one conditions at a time. 

Operator  Application Conditional Statements
OR (Boolean) Returns a TRUE, When checking 2 or more conditions, & one or more can be true IF-ELSE
AND (Boolean) Returns a TRUE, When checking 2 or more conditions, & all of then must be true IF-ELSE
NOT (Boolean) Returns a TRUE, if condition mentioned after NOT is false. IF-ELSE
EQ, = To check arithmetic equality IF-ELSE
NE , != Arithmetic operator to check inequality IF-ELSE
LE, <= Less than & Equal to IF-ELSE
GE, >= Greater than & Equal to IF-ELSE
IN Multiple 'OR', To check more than one condition on same variable. IF-ELSE
IS MISSING To check for missing value. WHERE
IS NULL To check for null value. WHERE
BETWEEN …. AND  ….. To check between. WHERE
CONTAINS Checks for more than one strings in a variable. WHERE
LIKE "%...." To check for strings, alphabets & wildcards WHERE
 =* For phonetic mathes WHERE


Precedence order of Boolean operators is decided in SAS, This order is as follows

                                                                     NOT > AND > OR

Nested IF statement: A nested if statement is used to check for conditions within conditions. This makes program a bit  less complicated to understand, if written properly in alignment & indents. Below is a simple syntax for Nested IF statement.

IF <CONDITION1> AND <CONDITION2>  THEN OUTPUT;
     ELSE 
     IF <CONDITION3> OR <CONDITION4> THEN DELETE;
         ELSE
         IF <CONDITION5> THEN CONTINUE;

Here OUTPUT, DELETE, CONTINUE are options defined in SAS, to change program flow. 

Select-When Statement: This conditional statement is used to test conditions on a variable. This variable is declared in SELECT statement as an arguement. This statement is more efficient when conditions to be checked in are mutually exclusive. Syntax is as below.

SELECT ( VAR1);
WHEN (CONDITION1)  
              EXECUTE THIS;
WHEN (CONDITION2)  
              EXECUTE THIS;
.
.
.
.
OTHERWISE EXECUTE THIS
END;
RUN;


Where Statement: A Where statement is also used to test conditions & its used mostly in subsetting dataset. Syntax of a where statement subsetting data is given below.

DATA XYZ;
SET ABC;
WHERE CONDITION1;
RUN




DO Loops: Sometimes we need a certain part of code to be run iteratively. One way is to write these codes again & again or simply use a 'DO' loop statement. A Do loop can run a particlular lines of code for a defined no. of times.
This Do loop can perform a code after a condition is checked as mentioned below. Suppose we have if-conditional statements which can perform a single action after checking given conditions. This will increase length of code & hence in-turn will decrease program inefficiency.

IF <CONDITION1> THEN <ACTION1>;
IF <CONDITION1> THEN <ACTION2>;
IF <CONDITION1> THEN <ACTION3>;
IF <CONDITION1> THEN <ACTION4>;

DO Group statement:  Using a Do group, we can perform multiple actions in a single conditional statement. For above IF statements we can write,

IF <CONDITION1> THEN  DO;
<ACTION1>;
<ACTION2>;
<ACTION3>;
<ACTION4>;
END;


Iterative DO loops: When we have to run a group of statement multiple times, without checking any condition on variable. An index variable is used to program no. of iteration. This index variable is incremented every time by a pre-defined incremental value & following statements will be executed till end statement. This loop will keep on iterating till the index variable value is less than or equal to 'TO' value. Its syntax is defined as

DO  N=  <initial value>  TO <terminal value>  BY <incremental value>;
STATEMENT1;
STATEMENT2;
.
.
.
END;
RUN;


DO WHILE/ DO UNTIL Loops: These loops are used when in place of index variable we have to test a condition on variable present in data or calculated variable. Choosing between DO-WHILE or DO-UNTIL depends upon when conditions applied to the loop will be checked. In a DO-UNTIL loop, condition is checked at the bottom of the loop, everytime after loop is iterated. While, a DO-WHILE loop is used when condition needs to be checked at the beginning of loop. So here first condition is tested & if true then loop is run one time & then again condition is checked. Syntax of loops are

DO UNTIL (<VARIABLE CONDITION>);
STATEMENT1
STATEMENT2
.
.
.
END;


DO WHILE (<VARIABLE CONDITION>);
STATEMENT1
STATEMENT2
.
.
.
END;

Formats & Informats in SAS


Formats and informats are one of the most important PROCs in SAS, as it defines the appearance of SAS variable in ouputs of a dataset. They are used to group variables, without changing the internal data of input dataset. 

Informats are used to 'READ' a value in a particular manner or it generally tells SAS about what is the structure of datatype of an input dataset. It doesnot give any information about how SAS is going to write this data in output dataset. This information is defined by Formats, which will tell SAS how to 'WRITE' data in dataset.

There are many informats & formats pre-defined into SAS. Few of them are...

DOLLAR5. : These informats are used to read values like salary, revenue, profit etc., values which are having a '$' sign mentioned in data. '5.' which is mentioned after 'DOLLAR' informat is the length of variable. In order to write the values in same format as in raw datafile, we have to mention all formats with variable in a FORMAT statement. 

COMMA6.:  A COMMA informat is used to read values having a comma delimiter in data value. These values are generally for a numeric variable & in absence of the appropriate COMMA informat SAS will treate those numeric values as charcters & output a missing "." value in place of value.

DATE9.:  SAS has lots of Date formats which we will explain in detail in our future post related to Date type variable. For now just remember DATE9. is a format which is used to read date type variable like 12JUN1993.

MMDDYY10.: This informat/format is used to read/write a date variable which is like 10/06/1983. In its absence SAS will treat these values as character variable.

MMDDYY8.: This is used to read or write 10/06/83.

Let's write  SAS formats for a data file having data of employees.

EMP_ID   GENDER  SALARY 
101               M              23000
103               m              35000
104               Male          42500
110                F               32500
119               Female      39500


SAS gives liberty to user to create user defined formats & informats using PROC FORMAT & PROC INFORMAT.

We will define a fomat for the salary bracket of the company employees.

PROC FORMAT;
VALUE SAL_BCKT  LOW - <35000 = 'Less than 35k'
                                    35000 - <50000 = 'btw 35 - 50k'
                                    50000 - HIGH = 'Gtr than 50k'
                                    other   = 'Missing';
RUN;

Since Gender is defined in so many ways we have to create a format in order to bring all in a similar standard form.

PROC FORMAT;
VALUE  $GEN   'm','M', 'Male''MALE'
                             'f','F','Female' = 'FEMALE';
RUN;      

Now when we write a code to input this employee data file we will introduce these formats

DATA  EMP_SAL;
INPUT EMP_ID GENDER$ SALARY;
INFILE " <FILE-PATH>" DSD;
FORMAT SALARY SAL_BCKT. GENDER $GEN.;
RUN;

Output dataset EMP_SAL Contains following values.


SAS users will always want to use their customised Formats & it is a general practise to save all the formats together in a single library which when loaded to SAS, will bring all previously defined Formats & Informats to SAS.

A SAS Format default library is Work folder. It can be changed to a pre-determined location using Library option in PROC FORMAT statement.

PROC FORMAT  LIBRARY = mylib;

Now whenevr we use a Format statement, SAS will first look into default formats, then into work library, then any other user defined library. In order to increase system performance & save all the time SAS spends looking for user defined formats in other places. we have to set

OPTIONS FMTSEARCH (mylib);

Now SAS will start looking for Formats from mylib library.

If a user want to see definition of all the Formats defined in SAS, he can do so by giving fmtlib option into Proc format statement. All the definition will come in output window.

PROC FORMAT LIBRARY = mylib FMTLIB;
RUN;

A 'SELECT' statement  can be used in order to see a particular format definition.





Steps involved in Code processing in SAS

This post is an attempt to answer those questions like what exactly happen when SAS runs a code or What happens at the back-end, when a user writes a code in SAS programming window & submit or run the program. Understanding this back-end SAS processing helps a programmer to know how SAS run & sometimes make their life easy by taking advantage of execution process.

SAS process any submitted code or data in 2 steps.

Compile Stage: In this stage SAS performs following tasks
1. In compile stage SAS assigns area in memory to store dataset called input buffer
2. SAS checks for input file & determines various variable attributes ( i.e. datatype, length etc.)
3. Reads code for any invalid syntax, errors & determines names of variable.
4. A descriptor portion is formed which will store all information related to variables such as variable name, datatype, length, label, default format & informat etc.

During compile state, SAS doesnot read any data from input file & doesnot evaluate any logical or condtional loops or statement. A reserved memory called as program data vector is also created to store all information about variables, data step, errors etc. Then SAS starts checking input data code & if any variable is assigned in between, it checks for its datatype, name & assign it a place in descriptor portion. 

Tip: We can declare the length for different variables before an input statement. This length will be then stored in descriptor portion as default length. 

Lets' consider a small program for salary of employees.

DATA EMP_SAL;
INPUT EMP_ID EMP_NAM$ AGE GENDER$ SALARY;
SALPERAGE = SALARY/AGE;
DATALINES;
101 AJAY 30 M 30000
102 MANI 28 F 28500
103 SAHIL 32 M 35000
;
RUN;

After submitting above program, SAS will first create a descriptor portion, which will store all attributes of variables. Descriptor portion will look like this

Descriptor Portion:

EMP_ID
EMP_NAM
AGE
GENDER
SALARY
SALPERAGE
NUMERIC (8BYTES) 
Format 12.
Informat 12.
 CHARACTER
 (8BYTES)
Format $8.
Informat $8.
NUMERIC (8BYTES)
Format 12.
Informat 12. 
CHARACTER (8BYTES)
Format $8.
Informat $8. 
NUMERIC (8BYTES)
Format 12.
Informat 12. 
NUMERIC (8BYTES)
Format 12.
Informat 12.

As you can see SAS has allocated default memories to each variable in input buffer. 

Now SAS is ready to run it's second stage of processing code.

Execution Stage: In excution stage,  all values are set to missing or no value. In SAS numeric missing value is denoted by 'PERIOD' i.e. "." & a character missing value is denoted by 'BLANK SPACE'  i.e. "  ".SAS starts with intially setting all variable values to missing & this happens every time SAS reads a new line of data. An internal pointer keeps a track of current record executed. SAS will keep on running or executing till it reaches an end of file marker.

Program Data Vector:  _n_ =1


EMP_ID
EMP_NAM
AGE
GENDER
SALARY
SALPERAGE
NUMERIC (8BYTES) 
Format 12.
Informat 12.
 CHARACTER
 (8BYTES)
Format $8.
Informat $8.
NUMERIC (8BYTES)
Format 12.
Informat 12. 
CHARACTER (8BYTES)
Format $8.
Informat $8. 
NUMERIC (8BYTES)
Format 12.
Informat 12. 
NUMERIC (8BYTES)
Format 12.
Informat 12.
 .

. 

. 
. 


When SAS executes first obs or line of data in above program

Program Data Vector: _n_ =1
EMP_ID
EMP_NAM
AGE
GENDER
SALARY
SALPERAGE
NUMERIC (8BYTES) 
Format 12.
Informat 12.
 CHARACTER
 (8BYTES) 
Format $8.
Informat $8.
NUMERIC (8BYTES)
Format 12.
Informat 12. 
CHARACTER (8BYTES)
Format $8.
Informat $8. 
NUMERIC (8BYTES)
Format 12.
Informat 12. 
NUMERIC (8BYTES)
Format 12.
Informat 12.
 101
AJAY
30
M
30000
. 

Then it calculates SALPERAGE variable & put this value in Program data Vector

Program Data Vector: _n_ =1
EMP_ID
EMP_NAM
AGE
GENDER
SALARY
SALPERAGE
NUMERIC (8BYTES) 
Format 12.
Informat 12.
 CHARACTER
 (8BYTES) 
Format $8.
Informat $8.
NUMERIC (8BYTES)
Format 12.
Informat 12. 
CHARACTER (8BYTES)
Format $8.
Informat $8. 
NUMERIC (8BYTES)
Format 12.
Informat 12. 
NUMERIC (8BYTES)
Format 12.
Informat 12.
 101
AJAY
30 
M
30000 
1000 

Once SAS reaches a run statement, it save data in input buffer or dataset & again turns back to dataline statement. Internal pointer for program is set to 2 & all values of variables in Program Data Vector is set to missing and SAS is ready to execute second line of data.

Program Data Vector: _n_ =2
EMP_ID
EMP_NAM
AGE
GENDER
SALARY
SALPERAGE
NUMERIC (8BYTES) 
Format 12.
Informat 12.
 CHARACTER
 (8BYTES) 
Format $8.
Informat $8.
NUMERIC (8BYTES)
Format 12.
Informat 12. 
CHARACTER (8BYTES)
Format $8.
Informat $8. 
NUMERIC (8BYTES)
Format 12.
Informat 12. 
NUMERIC (8BYTES)
Format 12.
Informat 12.
 .

. 

. 
. 

SAS will keep on executing until it reaches an end of file marker.

My First SAS Program