0

I know I can use call execute to create and execute multiple data steps. But is there a way to generate code for one singular data step, with many repetitive lines of code?

In R for instance I can create a vector of variables, executing some paste/print statement and get something approximating the output I need. As follows:

    strings<-c("Exkl_UtgUtl_Flyg", 
                      "Exkl_UtgUtl_Tag", 
                       "Exkl_UtgUtl_Farja", 
                       "Exkl_UtgUtl_Hyrbil", 
                       "Exkl_UtgUtl_Bo", 
                       "Exkl_UtgUtl_Aktiv", 
                       "Exkl_UtgUtl_Annat")

first_string<-strings[1]

other_strings<-strings[strings!=first_string]

gsub("\t", "",gsub("\n","",gsub(",","",paste0(
    paste0("DATA IBIS3_5;
      Set IBIS3_5;
      if ",first_string,"=3 and hjalpvariabel=1 then do;",
           first_string,"=1;",
           paste0(gsub("Exkl_","",first_string),"SSEK_Pers"),"  =   ",paste0(gsub("^Exkl_","",first_string),"SSEK_PPmedel"),";
      end;
      else if ",first_string,"=3 then ",first_string,"=2;"),
    
    paste0("else if ",other_strings,"=3 and hjalpvariabel=1 then do;",
           other_strings,"=1;",
           paste0(gsub("Exkl_","",other_strings),"SSEK_Pers")," =   ",paste0(gsub("^Exkl_","",other_strings),"SSEK_PPmedel"),";
      end;
      else if ",other_strings,"=3 then ",other_strings,"=2;", collapse=","),"run;"))))

I still have to delete the quotes and the bracketed number manually, but that's at least bearable. The final output looks something like this:

     DATA IBIS3_5;      Set IBIS3_5;      
if Exkl_UtgUtl_Flyg=3 and hjalpvariabel=1 then do; Exkl_UtgUtl_Flyg=1;UtgUtl_FlygSSEK_Pers=UtgUtl_FlygSSEK_PPmedel;      end;               else if Exkl_UtgUtl_Tag=3 and hjalpvariabel=1 then do;Exkl_UtgUtl_Tag=1;UtgUtl_TagSSEK_Pers=UtgUtl_TagSSEK_PPmedel;      end;      else if Exkl_UtgUtl_Tag=3 then Exkl_UtgUtl_Tag=2;                  
     else if Exkl_UtgUtl_Farja=3 and hjalpvariabel=1 then do;Exkl_UtgUtl_Farja=1;UtgUtl_FarjaSSEK_Pers=UtgUtl_FarjaSSEK_PPmedel;      end;      else if Exkl_UtgUtl_Farja=3 then Exkl_UtgUtl_Farja=2;      
     else if Exkl_UtgUtl_Hyrbil=3 and hjalpvariabel=1 then do;Exkl_UtgUtl_Hyrbil=1;UtgUtl_HyrbilSSEK_Pers=UtgUtl_HyrbilSSEK_PPmedel;      end;      else if Exkl_UtgUtl_Hyrbil=3 then Exkl_UtgUtl_Hyrbil=2;
     else if Exkl_UtgUtl_Bo=3 and hjalpvariabel=1 then do;Exkl_UtgUtl_Bo=1;UtgUtl_BoSSEK_Pers=UtgUtl_BoSSEK_PPmedel;      end;      else if Exkl_UtgUtl_Bo=3 then Exkl_UtgUtl_Bo=2;                        
     else if Exkl_UtgUtl_Aktiv=3 and hjalpvariabel=1 then do;Exkl_UtgUtl_Aktiv=1;UtgUtl_AktivSSEK_Pers=UtgUtl_AktivSSEK_PPmedel;      end;      else if Exkl_UtgUtl_Aktiv=3 then Exkl_UtgUtl_Aktiv=2;      
     else if Exkl_UtgUtl_Annat=3 and hjalpvariabel=1 then do;Exkl_UtgUtl_Annat=1;UtgUtl_AnnatSSEK_Pers=UtgUtl_AnnatSSEK_PPmedel;      end;      else if Exkl_UtgUtl_Annat=3 then Exkl_UtgUtl_Annat=2;      
    run;

Is there a way of generating this code in SAS, without, having to resort to other programs?

Magnus
  • 728
  • 4
  • 17
  • See this: https://github.com/savian-net/SasTipsTricks#put-statements-vs-macros. Look at PUT statements vs Macros. – AlanC Oct 03 '22 at 12:42
  • I do not see the pattern in the code you posted. Can you explain the pattern? – Tom Oct 03 '22 at 13:11
  • Is the question how to use CALL EXECUTE() to generate a single complete data step where the number of statements you need to generate is determined by the number of observations being read into the data step that is generating the code? If so check out using `_n_=1` to test for the first observation and the `end=` option to set a variable you can test for the last observations. – Tom Oct 03 '22 at 13:13
  • The code that follows the if- and the else if- statements is nearly identical. The name of the UtgUtl_FlygSSEK_Pers-variable can be determined by erasing the Exkl_-part of the first variable and concatenating it with SEK_Pers. The name for the UtgUtl_TagSSEK_PPmedel variable can be determined by erasing the Exkl_part of the first variable and concatenating it with SSEK_PPmedel. – Magnus Oct 03 '22 at 13:18
  • The biggest difference between the statements is that the first is an if-statement and the rest of them are else if-statements. Apart from that, the only difference between them is the variable which is referenced and which the other variable names are based upon. – Magnus Oct 03 '22 at 13:20
  • This seems more like a transpose. Can you show some sample input and expected output data. – Reeza Oct 03 '22 at 15:14
  • 1
    You could look at [this answer](https://stackoverflow.com/questions/25545892/dynamically-call-macro-from-sas-data-step/25552310#25552310), which shows how to create macro calls from a datastep. The concept is also covered in my [presentation on Writing Code With Your Data](https://github.com/snoopy369/presentations/tree/master/Writing%20Code%20With%20Your%20Data). – Joe Oct 03 '22 at 15:19
  • I've managed to find a workaround in R, updating question. – Magnus Oct 04 '22 at 06:04

2 Answers2

2

I do not see the pattern in the code you want to generate so I will leave that part to your imagination. So for the purpose of discussion let's assume you want to generate code like:

var1=var1**2;
var2=var2**2;

You can use conditional logic in the data step that is generating the code via CALL EXECUTE() to generate the beginning (and possible the ending) of the data step you want to generate.

One way is to test when you are on the first (or last) observation.

Example:

data _null_;
  set variables end=eof;
  if _n_=1 then call execute('data want; set have;');
  call execute(cats(variable,'=',variable,'**2;'));
  if eof then call execute('run;');
run;

You could also use that _n_=1 test to determine whether or not you need to generate the ELSE.

  if _n_>1 then call execute(' else ');

Another way is to use a DO loop to read all of the observations.

data _null_;
  call execute('data want; set have;');
  do while(not eof);
    set variables end=eof;
    call execute(cats(variable,'=',variable,'**2;'));
  end;
  call execute('run;');
  stop;
run;

Or if instead of using CALL EXECUTE() you write the code to a file you don't need to worry about generating the beginning or ending of the data step. You can just %INCLUDE the generated code into the middle of a data step.

filename code temp;
data _null_;
  file code;
  set variables end=eof;
  put variable '=' variable '**2;' ;
run;
data want;
  set have;
%include code / source2;
run;
Tom
  • 47,574
  • 2
  • 16
  • 29
0

The below writes the programs to a file then brings the file bqack into SAS for execution. This is how I avoid macros in almost all cases. 13 years of macros and I ended up with 5 ampersands one night. Vowed to fix.:

* USE WHEN NOT DEBUGGING CODE: ;
        * filename TEMP '$MYTEMPFILE';
        * USE WHEN DEBUGGING CODE: ;
        filename TEMP 'c:\temp\Test.sas';

        data _null_ ;
           file TEMP ;
           set DICT end=eof;
           if _n_ = 1 then
              do ;
                 put 'data Africa ; '
                   / ' attrib '
                   ;
              end;
           if label = "" then
              put @12 name @30 'label="XX_' name +(-1) '"' ;
           else
              put @12 name @30 'label="XX_' label +(-1)'"' ;
           if eof then
              do ;
                 put @12';'
                      / ' set sashelp.SHOES;'
                      / 'run;'
                      ;
              end;
        run;

        %include TEMP ;
AlanC
  • 529
  • 3
  • 7
  • Hi Alan! Don't forget that a good answer in Stack Overflow needs text in addition to code, or it's subject to deletion. Thanks!! – Joe Oct 03 '22 at 15:20