0
votes

I'm trying to append a string macro variable to a data set name in SAS. I want to create datasets that read something like work.cps2020jan and work.cps2020feb. But that's not what I am getting. My code:

%macro loop(values); 
 
     %let count=%sysfunc(countw(&values));

     %do i = 1 %to &count;                                           
      %let value=%qscan(&values,&i,%str(,));                                                                                          
      %put &value; 

    data work.cps2020&value.;
        set "A:\cpsb2020&value" ;

    mth = "&value.";

    keep
    PEMLR 
    mth
    ;

    run;

    %end;                                                                                                                      
                                                                                                                              
%mend;
                         
%loop(%str(jan,feb)); 

Running this code results in the following output in the log:

NOTE: There were 138697 observations read from the data set
      A:\cpsb2020jan.
NOTE: The data set WORK.CPS2020 has 138697 observations and 2 variables.
NOTE: The data set WORK.JAN has 138697 observations and 2 variables.
NOTE: DATA statement used (Total process time):
      real time           4.29 seconds
      cpu time            0.20 seconds


feb

NOTE: There were 139248 observations read from the data set
      A:\cpsb2020feb.
NOTE: The data set WORK.CPS2020 has 139248 observations and 2 variables.
NOTE: The data set WORK.FEB has 139248 observations and 2 variables.
NOTE: DATA statement used (Total process time):
      real time           4.44 seconds
      cpu time            0.15 seconds

I don't understand why my macro creates two datasets per loop instead of one dataset per loop called work.cps2020jan and work.cps2020feb. If I change &value. to &i. SAS outputs work.cps20201 and work.cps20202. But that's not what I want.

Any insights?

3

3 Answers

1
votes

The %QSCAN macro function will mask it's result with special invisible (non-printable) characters only visible to the macro processor system.

What happened is that

data work.cps2020&value.;

was seen as

data work.cps2020<mask-character><non-masked part of symbol value><mask-character>;  

during executor processing, which treated the non-printable mask character as a non-syntax token separator, resulting in a DATA statement listing two output tables.

data work.cps2020 jan;

The positions of mask characters in a macro variable can be observed (in the LOG) using %put _user_, or, the actual symbol contents can be captured from a metadata view such as SASHELP.VMACRO or DICTIONARY.MACRO

Let's simplify your macro and add some logging and symbol capture

%macro loop(values); 
  %local count i;

  %let count=%sysfunc(countw(&values));
  %do i = 1 %to &count;
    %let value=%qscan(&values,&i,%str(,));

    %put _user_;                   %*--- log them masks;

    data x&i;                      %* --- symbol capture;
      set sashelp.vmacro;
      where name like '%VALUE%';
      value_hex = put (value,$HEX40.);
    run;

    %* --- do the step that creates two tables;

    data work.cps2020&value.;
      set sashelp.class;
    run;
  %end;
%mend;

options nomprint nosymbolgen nomlogic;

%loop(%str(jan,feb));

proc print data=x1 noobs style(data)=[fontsize=14pt fontfamily="Courier"];
  var value:;
run;

LOG snippet, those little boxes are the special invisible masking characters (I am showing them in image captures because stack overflow / html won't show non-printable characters)

enter image description here

Same LOG text, copy and pasted into Notepad2 show the mask characters as control characters

enter image description here

The Proc PRINT of the captured macro symbol data will expose the hexadecimal masking characters

enter image description here

  • 06 macro %quote start
  • 08 macro %quote end
  • 01 macro %str start
  • 02 macro %str end
  • 1E masked version of comma
0
votes

&value is returned as quoted by %qscan(). Use %scan() instead. Quoted macro variables can sometimes cause issues on resolution when they're used in this way. It's best to only quote them when needed, such as in a %put statement that has a % sign within it.

0
votes

You don't need %qscan(). If the value contained any characters that need macro quoting then they would be invalid for use in a member name anyway. So use %scan() instead.

But when used inside of a macro the tokenizer will sometimes mistakenly see things like xxx&mvar as two tokens even when there are no special characters in &mvar. You can group the value you are generating to work around that.

For example by making a new macro variable

%let dsn=cps2020&value.;
data work.&dsn. ;

Or use the %unquote() function:

data %unquote(work.cps2020&value.);

Or use a name literal:

data work."cps2020&value."n;