0

I'm trying to write robust code to assign values to macro variables. I want the names of the macro variables to depend on values coming from the variable 'subgroup'. So subgroup could equal 1, 2, or 45 etc. and thus have macro variable names trta_1, trta_2, trt_45 etc.

Where I am having difficulty is calling the macro variable name. So instead of calling e.g. &trta_1 I want to call &trta_%SCAN(&subgroups, &k), which resolves to trta_1 on the first iteration. I've used a %SCAN function in the macro variable name, which is throwing up a warning 'WARNING: Apparent symbolic reference TRTA_ not resolved.'. However, the macro variables have been created with values assigned.

How can I resolve the warning? Is there a function I could run with the %SCAN function to get this to work?

data data1 ;
  input subgroup trta trtb ;
  datalines ;
  1 30 58
  2 120 450
  3 670 3
run;

%LET subgroups = 1 2 3 ;
%PUT &subgroups;

%MACRO test;
  %DO k=1 %TO 3;

    DATA test_&k;
      SET data1; 
      WHERE subgroup = %SCAN(&subgroups, &k);
      CALL SYMPUTX("TRTA_%SCAN(&subgroups, &k)", trta, 'G');
      CALL SYMPUTX("TRTB_%SCAN(&subgroups, &k)", trtb, 'G'); 
    RUN;  

    %PUT "&TRTA_%SCAN(&subgroups, &k)" "&TRTB_%SCAN(&subgroups, &k)"; 
  %END;

%MEND test;
%test;
Jørgen R
  • 10,568
  • 7
  • 42
  • 59
Dawnoak
  • 25
  • 2
  • 5

2 Answers2

1

Using the structure you've provided the following will achieve the result you're looking for.

data data1;
  input subgroup trta trtb;
  datalines;
1 30 58
2 120 450
3 670 3
;
run;

%LET SUBGROUPS = 1 2 3;
%PUT &SUBGROUPS;

%MACRO TEST;
  %DO K=1 %TO 3;

    %LET X = %SCAN(&SUBGROUPS, &K) ;

    data test_&k;
      set data1; 
      where subgroup = &X ;
      call symputx(cats("TRTA_",&X), trta, 'g');
      call symputx(cats("TRTB_",&X), trtb, 'g'); 
    run;  

    %PUT "&&TRTA_&X" "&&TRTB_&X"; 
  %END;
%MEND TEST;
%TEST;

However, I'm not sure this approach is particularly robust. If your list of subgroups changes you'd need to change the 'K' loop manually, you can determine the upper bound of the loop by dynamically counting the 'elements' in your subgroup list.

If you want to call the macro variables you've created later in your code, you could a similar method.

data data2;
  input subgroup value;
  datalines;
1  20
2  25
3  15
45 30
  ;
run ;


%MACRO TEST2;
  %DO K=1 %TO 3;

    %LET X = %SCAN(&SUBGROUPS, &K) ;

    data data2 ;
      set data2 ;
      if subgroup = &X then percent = value/&&TRTB_&X  ;
      format percent percent9.2 ;      
    run ;
  %END;
%MEND TEST2;

%TEST2 ;

Effectively, you're re-writing data2 on each iteration of the loop.

AJ7
  • 36
  • 3
  • That would work. However, what I should have said is that I want to use the macro variables outside of that macro, so I want to be able to call &trta_1 or &trta_2 or &trtb_3 or &trtb_45 etc in my code elsewhere. In later code I'm saying, for example, if subgroup=45 then percent=value/&trtb_45, only I'm using %scan(&subgroups, &k) so that I'm not explicitly stating trtb_45, as it may in another re-run be trtb_309. – Dawnoak Sep 10 '14 at 13:24
  • 1
    Edited my answer. Just want to stress there's almost certainly a better way to approach your problem, but without knowing exactly what you're doing it's difficult to advise further. – AJ7 Sep 10 '14 at 14:04
  • Yeah, I think you're right there. I can do it if I create a macro variable using the %let statement later on in my code where I'm calculating percentage. It gets around using the %SCAN function within my macro variable name. Perhaps later on I can figure out how to do it using the %scan function, but for now I am happy with your suggestion - thanks! – Dawnoak Sep 10 '14 at 14:22
  • Agree with the comment "there's almost certainly a better way to approach your problem". This is a good example of where the macro language is the wrong answer. – Joe Sep 10 '14 at 14:45
0

This should cover your requirements. You can load and unload an array of macro variable without a macro. I have included an alternate method of unloading a macro variable array with a macro for comparison.

Load values into macro variables including Subgroup number within macro variable name e.g. TRTA_45.

data data1;
    input subgroup trta trtb;

    call symput ('TRTA_'||compress (subgroup), trta);
    call symput ('TRTB_'||compress (subgroup), trtb);

    datalines;
    1 30 58
    2 120 450
    3 670 3
    45 999 111
    ;
run;

No need for macro to load or refer to macro variables.

%put TRTA_45: &TRTA_45.;

%let Subgroup_num = 45;

%put TRTB__&subgroup_num.: &&TRTB_&subgroup_num.;

If you need to loop through the macro variables then you can use Proc SQL to generate a list of subgroups.

proc sql noprint;
select  subgroup
,   count (*)   
into    :subgroups separated by ' '
,   :No_Subgroups
from    data1
;
quit;

%put Subgroups: &subgroups.;

%put No_Subgroups: &No_Subgroups.;

Use a macro to loop through the macro variable array and populate a table.

%macro subgroups;

data subgroup_data_macro;

%do i = 1 %to &no_subgroups.;
    %PUT TRTA_%SCAN(&subgroups, &i ): %cmpres(&TRTA_%SCAN(&subgroups, &i ));
    %PUT TRTB_%SCAN(&subgroups, &i ): %cmpres(&TRTB_%SCAN(&subgroups, &i ));

    subgroup = %SCAN(&subgroups, &i );

    TRTA    = %cmpres(&TRTA_%SCAN(&subgroups, &i ));
    TRTB    = %cmpres(&TRTB_%SCAN(&subgroups, &i ));    

    output;
%end;
run;
%mend subgroups;

%subgroups;

Or use a data step (outside a macro) to loop through the macro variable array and populate a table.

data subgroup_data_sans_macro;

do i = 1 to &no_subgroups.;
    subgroup = SCAN("&subgroups", i );

    TRTA = input (symget (compress ('TRTA_'||subgroup)),20.);
    TRTB = input (symget (compress ('TRTB_'||subgroup)),20.);

    output; 
end;
run;    

Ensure both methods (within and without a macro) produce the same result.

proc compare 
base = subgroup_data_sans_macro
compare = subgroup_data_macro
;
run;    
Chris
  • 26
  • 1