BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
Winnie1
Fluorite | Level 6

Hello, can anyone help check the following Macro loop code? I cannot find anything wrong. But it doesn't give me anything after I run the code?  Any help will be really appreciated. 

 

%macro loop(year, nfirms);
/* Loop by firm &i for year &year. This will generate a dataset of firm i-j AcctComp for each year. */

data temp1a; set temp1; if year(datadate1)=&year;
proc sort; by gvkey1;

data nfirms; set temp1a;
keep gvkey1;
proc sort nodupkey; by gvkey1;

data nfirms; set nfirms; id = _N_;

data temp1a; merge temp1a nfirms; by gvkey1;

%do i= 1 %to &nfirms;

data est1; set temp1a;
proc sort nodupkey; by gvkey1;

data est2; set temp1a; if id = &i;

PROC SQL;
CREATE TABLE est2 AS SELECT
est2.*, est1.gvkey1 AS gvkey_j, est1.a_i AS a_j, est1.b_i AS b_j
FROM est2 LEFT JOIN est1
ON est2.gvkey1 ~= est1.gvkey1
AND est2.sic2 = est1.sic2;
QUIT;

data est2; set est2;
a_dif = a_i - a_j;
b_dif = b_i - b_j;
drop a_i a_j b_i b_j;

proc sort; by gvkey1 datadate1 fqenddt1 datacqtr1 gvkey_j;

data est2; set est2;

proc sort; by gvkey1 datadate1 fqenddt1 datacqtr1 gvkey_j fqenddt;

data est2; set est2;
error = ABS(a_dif + bhr * b_dif);

proc sort; by gvkey1 datadate1 gvkey_j fqenddt;

PROC UNIVARIATE NOPRINT; by gvkey1 datadate1 gvkey_j;
VAR error ;
OUTPUT OUT = univ N=n MEAN = me_error ;

data univ; set univ;
acctcomp = -1 * me_error*100;
acctcomp = round(acctcomp, 0.001);

rename gvkey1 = gvkey_i;
rename datadate1 = datadate_i;

if not missing (acctcomp);
if 14 <= n <= 16;

drop me_error n;

PROC APPEND BASE = final data = univ; RUN;

%end ;

data QDC.acctcomp&year; set final;

proc sort; by gvkey_i datadate_i gvkey_j;

%mend loop;

1 ACCEPTED SOLUTION

Accepted Solutions
jimbarbour
Meteorite | Level 14

Yes.

 

The code between %Macro and %Mend doesn't actually execute until you call it.  You have to call the macro with a % sign and then the name of the macro or it won't do anything.  

 

It should generally look something like this.  The values 2019 and 5 are just examples.  Of course you should use values that make sense for what you're doing.

%MACRO Loop(year, nfirms);
    ...macro code goes here...
%MEND Loop;

%Loop(2019, 5);

Jim

View solution in original post

16 REPLIES 16
jimbarbour
Meteorite | Level 14

Hi, @Winnie1,

 

Would it be possible for you to post the log?  If you can post log, please use the "insert code" option.

jimbarbour_0-1601691460842.png

It's much easier to read that way.

 

Thank you,

 

Jim

Winnie1
Fluorite | Level 6
1386  %macro loop(year, nfirms);
1387  /* Loop by firm &i for year &year. This will generate a dataset of
1387! firm i-j AcctComp for each year. */
1388
1389  data temp1a; set temp1; if year(datadate1)=&year;
1390  proc sort; by gvkey1;
1391
1392  data nfirms; set temp1a;
1393  keep gvkey1;
1394  proc sort nodupkey; by gvkey1;
1395
1396  data nfirms; set nfirms; id = _N_;
1397
1398  data temp1a; merge temp1a nfirms; by gvkey1;
1399
1400  %do i= 1 %to &nfirms;
1401
1402  data est1; set temp1a;
1403  proc sort nodupkey; by gvkey1;
1404
1405  data est2; set temp1a; if id = &i;
1406
1407  PROC SQL;
1408      CREATE TABLE est2 AS SELECT
1409      est2.*, est1.gvkey1 AS gvkey_j, est1.a_i AS a_j, est1.b_i AS b_j
1410      FROM est2 LEFT JOIN est1
1411      ON est2.gvkey1 ~= est1.gvkey1
1412      AND est2.sic2 = est1.sic2;
1413  QUIT;
1414
1415  data est2; set est2;
1416  a_dif = a_i - a_j;
1417  b_dif = b_i - b_j;
1418  drop a_i a_j b_i b_j;
1419
1420  proc sort; by gvkey1 datadate1 fqenddt1 datacqtr1 gvkey_j;
1421
1422  data est2; set est2;
1423
1424  proc sort; by gvkey1 datadate1 fqenddt1 datacqtr1 gvkey_j fqenddt;
1425
1426  data est2; set est2;
1427  error = ABS(a_dif + bhr * b_dif);
1428
1429  proc sort; by gvkey1 datadate1 gvkey_j fqenddt;
1430
1431  PROC UNIVARIATE NOPRINT; by gvkey1 datadate1 gvkey_j;
1432  VAR error ;
1433  OUTPUT OUT = univ N=n MEAN = me_error ;
1434
1435  data univ; set univ;
1436  acctcomp = -1 * me_error*100;
1437  acctcomp = round(acctcomp, 0.001);
1438
1439  rename gvkey1 = gvkey_i;
1440  rename datadate1 = datadate_i;
1441
1442  if not missing (acctcomp);
1443  if 14 <= n <= 16;
1444
1445  drop me_error n;
1446
1447  PROC APPEND BASE = final data = univ; RUN;
1448
1449  %end ;
1450
1451  data QDC.acctcomp&year; set final;
1452
1453  proc sort; by gvkey_i datadate_i gvkey_j;
1454
1455  %mend loop;
ballardw
Super User

One suggestion:

1407  PROC SQL;
1408      CREATE TABLE est2 AS SELECT
1409      est2.*, est1.gvkey1 AS gvkey_j, est1.a_i AS a_j, est1.b_i AS b_j
1410      FROM est2 LEFT JOIN est1
1411      ON est2.gvkey1 ~= est1.gvkey1
1412      AND est2.sic2 = est1.sic2;
1413  QUIT;

has at least a chance of being a problem.

When I run a simpler program I get this warning:

9    proc sql;
10      create table work.class as
11      select *
12      from work.class
13      ;
WARNING: This CREATE TABLE statement recursively references the
         target table. A consequence of this is a possible data
         integrity problem.
NOTE: Table WORK.CLASS created, with 19 rows and 5 columns.

14   quit;

General style notes when working with macro code: Always use a run, or quit, to end a procedure or data step.

The nature of the macro language makes it very possible to generate code that does not end steps at the time you expect them too otherwise.

 

Do not reuse data set names inside a macro. When you reuse the same name then you cannot tell where the contents have problems.

Always explicitly state the input data set by using data=somesetname on procedures. It is very easy to have a macro code branch execute and you forgot which set was intended to be sorted leaving 1) the proper set unsorted and 2) possibly place the wrong order on another set or 3) using some other proc on the wrong set.

 

Any time you use constructs like

1398  data temp1a; merge temp1a nfirms; by gvkey1;

or Set, Modify or Update as well as merge, you completely replace the input data set. So logic errors can make it impossible to tell whether a set has bad values before or after the merge (or set ).

 

In this block of code:

1415  data est2; set est2;
1416  a_dif = a_i - a_j;
1417  b_dif = b_i - b_j;
1418  drop a_i a_j b_i b_j;
1419
1420  proc sort; by gvkey1 datadate1 fqenddt1 datacqtr1 gvkey_j;
1421
1422  data est2; set est2;
1423
1424  proc sort; by gvkey1 datadate1 fqenddt1 datacqtr1 gvkey_j fqenddt;
1425
1426  data est2; set est2;
1427  error = ABS(a_dif + bhr * b_dif);

Lines 1420  through 1422 do, for practical purposes, NOTHING.

When you sort data without a need to use by processing, as the three data steps do, then you are wasting potentially lots of time for no purpose.

 

The whole bit can be accomplished with

data est2; 
   set est2;
   a_dif = a_i - a_j;
   b_dif = b_i - b_j;
   drop a_i a_j b_i b_j;
   error = ABS(a_dif + bhr * b_dif);
run;

General rule of thumb when you find that you need a new variable: go back to a previous data step and add the code there, don't run a data step to add one variable.

 

And the Log should have included:

The statement that calls the macro

Any messages,  notes or errors that appear from running the macro.

The Option Mprint, in addition to providing details of the code generated the call will place the notes and messages in relation to the code that creates them.

jimbarbour
Meteorite | Level 14

Thank you, @ballardw 

 

These things in particular, @Winnie1, would be very helpful as I try to determine what is wrong:


@ballardw wrote:

the Log should have included:

The statement that calls the macro

Any messages,  notes or errors that appear from running the macro.

The Option Mprint, in addition to providing details of the code generated the call will place the notes and messages in relation to the code that creates them.


Jim

Winnie1
Fluorite | Level 6

Thank you, @jimbarbour. I have posted the log. There is no error in the log. 

jimbarbour
Meteorite | Level 14

@Winnie1,

 

Thank you for posting the log.  Can you re-run with these options in effect:

OPTIONS 	MCOMPILENOTE=NOAUTOCALL;
OPTIONS	SYMBOLGEN;
OPTIONS	MPRINT;

I'd like to see what the SAS macro facility is seeing.

 

Before you re-run, you might want to try something really basic:  Close your session and re-open it.  Don't do this if you have work files that have taken you hours to build, but if you can re-create your work files quickly, then close and re-open your session before re-trying.

 

And can you tell me a few more things?

 

How is the macro being called?  Can you post the calling code?  

 

What is the value of nfirms and how is it being set?  What year(s) are you running for?

%macro loop(year, nfirms);

 

How many records are in temp1?  What is the distribution of years?   

data temp1a; set temp1; if year(datadate1)=&year;

Jim

Winnie1
Fluorite | Level 6

Hi Jim,

Thank you so much for your help. I re ran your code. But it didn't work. It didn't show any result. There are 236,480 records in the dataset and it is from 2014-2019. nfirms is based on the pair of the firm i and j. Is there anyway we can post the dataset so you may understand it better? 

jimbarbour
Meteorite | Level 14

Thank you @Winnie1 ,

 

You can post a few records by exporting them to Excel and then attaching it to a reply.

 

Can you post the log again?  The OPTION statements should have caused SAS to produce diagnostic messages.  I want the macro but also the code immediately following the macro.  There should be compile and execution log statements.

 

At a minimum, I'm looking for this and what follows it:

NOTE: The macro LOOP completed compilation without errors.
      29 instructions 1784 bytes.

Jim

Winnie1
Fluorite | Level 6
152  %macro loop(year, nfirms);
153  OPTIONS     MCOMPILENOTE=NOAUTOCALL;
154  OPTIONS SYMBOLGEN;
155  OPTIONS MPRINT;
156  /* Loop by firm &i for year &year. This will generate a dataset of
156! firm i-j AcctComp for each year. */
157
158  data temp1a; set temp1; if year(datadate1)=&year;
159  proc sort; by gvkey1;
160
161  data nfirms; set temp1a;
162  keep gvkey1;
163  proc sort nodupkey; by gvkey1;
164
165  data nfirms; set nfirms; id = _N_;
166
167  data temp1a; merge temp1a nfirms; by gvkey1;
168
169  %do i= 1 %to &nfirms;
170
171  data est1; set temp1a;
172  proc sort nodupkey; by gvkey1;
173
174  data est2; set temp1a; if id = &i;
175
176  PROC SQL;
177      CREATE TABLE est2 AS SELECT
178      est2.*, est1.gvkey1 AS gvkey_j, est1.a_i AS a_j, est1.b_i AS b_j
179      FROM est2 LEFT JOIN est1
180      ON est2.gvkey1 ~= est1.gvkey1
181      AND est2.sic2 = est1.sic2;
182  QUIT;
183
184  data est2; set est2;
185  a_dif = a_i - a_j;
186  b_dif = b_i - b_j;
187  drop a_i a_j b_i b_j;
188
189  proc sort; by gvkey1 datadate1 fqenddt1 datacqtr1 gvkey_j;
190
191  data est2; set est2;
192
193  proc sort; by gvkey1 datadate1 fqenddt1 datacqtr1 gvkey_j fqenddt;
194
195  data est2; set est2;
196  error = ABS(a_dif + bhr * b_dif);
197
198  proc sort; by gvkey1 datadate1 gvkey_j fqenddt;
199
200  PROC UNIVARIATE NOPRINT; by gvkey1 datadate1 gvkey_j;
201  VAR error ;
202  OUTPUT OUT = univ N=n MEAN = me_error ;
203
204  data univ; set univ;
205  acctcomp = -1 * me_error*100;
206  acctcomp = round(acctcomp, 0.001);
207
208  rename gvkey1 = gvkey_i;
209  rename datadate1 = datadate_i;
210
211  if not missing (acctcomp);
212  if 14 <= n <= 16;
213
214  drop me_error n;
215
216  PROC APPEND BASE = final data = univ; RUN;
217
218  %end ;
219
220  data QDC.acctcomp&year; set final;
221
222  proc sort; by gvkey_i datadate_i gvkey_j;
223
224  %mend loop;
NOTE: The macro LOOP completed compilation without errors.
      30 instructions 1888 bytes.

jimbarbour
Meteorite | Level 14

Excellent!  👍👍

 

Thank you, @Winnie1.

 

I need just a bit more from your log, please.  The three OPTION statements are diagnostic in nature.  They'll make certain things show up in the log.  They won't fix anything, but they'll help us determine what is wrong.

OPTIONS 	MCOMPILENOTE=NOAUTOCALL;
OPTIONS	SYMBOLGEN;
OPTIONS	MPRINT;

 The first option statement produced the statement:

NOTE: The macro LOOP completed compilation without errors.

 

Now, I need to see another say 20 to 30 lines.  I need to see where the LOOP macro is called and what SAS wrote to the log after the call.

 

Jim

Winnie1
Fluorite | Level 6

Hi Jim,

Thank you so much for the help! And thank you for your patience with a new user. Here is all logs I have in log window. The code I got is from website posted by others. I guess it worked with others. 

306  data QDC.temp1;set temp1;run;

NOTE: There were 236480 observations read from the data set WORK.TEMP1.
NOTE: The data set QDC.TEMP1 has 236480 observations and 12 variables.
NOTE: DATA statement used (Total process time):
      real time           0.66 seconds
      cpu time            0.04 seconds


307
308  proc sort data=temp1; by year;run;

NOTE: Input data set is already sorted, no sorting done.
NOTE: PROCEDURE SORT used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds


309
310  %macro loop(year, nfirms);
311  OPTIONS     MCOMPILENOTE=NOAUTOCALL;
312  OPTIONS SYMBOLGEN;
313  OPTIONS MPRINT;
314  /* Loop by firm &i for year &year. This will generate a dataset of
314! firm i-j AcctComp for each year. */
315
316  data temp1a; set temp1; if year(datadate1)=&year;
317  proc sort; by gvkey1;
318
319  data nfirms; set temp1a;
320  keep gvkey1;
321  proc sort nodupkey; by gvkey1;
322
323  data nfirms; set nfirms; id = _N_;
324
325  data temp1a; merge temp1a nfirms; by gvkey1;
326
327  %do i= 1 %to &nfirms;
328
329  data est1; set temp1a;
330  proc sort nodupkey; by gvkey1;
331
332  data est2; set temp1a; if id = &i;
333
334  PROC SQL;
335      CREATE TABLE est2 AS SELECT
336      est2.*, est1.gvkey1 AS gvkey_j, est1.a_i AS a_j, est1.b_i AS b_j
337      FROM est2 LEFT JOIN est1
338      ON est2.gvkey1 ~= est1.gvkey1
339      AND est2.sic2 = est1.sic2;
340  QUIT;
341
342  data est2; set est2;
343  a_dif = a_i - a_j;
344  b_dif = b_i - b_j;
345  drop a_i a_j b_i b_j;
346
347  proc sort; by gvkey1 datadate1 fqenddt1 datacqtr1 gvkey_j;
348
349  data est2; set est2;
350
351  proc sort; by gvkey1 datadate1 fqenddt1 datacqtr1 gvkey_j fqenddt;
352
353  data est2; set est2;
354  error = ABS(a_dif + bhr * b_dif);
355
356  proc sort; by gvkey1 datadate1 gvkey_j fqenddt;
357
358  PROC UNIVARIATE NOPRINT; by gvkey1 datadate1 gvkey_j;
359  VAR error ;
360  OUTPUT OUT = univ N=n MEAN = me_error ;
361
362  data univ; set univ;
363  acctcomp = -1 * me_error*100;
364  acctcomp = round(acctcomp, 0.001);
365
366  rename gvkey1 = gvkey_i;
367  rename datadate1 = datadate_i;
368
369  if not missing (acctcomp);
370  if 14 <= n <= 16;
371
372  drop me_error n;
373
374  PROC APPEND BASE = final data = univ; RUN;
375
376  %end ;
377
378  data QDC.acctcomp&year; set final;
379
380  proc sort; by gvkey_i datadate_i gvkey_j;
381
382  %mend loop;
NOTE: The macro LOOP completed compilation without errors.
      30 instructions 1888 bytes.
383  run;

Reeza
Super User

Where are you actually using the macro though? Where’s the macro call?

 

We should see a line like this that calls the macro, so far you’ve just shown the definition. 

 

%loop(2019, 85);

Winnie1
Fluorite | Level 6

So to call Macro, I should add this line first: %loop(year, nfirms), is that right?

jimbarbour
Meteorite | Level 14

Yes.

 

The code between %Macro and %Mend doesn't actually execute until you call it.  You have to call the macro with a % sign and then the name of the macro or it won't do anything.  

 

It should generally look something like this.  The values 2019 and 5 are just examples.  Of course you should use values that make sense for what you're doing.

%MACRO Loop(year, nfirms);
    ...macro code goes here...
%MEND Loop;

%Loop(2019, 5);

Jim

SAS Innovate 2025: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 16 replies
  • 1406 views
  • 16 likes
  • 4 in conversation