Pages

SyntaxHighlighter

Wednesday, April 4, 2012

SAS Explorer Detailed View

The default display of the SAS Explorer is to show large icons as is shown below.
My preference is to display the same content as smaller images with more information such as is shown below.  In order for this to happen, consider adding the following display manager (dm) command to your autoexec.sas file:

dm "explorer 1; details on" ; *-- set explorer to detailed view;

Friday, March 23, 2012

SAS Catalogs


SAS catalogs are heavily underutilized in my experience. Catalogs are analogous to a Zip file and are used to store formats, macros, SCL, graphics and more in catalog entries based on its type. Because catalogs are part of the SAS/BASE product, it is also a great way to store dynamically generated code then call it later on. Another benefit of using SAS catalogs to store information is that it is portable and does not have dependencies on OS storage devices that can change (e.g. does everyone have a c:\temp\ folder?).

A catalog is fully identified by a four-level name such as libref.catalog.entry-name.entry-type:

libref - the library that will store the catalog (use WORK for throw away entries)
catalog - name of the catalog (think of it as a zip file)
entry-name - name of the catalog entry
entry-type - types include SOURCE, LOG, OUTPUT, FORMAT, MACRO, CATAMS, SCL, GRSEG

The below code uses the disposable WORK libref to dynamically write out and then execute SOURCE code using both the full four-level name and the abbreviated version.

filename fullname catalog "work.catalog.mycode1.source" ;
filename abbrev   catalog "work.catalog" ;

data _null_   ;
  file fullname ;
  put "data _null_ ;"
    / "  put 'Hello from fullname' ;"
    / "run ;" ;
  file abbrev(mycode2.source) ;
  put "data _null_ ;"
    / "  put 'Hello from abbrev' ;"
    / "run ;" ;
run ;

%inc fullname ;
%inc abbrev( mycode2.source ) ;

filename fullname clear ;
filename abbrev clear ;

Reference: Using a SAS Catalog to Develop and Manage a SAS Project, by David D Chapman

Friday, March 2, 2012

Adding custom tips to GTL output

The default behavior for mouse over events is to show only the values being plotted. I contacted SAS tech support to find out how to display other variables in the output. The key is to use the rolename=(tip1=column) tip=(tip1) syntax shown below.

proc template ;
  define statgraph sgplot ;
    begingraph ;
      layout overlay ;
        ModelBand "G63LAMQ2" /  
          display=(outline) OutLineAttrs=GraphPredictionLimits 
          Name="MODELBAND" LegendLabel="95% Prediction Limits" ;
        ModelBand "G63LAMQ3" /  
          Name="MODELBAND1" LegendLabel="95% Confidence Limits" ;
        ScatterPlot X=Age Y=Weight / 
          primary=true 
          rolename=(tip1=name tip2=sex tip3=age tip4=weight) 
          tip=(tip1 tip2 tip3 tip4) ;
        RegressionPlot X=Age Y=Weight / 
          NAME="REG" LegendLabel="Regression" clm="G63LAMQ3" cli="G63LAMQ2" ;
        DiscreteLegend "MODELBAND" "MODELBAND1" "REG" ;
      endlayout ;
    endgraph ;
  end ;
run ;

ods graphics / imagemap = on ;
ods listing close ;
ods html path='c:\temp' file='tips.html' ;

  proc sgrender data=sashelp.class template=sgplot ;
  run ;

ods html close ;
ods listing ;

Monday, February 27, 2012

getconnection() - macro function


I wanted a way to call a SAS macro as a function to return a streaming value that would be rendered in place. Doing so would by definition eliminate global macro variable collisions.

The below source code does just that using all %local macro variables and only macro code. The returned value (a connection string to a remote database server) is used in place which will eliminate hard coding issues when server definitions changes as they always do over time.

The next image reveals the trusted connection strings used as DSN-less connections followed by the SAS macro function.



proc sql noprint stimer ;
   connect to odbc ( "%getconnection(server=prod)" ) ;
     create table mydsn as
          select       *
            from       connection to odbc 
            (
              select     *
                from     db.schema.table 
            ) ;
   disconnect from odbc ;
quit;


%macro getconnection( server = Prod ) ;
  %local libref
         path 
         closelib 
         rc
         dsid 
         returncolumn
         connection ;

  %let libref       = afutil ;
  %let path         = c:\xport\ ;
  %let closelib     = N ;
  %let returncolumn = connection ;

  %if %sysfunc( libref( &libref. ) ) ne 0 %then %do ;
    %let rc = %sysfunc( libname( &libref., &path. ) ) ;
    %if &rc ne 0 %then %do ;
      %put ERROR: could not create the libname &libref.. ;
      %return ;
    %end ;
    %let closelib = Y ;
  %end ; 

  %let dsid = %sysfunc( open( &libref..servers( where = ( upcase( server ) = upcase( "&server." ) ) ) ) ) ;                                                                          
  %let rc = %sysfunc( fetch( &dsid. ) ) ;
  %if &rc ne 0 %then %do ;
    %put ERROR: no entry found in &libref..servers where upcase( server ) = upcase( "&server." ) ;
    %let rc = %sysfunc( close( &dsid. ) ) ; 
    %return ;
  %end ; 
 
  %let connection = %sysfunc( getvarc( &dsid., %sysfunc( varnum( &dsid., &returncolumn. ) ) ) ) ;                                                                
  %let rc = %sysfunc( close( &dsid. ) ) ; 

  %if &closelib. = Y %then %let rc = %sysfunc( libname( &libref. ) ) ; 

  &connection.    
%mend ;

Monday, February 6, 2012

Moving Average



The below code reveals the concept of calculating a moving average. A _temporary_ array retains its values and is not included in the program data vector (PDV). The use of modulus division is critical in replacing the most recent value with the oldest retained value in the array.

%let rows = 3 ;
data movingaverage ;
  array ave[ &rows ] _temporary_ ; 
  do i = 1 to 5 ;
    subscript = mod( i , &rows ) ; 
    subscript = ifn( subscript, subscript, &rows ) ;
    ave[ subscript ] = i ; 
    if i >= &rows then average = mean( of ave[ * ] ) ;
    output ;
  end ;
run ;

Tuesday, January 24, 2012

It varies



If I extract source code from a SAS catalog entry of type source, all the lines are padded with spaces to the LRECL value which defaults to 128. Therefore, when you copy and paste the source code to print it, the lines come out as double spaced due to the line length.

This is where the $VARYING. format comes into play. In the below code, the input file is read in as $CHAR256. to retain the leading spaces then the line LENGTH() is determined after trailing spaces have been removed with the TRIM() function. That line length value (len) is added to the $VARYING. format which will now keep my leading spaces or indentation and remove the trailing spaces.

The CLIPBRD (clipboard) access method is used so that the contents are written to the computer's clipboard so that the trimmed contents can be pasted to whatever destination you desire.

filename xin catalog "lib.cat.entry.source" ;
filename xout clipbrd ;
data _null_ ;
  infile xin ;
  file xout ;
  input line $char256. ;
  len = length( trim( line ) ) ;
  put line $varying256. len ;
run ;
filename xin clear ;
filename xout clear ;

Thursday, January 5, 2012

What remote libnames are assigned?


I was attempting to upload some SAS formats from my local PC to the SAS server and while there was no error message in the log, the formats did not get updated. I had assumed the APFMTLIB was already assigned but it was not and the way I determined that was via the following code.

signon ;
  rsubmit wait = yes ;

    proc sql ;
      create table onserver as
        select   libname
               , path
          from   dictionary.libnames ;
    quit ;

    proc download
      data = onserver
      out  = localdsn ;
    run ;

  endrsubmit ;
signoff ;