1
votes

I am trying to implement a macro which will allow me to run several logistic regression models that have the same outcome but a different main explanatory variable (the covariates would be common for all models) for several datasets. I have written a scan and eval macro that scans two global variables but it's not quite working. The code is shown below:

%let numbers=5 7 8 9 10 12 13 14 16 18 19 24  26 
32 33  35 37  39  41  44 45  48 50 52 
55 56  58  66 67 68 ; 

%let list=voting national local safe street violence say free;

%macro logistic;    
%let j=1;
%let m=1;
%let first=%scan(&list,%eval(&j));
%let second=%scan(&numbers,%eval(&m));
%do %while (&first ne );
%do %while (&second ne );

proc logistic data=socialcapital&second. descending;
model depression= &first. agec married edu inc_2 inc_3 inc_4 inc_5/risklimits;
ods output ParameterEstimates=mv_model1&second._&first.;
run;

%let j=%eval(&j+1);
%let m=%eval(&m+1);
%let first=%scan(&list,%eval(&j));
%let second=%scan(&numbers,%eval(&m)); 
%end;
%end;
run;

%mend;  
%logistic;

The global variable numbers refers to the "socialcaptial" dataset that I am using. Each dataset represents a country and so each number in the "numbers" global variable refers to a dataset. The global variable list refers to the list of main explanatory variables that I want to include in the model, one main explanatory variable per model. What I am looking to get is 8 separate multivariable logistic regression results for each country.

However, it appears that the scan function is not working properly for me so I know that I have done something wrong, but I am not sure what. It seems that the macro assigns 1 variable from &list to 1 dataset from &numbers until it runs out of variables from &list and simply runs the model with just the covariates instead of running all 8 models using the dataset 5, then running all 8 models again using dataset 7, and so forth.

Basically, I have messed up something with the numbering and I am not quite sure how to proceed with this macro. I know that I can get rid of the &numbers global variable by using a "by statement" in proc logistic with a stacked dataset but I would really like to learn how to get this to work for future models where that might not be an option.

3

3 Answers

1
votes

Maggie,

I believe the code below will do what you want. I commented out the LOGISTIC procedure and put in a PUT statement for testing, and it seems to resolve the way that I expect you think it should.

%let numbers=5 7 8 9 10 12 13 14 16 18 19 24  26 
32 33  35 37  39  41  44 45  48 50 52 
55 56  58  66 67 68 ; 

%let list=voting national local safe street violence say free;

%macro logistic;
   %let j=1;
   %let first=%scan(&list,%eval(&j));
   %do %while (&first ne );
      %let m=1;
      %let second=%scan(&numbers,%eval(&m));
      %do %while (&second ne );

        /*
         proc logistic data=socialcapital&second. descending;
         model depression= &first. agec married edu inc_2 inc_3 inc_4 inc_5/risklimits;
         ods output ParameterEstimates=mv_model1&second._&first.;
         run;
        */
         %put J=&j - M=&m - FIRST=&first - SECOND=&second;

         %let m=%eval(&m+1);
         %let second=%scan(&numbers,%eval(&m));
      %end;
      %let j=%eval(&j+1);
      %let first=%scan(&list,%eval(&j));
   %end;
   run;
%mend;

%logistic;
1
votes

Here's another way to do it: (if you end up with NUMBERS and LIST in data sets, we can alter the code to handle that too)

%let numbers=5 7 8 9 10 12 13 14 16 18 19 24  26 
 32 33  35 37  39  41  44 45  48 50 52 
 55 56  58  66 67 68 ; 

%let list=voting national local safe street violence say free;

%macro logistic(First=, Second=);    
 %Put FIRST= &first;
 %Put SECOND= &second;
 /*proc logistic data=socialcapital&second. descending;*/
 /*model depression= &first. agec married edu inc_2 inc_3 inc_4 inc_5/risklimits;*/
 /*ods output ParameterEstimates=mv_model1&second._&first.;*/
 /*run;*/

%mend logistic;  

%Macro Test;
 %do i = 1 %to %sysfunc(countw(&list));
  %Let first=%scan(&list,&i);
  %do j = 1 %to %sysfunc(countw(&numbers));
   %Let second=%scan(&numbers,&j);
   %logistic(First=&first,Second=&second)
  %end;
 %end;
%Mend test;
%test
1
votes

Whoops, small correction. I should have used "numbers" not index "i" below.

You can do this with a macro but you also can do this in either a data step( using call execute ) or in Proc IML(with 9.22 or higher) with submit blocks nested in a loop. To get an idea please see below.

Data _Null_;
 Do numbers = 5, 7, 
                  8 to 10, 
                  12 to 14, 
                  16, 18, 19, 24, 26, 32, 
                  33 to 41 by 2, 
                  44, 45, 48, 50, 52, 55, 56, 58, 
                  66 to 68;  
   Do IndpVar = "voting", "national", "local", "safe", "street", "violence", "say", "free";
       call execute( '%Put '||strip(Indpvar)||strip(put(numbers,best.))||';');
      "Logistic Code Goes Here";
   End;
   End;
 Run;