In SAS, you can generate a set of random numbers that are uniformly distributed by using the RAND function in the DATA step or by using the RANDGEN subroutine in SAS/IML software. (These same functions also generate samples from other common distributions such as binomial and normal.) The syntax is simple. The following DATA step creates a data set that contains 10 random uniform numbers in the range [0,1]:

```data A; call streaminit(123); /* set random number seed */ do i = 1 to 10; u = rand("Uniform"); /* u ~ U[0,1] */ output; end; run;```

The syntax for the SAS/IML program is similar, except that you can avoid the loop (vectorize) by allocating a vector and then filling all elements by using a single call to RANDGEN:

```proc iml; call ranseed(123); /* set random number seed */ u = j(10,1); /* allocate */ call randgen(u, "Uniform"); /* u ~ U[0,1] */```

### Random uniform on the interval [a,b]

If you want generate random numbers on the interval [a,b], you have to scale and translate the values that are produced by RAND and RANDGEN. The width of the interval [a,b] is b-a, so the following statements produce random values in the interval [a,b]:

``` a = -1; b = 1; /* example values */ x = a + (b-a)*u;```

The same expression is valid in the DATA step and the SAS/IML language.

### Random integers

You can use the FLOOR or CEIL functions to transform (continuous) random values into (discrete) random integers. In statistical programming, it is common to generate random integers in the range 1 to Max for some value of Max, because you can use those values as observation numbers (indices) to sample from data. The following statements generate random integers in the range 1 to 10:

``` Max = 10; k = ceil( Max*u ); /* uniform integer in 1..Max */```

If you want random integers between 0 and Max or between Min and Max, the FLOOR function is more convenient:

``` Min = 5; n = floor( (1+Max)*u ); /* uniform integer in 0..Max */ m = min + floor( (1+Max-Min)*u ); /* uniform integer in Min..Max */```

Again, the same expressions are valid in the DATA step and the SAS/IML language.

### Putting it all together

The following DATA step demonstrates all the ideas in this blog post and generates 1,000 random uniform values with various properties:

```%let NObs = 1000; data Unif(keep=u x k n m); call streaminit(123); a = -1; b = 1; Min = 5; Max = 10; do i = 1 to &NObs; u = rand("Uniform"); /* U[0,1] */ x = a + (b-a)*u; /* U[a,b] */ k = ceil( Max*u ); /* uniform integer in 1..Max */ n = floor( (1+Max)*u ); /* uniform integer in 0..Max */ m = min + floor((1+Max-Min)*u); /* uniform integer in Min..Max */ output; end; run;```

You can use the UNIVARIATE and FREQ procedures to see how closely the statistics of the sample match the characteristics of the populations. The PROC UNIVARIATE output is not shown, but the histograms show that the sample data for the u and x variables are, indeed, uniformly distributed on [0,1] and [-1,1], respectively. The PROC FREQ output shows that the k, n, and m variables contain integers that are uniformly distributed within their respective ranges. Only the output for the m variable is shown.

```proc univariate data=Unif; var u x; histogram u/ endpoints=0 to 1 by 0.05; histogram x/ endpoints=-1 to 1 by 0.1; run;   proc freq data=Unif; tables k n m / chisq; run;```

One of the great innovations with SAS 9.3 is the focus on ODS statistical graphics.

"Wait a minute," you're thinking, "weren't ODS graphics added in SAS 9.2?"

Yes, that's true.  But with SAS 9.3 there is even more capability: more analytical SAS procedures support the graphs, and there are more features in the specialized graph procedures such as SGPLOT.

But even more remarkable is the change in SAS display manager to bring ODS graphics into focus.  Now HTML output (not text listing) is the default output from SAS display manager.  And a new ODS style, named "HTMLBlue", has been designed to show the graphs in a crisp and colorful way.

If you are using SAS Enterprise Guide 4.3, you don't have HTMLBlue in your list of available styles.  But you can add it with a few simple steps.

### 1. Use SAS to create a copy of the HTMLBlue stylesheet.

```filename out temp; /* change the location of the css fileref as necessary */ filename css "c:\temp\htmlblue.css"; ods html file=out style=htmlblue stylesheet=css; proc print data=sashelp.class; run; ods html close;```

You can run this code in a SAS window or in SAS Enterprise Guide, making sure that you are able to write to the location that the CSS fileref is mapped to.  After running the program, the htmlblue.css file will appear in the location specified in the program.

### 2. Copy the htmlblue.css file to the Styles subfolder in your SAS Enterprise Guide application directory.

Depending on how you installed SAS Enterprise Guide, that location might be:

C:\Program Files\SAS\EnterpriseGuide\4.3\Styles
or
C:\Program Files\SASHome\x86\SASEnterpriseGuide\4.3\Styles

### 3. In SAS Enterprise Guide, change the preferences for your results output to use the new HTMLBlue style.

The style will automatically appear in the list of available styles.  For example, to change the default output for SAS Report, select Tools->Options->Results->SAS Report, and select "htmlblue" from the list of styles.

And even though the style is named "HTMLBlue", you don't have to use it for just HTML.  It's just as attractive when using SAS Report.  Of course you can also use it for RTF and PDF output as well, although you might prefer a cleaner style such as Journal for printed output.  The SAS OnlineDoc for statistical graphics contains a comparison of the various styles, with considerable focus on the details for the graphics.

It seems like such a simple problem: how can you reliably compute the age of someone or something? Susan lamented the subtle issues using the YRDIF function exactly 1.0356164384 years ago.

Sure, you could write your own function for calculating such things, as I suggested 0.1753424658 years ago.

Or you could ask your colleagues in the discussion forums, as I noticed some people doing just about 3.7890410959 years ago.

Or, now that SAS 9.3 is available, you can take advantage of the new AGE basis that the YRDIF function supports. For example:

```data _null_; x = yrdif('29JUN2010'd,today(),'AGE'); y = yrdif('09MAY2011'd,today(),'AGE'); z = yrdif('27SEP2007'd,today(),'AGE'); put x= y= z=; run;```

Yields:

```x=1.0356164384 y=0.1753424658 z=3.7890410959
```

This new feature and many more were added in direct response to customer feedback from Susan and others. And that's a practice that never gets old.

It seems like such a simple problem: how can you reliably compute the age of someone or something? Susan lamented the subtle issues using the YRDIF function exactly 1.0356164384 years ago.

Sure, you could write your own function for calculating such things, as I suggested 0.1753424658 years ago.

Or you could ask your colleagues in the discussion forums, as I noticed some people doing just about 3.7890410959 years ago.

Or, now that SAS 9.3 is available, you can take advantage of the new AGE basis that the YRDIF function supports. For example:

data _null_;
x = yrdif('29JUN2010'd,today(),'AGE');
y = yrdif('09MAY2011'd,today(),'AGE');
z = yrdif('27SEP2007'd,today(),'AGE');
put x= y= z=;
run;

Yields:

x=1.0356164384 y=0.1753424658 z=3.7890410959

This new feature and many more were added in direct response to customer feedback from Susan and others. And that's a practice that never gets old.

SAS Enterprise Guide sets values for several useful SAS macro variables when it connects to a SAS session, including one macro variable, &_CLIENTPROJECTPATH, that contains the name and path of the current SAS Enterprise Guide project file.

(To learn about this and other macro variables that SAS Enterprise Guide assigns, look in Help->SAS Enterprise Guide help, and search the Index for "macro variables".)

If your SAS program knows the path of your project file, then you can use that information to make your project more "portable", and reference data resources using paths that are relative to the project location, instead of having to hard-code absolute paths into your program.

For example, I've been working with some data from DonorsChoose.org, and I've captured that work in a project file. After saving the project, I can easily get the name of the project file by checking the value of &_CLIENTPROJECTPATH:

```15    %put &amp;_clientprojectpath;
'C:\DataSources\DonorsChoose\DonorsChoose.egp'```

If you can get the full path of the project file, then you can build that information into a SAS program so that as you move the project file and its "assets" (such as data), you don't need to make changes to the project to accommodate a new project folder "home". Here is the bit of code that takes the project file location and distills a path from it, and then uses it to assign a path-based SAS library.
```/* a bit of code to detect the local project path                          */
/* NOTE: &amp;_CLIENTPROJECTPATH is set only if you saved the project already! */
%let localProjectPath =
%sysfunc(substr(%sysfunc(dequote(&amp;_CLIENTPROJECTPATH)), 1,
%sysfunc(findc(%sysfunc(dequote(&amp;_CLIENTPROJECTPATH)), %str(/), -255 ))));

libname DC "&amp;localProjectPath";```

Here's the output:
```21  libname DC "&amp;localProjectPath";
NOTE: Libref DC was successfully assigned as follows:
Engine:        V9
Physical Name: C:\DataSources\DonorsChoose```

This allows me to keep the project together with the data that it consumes and creates. And I can easily share the project and data with a colleague, who can store it in a different folder on his/her machine, and it should work just the same without any changes.

SAS Enterprise Guide sets values for several useful SAS macro variables when it connects to a SAS session, including one macro variable, &_CLIENTPROJECTPATH, that contains the name and path of the current SAS Enterprise Guide project file.

(To learn about this and other macro variables that SAS Enterprise Guide assigns, look in Help->SAS Enterprise Guide help, and search the Index for "macro variables".)

If your SAS program knows the path of your project file, then you can use that information to make your project more "portable", and reference data resources using paths that are relative to the project location, instead of having to hard-code absolute paths into your program.

For example, I've been working with some data from DonorsChoose.org, and I've captured that work in a project file. After saving the project, I can easily get the name of the project file by checking the value of &_CLIENTPROJECTPATH:

15    %put &_clientprojectpath;
'C:\DataSources\DonorsChoose\DonorsChoose.egp'

If you can get the full path of the project file, then you can build that information into a SAS program so that as you move the project file and its "assets" (such as data), you don't need to make changes to the project to accommodate a new project folder "home". Here is the bit of code that takes the project file location and distills a path from it, and then uses it to assign a path-based SAS library.

/* a bit of code to detect the local project path                          */
/* NOTE: &_CLIENTPROJECTPATH is set only if you saved the project already! */
%let localProjectPath =
%sysfunc(substr(%sysfunc(dequote(&_CLIENTPROJECTPATH)), 1,
%sysfunc(findc(%sysfunc(dequote(&_CLIENTPROJECTPATH)), %str(/\), -255 ))));

libname DC "&localProjectPath";

Here's the output:

21  libname DC "&localProjectPath";
NOTE: Libref DC was successfully assigned as follows:
Engine:        V9
Physical Name: C:\DataSources\DonorsChoose

This allows me to keep the project together with the data that it consumes and creates. And I can easily share the project and data with a colleague, who can store it in a different folder on his/her machine, and it should work just the same without any changes.

The recent issue of InformationWeek features a Q&A session with Ken Thompson, one of the creators of the Unix operating system. (He collaborated with Dennis Ritchie, of C language fame. Since much of SAS is written in C, I daresay there are a few copies of K&R around here.) One [...]

The recent issue of InformationWeek features a Q&A session with Ken Thompson, one of the creators of the Unix operating system. (He collaborated with Dennis Ritchie, of C language fame. Since much of SAS is written in C, I daresay there are a few copies of K&R around here.)

One of the questions hit on the topic of code reviews:

Interviewer: Was there any concept of looking at each other's code or doing code reviews?

Thompson: [Shaking head] We were all pretty good coders.

The implication: only bad coders need code reviews.

This is an outdated attitude towards software development. Unfortunately, I've encountered this attitude among some software professionals that I've worked with (outside of SAS as well as within). Why do some programmers (especially those from the "good ol' days") place little value on code reviews?

Perhaps peer code reviews were less important 30 years ago. The constraints around running code were much tighter. There were fewer layers in the stack where bugs could lurk. Memory was scarce, instruction sets were limited. Computer applications were like my 1973 AMC Gremlin: fewer moving parts, so it was easier to see which parts weren't working correctly and predict when they would fail. (Most often, it was the carburetor -- another outmoded concept.)

Also, the discipline of programming was newer back then. Perhaps there was less variability among the approaches you could use to solve a problem with code. If you had the skills to code the solution, maybe your solution would not differ much from that of any other similarly skilled colleague.

Well, those days are gone. Now more than ever, we need code reviews to be a formal part of how we work as SAS professionals (and as software professionals in general). Mark Chu-Carroll spells out many of the reasons in his blog post, Things Everyone Should Do: Code Review. As Mark says, it's not about the problems that a reviewer will catch; it's more about the programmer who prepares to have his/her work reviewed. Knowing that you have to explain this to another person, that someone else will be looking at your work...that can help to keep you on your toes.

In addition to reasons that Mark cites, those of us who work with SAS have a special motivation: we need to pass on the knowledge of a rich legacy code base to our younger workers. And because the SAS language expands with each new release, old-time SAS programmers need exposure to people who can apply new techniques to old problems. Code reviews are one way to help make that happen: improve quality, ensure continuity, and keep it fresh. Who can argue against that?

About a year ago (wow, has it been that long?), I posted an example program that lets you report on the contents of a SAS information map. Using my example, you can see the data items, filters, and folder structure within a given information map.

Last week a reader posted a comment, wondering how to also learn the names of the SAS tables that contribute to the SAS information map's definition. I know that you can't get to that information using the information map dictionary tables, but I did learn that it is possible using PROC INFOMAPS and the LIST statement.

Here is an example that relates to the information map that I reported on earlier:

proc infomaps ;
update infomap "Cars" mappath="/Shared Data/Maps";
list datasources;
run;

The output goes to the SAS log:

Total datasources: 1
Data source: Sample Data.CARS_1993
ID: CARS_1993
Name: CARS_1993
Description:

Wow, I guess that was a pretty simple information map, with only one data source. I'm sure that you can come up with more complex examples.

SAS-based processes are critical to many organizations, but sometimes the trickiest part of your job falls into one or both of these activities:
• Getting stuff from the outside world "into" SAS. (Once it's in SAS, as many of you know, the world is your oyster.)
• Getting the output of your SAS process "out" to the non-SAS people who need to consume it.
Here's a handy DATA step program that can copy file content from one place to another. It copies the content byte-by-byte, so it's great for copying files from your SAS session to a place outside of SAS, or vice versa.

/* these IN and OUT filerefs can point to anything */
filename in "c:\dataIn\input.xlsx";
filename out "c:\dataOut\output.xlsx";

/* copy the file byte-for-byte  */
data _null_;
length filein 8 fileid 8;
filein = fopen('in','I',1,'B');
fileid = fopen('out','O',1,'B');
rec = '20'x;
rc = fget(filein,rec,1);
rc = fput(fileid, rec);
rc =fwrite(fileid);
end;
rc = fclose(filein);
rc = fclose(fileid);
run;

filename in clear;
filename out clear;

It's true that you can copy disk-based files from one place to another by using operating system shell commands (via SYSTASK, for example). But the cool thing about the above program is that it can copy files to/from other places as well -- any location that you can access with a FILENAME statement, including URLs. For example, imagine that there is a file on the Web that you want to bring into SAS for analysis. Simply use FILENAME URL to define the IN fileref. Here's an example that grabs an Excel file from the Web and imports it into SAS:

filename in url "http://www.LotsOfData.org/data/data.xlsx"
/* PROXY= is important for going outside firewall, if you have one */
/* proxy="http://yourProxy.company.com" */
;
filename out "c:\temp\data.xlsx";

data _null_;
length filein 8 fileid 8;
filein = fopen('in','I',1,'B');
fileid = fopen('out','O',1,'B');
rec = '20'x;
rc = fget(filein,rec,1);
rc = fput(fileid, rec);
rc =fwrite(fileid);
end;
rc = fclose(filein);
rc = fclose(fileid);
run;

/* Works on 32-bit Windows */
/* If using 64-bit SAS, you must use DBMS=EXCELCS */
PROC IMPORT OUT= WORK.test