An Idea Exchange for SAS software and services

Comments
by Frequent Contributor
on ‎08-18-2015 11:59 PM

I've encountered both of these problems, but I wonder if they will be solved in the long run by the new Lua facility. 

by Super User
on ‎08-19-2015 09:49 AM

Sorry, not really following the point here.  Is it not an inherent part of SAS that there is no Object Orientation?  Admittedly, it could be quite useful to refer to a dataset object, e.g. return=Object<sashelp.cars>.Variables, however this can all be pulled directly from vtable/vcolumns.

Maybe I miss something here?

by Frequent Contributor
on ‎08-19-2015 02:21 PM

The data step has objects for ODS, Java, and hashes.  I hadn't thought about the possibility of a new object for data sets, but I don't see why it couldn't happen.  The hash object already reads and writes data sets.  I don't think there's a mechanism for object-oriented programming in the macro language.

Chris@NewZealand wanted two things:

- The ability to create a new data set using functions, something which can't be done now except indirectly through a hash object (and not at all in a general way in the macro language as far as I know).

- The ability to get the variable numbers for variables specified in a keep/drop list, or alternatively to have the numbers returned starting at 1 incremented by 1 regardless of the variable numbers in the original data set.

The example he showed (_NUMERIC_) and some others ( explicit lists, -- lists, _character_, maybe more) could be done with existing functions, but it would be tedious.  You'd have to open the data set twice.  The first time, you would open it with all variables, then read through them to find the ones you want and their variable numbers.  You wouldn't have to read any data.  Then open it a second time with the keep/drop option, and use the list of variable numbers you had just created.

And since there's a limit on the number of variables in a SAS data set, you don't have to try random numbers.  You could start at 1 and go up to 32K, or whatever the current limit is (I think that varies by engine).  You'd have to not mind error messages in the log.

by PROC Star
on ‎08-19-2015 05:19 PM

It seems you have to run a proc to run LUA, so probably not an answer to this issue for now at least. Also objects are not accessible from macros or proc fcmp.

Nasty messages in the log are not really an option, so the only proper way atm is, as you mention, to open the process the information twice: once while parsing the options into a useable list (using VTABLE or not; I try not to as it is slow and not better than opening the dataset itself), and once to get the information. A lot of code for very little benefit considering functions should do this just fine, especially since these functions are not totally consistent atm: when queried the DSID pointer states that it holds 3 variables but that the variable you are after is (implicitly non-existent) variable #5.

And that's just for my point #1.

by Frequent Contributor
on ‎08-19-2015 05:36 PM

It's my understanding that the goal is to have Lua routines usable in the same places as traditional macro routines.  I don't know the schedule for that.  But Lua handles data sets in a different way, and will be more suitable for some uses than traditional macros (and vice versa, no doubt).

DOSUBL and FCMP might help with the second requested item.  I don't think they can be called in a way that would create arbitrary data sets, but I haven't done a lot of thinking on that idea.

by PROC Star
on ‎08-19-2015 05:50 PM

We'll see. No upvotes here in any case so it seems accessing/processing such metadata is not a common pain.

Not that upvoting ideas makes any difference anyway, but at least this forum provides a bit of interesting banter. Smiley Happy

by Super User
on ‎08-20-2015 04:16 AM

"I don't think there's a mechanism for object-oriented programming in the macro language."

This is because the macro language is not an object orientated programming language.  It is a glorified search and replace system, or a code generator if you will.  In and of itself it doesn't interact with the base SAS at all.

"The ability to create a new data set using functions, something which can't be done now except indirectly through a hash object (and not at all in a general way in the macro language as far as I know)."

Why would you need to, can you provide an example of where you need to create a dataset outside of a normal datastep?

"The ability to get the variable numbers for variables specified in a keep/drop list, or alternatively to have the numbers returned starting at 1 incremented by 1 regardless of the variable numbers in the original data set."

Again, can you provide a case in point here, as I don't see why you cannot pull this information out from the metadata tables, if necessary using a sub-query in SQL and monotonic() to give them a number.

I am not against the idea, I am just struggling to see what the benefit is? 

by PROC Star
on ‎08-20-2015 09:34 PM

PROC SQL generates SAS code, so is not useful for the goal here.

The goal is nothing exceptional, just the standard wish to write common reuseable routines. For example we have a bunch of %convert_xxx2yyy macros for easy manipulations of short (thousands max normally) lists.

Here is a recent one to which I'll add the painful handling of variable names the function are helpless to access properly when I find the time:

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

  Name                convert_metadata2list.sas

  ¯¯¯¯

  Description         This macro will read a date set´s metadata and store one variable metadata type in a macro variable.

  ¯¯¯¯¯¯¯¯¯¯¯         Metadata is NAME or TYPE or LENGTH or FORMAT or INFORMAT or LABEL.

                      The list is sorted by variable order in the data set.

                      Optionally, delimiters and quotation characters can be defined.

                      Further help is available as part of the macro

  Used as a function  Yes  

  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

  Calls another macro No  

  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

  Generates SAS code  No  

  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

  Limitations         Dataset options not supported

  ¯¯¯¯¯¯¯¯¯¯¯

*********************************************************************************************************************

Who When        What            

*********************************************************************************************************************

C Graffeuille 2015-06-17  Initial version            

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

%macro convert_metadata2list(help, table=_LAST_, metadata=NAME, dlm=%str( ), quote_char=, is_verbose=0);

  %**************** Help screen *********************;

  %if %length(%superq(help)) %then %do;

    %put %nrstr( );

    %put %nrstr(**********************************************************************************);

    %put %nrstr(* ____________________________________________                                  *);

    %put %nrstr(*  Help screen for macro convert_metadata2list                                  *);

    %put %nrstr(* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯                                  *);

    %put %nrstr(*                                                                                *);

    %put %nrstr(*  This macro will read a data set%'s metadata                                    *);

    %put %nrstr(*     and store one metadata type in a macro variable. *);

    %put %nrstr(*  The list is in the order of the data set variables.                           *);

    %put %nrstr(* *);

    %put %nrstr(* Parameters: *);

    %put %nrstr(* *);

    %put %nrstr(* table=       OPTL Table to read. Data set options accepted. *);

    %put %nrstr(*                   Default=_LAST_ *);

    %put %nrstr(* *);

    %put %nrstr(* metadata=    OPTL metadata to read. Default=NAME *);

    %put %nrstr(*                   Use NAME or TYPE or LENGTH or FORMAT or INFORMAT or LABEL    *);

    %put %nrstr(*                                                                                *);

    %put %nrstr(* dlm=         OPTL Delimiter in list. Default=space. *);

    %put %nrstr(* *);

    %put %nrstr(* quote_char=  OPTL Values can be quoted. Default=none *);

    %put %nrstr(* *);

    %put %nrstr(* is_verbose=  OPTL Write out extra information about the process. Default=0 *);

    %put %nrstr(*                       Set to 1 for more information *);

    %put %nrstr(* *);

    %put %nrstr(* Examples: *);

    %put %nrstr(* *);

    %put %nrstr(*   %put Names=%convert_metadata2list%( table    = SASHELP.CLASS                  *);

    %put %nrstr(*                                    , metadata = NAME                           *);

    %put %nrstr(*                                    , dlm      = %%str(Smiley Wink *);

    %put %nrstr(*                                    , quote_char= %%str(%%%')  %);                  *);

    %put %nrstr(* *);

    %put %nrstr(*   Names='Name';'Sex';'Age';'Height';'Weight'                                   *);

    %put %nrstr(* *);

    %put %nrstr(* *);

    %put %nrstr(*   data _null_; *);

    %put %nrstr(* LIST= "%convert_metadata2list( table = SASHELP.CLASS, metadata = TYPE )";  *);   

    %put %nrstr(*     put LIST=; *);

    %put %nrstr(* run; *);

    %put %nrstr(* *);

    %put %nrstr(*   LIST=C C N N N *);

    %put %nrstr(* *);

    %put %nrstr(**********************************************************************************);

    %put %nrstr( );

    %return;

  %end;

  %**************** Init *********************;

  %local dsid nvars varnb list value rc ;

  %if        %qupcase(%superq(metadata)) eq TYPE     %then %let metadata=vartype ;

  %else %if  %qupcase(%superq(metadata)) eq LENGTH   %then %let metadata=varlen ;

  %else %if  %qupcase(%superq(metadata)) eq FORMAT   %then %let metadata=varfmt ;

  %else %if  %qupcase(%superq(metadata)) eq INFORMAT %then %let metadata=varinfmt;

  %else %if  %qupcase(%superq(metadata)) eq LABEL    %then %let metadata=varlabel;

  %else                                                    %let metadata=varname ;

  %if %superq(is_verbose) ne 1 %then %let is_verbose=0;

  %**************** Open table *********************;

  %let dsid=%sysfunc(open(%superq(table)));

  %if &dsid =0 %then %do;

    %put %sysfunc(repeat(#,80));

    %put Error opening %superq(table);

    %put %sysfunc(repeat(#,80));

    %return;

  %end;

  %**************** Build list *********************;

  %let nvars=%sysfunc(attrn(&dsid,nvars));

  %if &nvars %then %do varnb =1 %to &nvars;

    %let value=%qsysfunc(trim(%qsysfunc(&metadata(&dsid,&varnb))));

    %if &is_verbose %then %put varnb=&varnb value=%superq(value);

    %let sep=%sysfunc(ifc(&varnb=1,,%superq(dlm)));

    %if &varnb > 1 and %length(%superq(sep))=0 %then %let sep=%str( );

    %let list=%unquote(%superq(list))%superq(sep)%superq(quote_char)%superq(value)%superq(quote_char);

  %end;

  %**************** Term *********************;

  %let rc=%sysfunc(close(&dsid));

  %put ********** &nvars value%sysfunc(ifc(&nvars>1,s,)) found reading %superq(table) *********;

  %**************** Write out list *********************;

  %superq(list)

%mend;

******** THESE DON´T WORK ******;

*data T;

*  A="%convert_metadata2list( table=sashelp.class(keep=_character_) )";

*  put A=;

*run;

            

%*put %convert_metadata2list( table=sashelp.class(keep=_numeric_) );

by Super User
on ‎08-21-2015 04:21 AM

Yes, prime example of why I dislike macro language at all.  Back <= version 6 macro code was a way of working round the limits and even now its still in wide use.  With the code above, once you let the VCS take care of the header, the documentation take car of the help section, all you are actually left with is one datastep:

data _null_;

     set sashelp.vcolumn (where=(libname="xyz" and memname="abc")) end=last;

     retain final;

     length final $2000;

     final=catx(",",final,type);  /* Maybe with your options you would just need to change type here */

     if last then call symputx('list',final);

run;

by Frequent Contributor
on ‎08-21-2015 10:47 AM

The macro language been more than a simple text generator for a long time, unless you take the approach that base SAS itself is just a text generator - logs, lists, they're just text, right?  Except ODS, which produces pictures along with text.

Idea Statuses
Top Liked Authors