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

I have a macro code that is supposed to loop though different formats and apply those to the x axis in a line plot produced by sgplot

%let p=date wy my;
%let f=date9. weeku5. monyy5.;
%macro trend_perdiod;

                       %do I=1 %to %sysfunc(countw(&p.));

                               %let pp=%scan(&p.,&I.);

                               %let TF=%scan(&f.,&I.);

                               proc means data=Insas.&sn. noprint;

                                class &pp.;

                                freq w;

                                      output out=insas.&pp._&sn. mean=mean;

                               run;

                               *save mean value for time period;

                               proc sql noprint;

                                      select Mean into: Tmean separated by ' '

                                      from insas.&pp._&sn.

                                      where &pp.=.;

                               quit;

                               data insas.&pp._&sn.;

                                      set insas.&pp._&sn.;

                                      where &pp. ne .;

                                      lag_M=lag(mean);

                                      leakage=-(Lag_M-mean)/Lag_M;

                                      format leakage percentn12.2;

                               run;

                               *Plot;

                               Title1 bcolor=gold " &SN. - Average &Pp.";

                               proc sgplot data=insas.&pp._&sn.;

                                      xaxis TICKVALUEFORMAT=&TF.;

                                      series x=&pp. y=mean / markers;

                                      refline &Tmean. /axis=y label=("Avg=&Tmean.");

                                      yaxis grid;

                               run;

                               Title1;

                               proc print data=insas.&pp._&sn.;

                                      var &pp. mean leakage;

                                      format mean 12.2;

                               run;

                       %end;

               %mend trend_perdiod;

               %trend_perdiod;

The issue that I have is that the &TF. macro variable is not correctly resolved and I get the following error: 

ERROR 772-580: Syntax error: expecting a constant or a dynamic.

 

Any idea how to enclose my list of formats so that they are properly applied into the following statement?

xaxis TICKVALUEFORMAT=&TF.

Any help is appreciated! Thanks a lot!

1 ACCEPTED SOLUTION

Accepted Solutions
Astounding
PROC Star

Here's some of the detail about why that would work:

 

https://v8doc.sas.com/sashtml/macro/z514scan.htm

 

By default, %SCAN uses a dot as a delimiter.  So %SCAN is not picking up the dot at the end of the words within &F, when computing a value for &TF.  It's possible this would also work, but I can't test it right now:

 

%scan(&f., &i, )

 

 

View solution in original post

9 REPLIES 9
RW9
Diamond | Level 26 RW9
Diamond | Level 26

My, what a total ballache.  You know, if you did one simple change then that whole block of code would disappear, instead looping over the whole code, process your data once, then call a macro to do the sglpot, (not tested this, just off the top of my head):

proc means data=insas.yourds noprint;
  by pp;
  var date wy my;
  output out=means mean(date)= mean(wy)= mean(my)= / autoname;
run;

data insas.yourdata;
  merge insas.yourds means;
  by pp;
run;

%macro plot (var=,fmt=);

  proc sglpot data=insas.yourds;
    xaxis tickvalueformat=&fmt..;
    series x=&var. ...
    refline &var._mean / ...
  run;

%mend plot;

%plot (var=date,fmt=date9);
%plot (var=wy,fmt=weeku5);
%plot (var=my,fmt=monyy5);

Saying that, you could probably modify the underlying proc template code on that, and not need to use the 3 macro calls at all, but I doubt that will show any less code.

With regards to your question on the error, how can we tell unless you post the relevant log section?  It is telling you there is something wrong, enable debugging - options mlogic mprint symbolgen; - and then look at the log this will tell you what is wrong.  I suspect it maybe the passing of . as well, scan maybe using that as a delimiter.  Drop that from the string then use &tf.. - note the two dots, one to finish the macro variable, one to indicate its a format.  And avoid mixing upper case and lower case it really makes code hard to read (and use the code window - its the {i} above post area) to retain code formatting.

 

Shmuel
Garnet | Level 18

Use options mprint symbolgen; and check the log - what value has &TF.

 

If need more help - please post the whole log.

Kurt_Bremser
Super User

Another reason why working from lists in macro variables is sub-optimal:

%let p=date wy my;
%let f=date9. weeku5. monyy5.;
%macro trend_perdiod;
%do I=1 %to %sysfunc(countw(&p.));
  %let pp=%scan(&p.,&I.);
  %let TF=%scan(&f.,&I.);
  %put pp="&pp.";
  %put tf="&tf.";
%end;
%mend;
%trend_perdiod

data control;
input p $ f $;
cards;
date date9.
wy weeku5.
my monyy5.
;
run;

%macro testmac(p,f);
%put p="&p.";
%put f="&f.";
%mend;

data _null_;
set control;
call execute('%nrstr(%testmac(' !! trim(p) !! ',' !! trim(f) !! '));');
run;

Just compare the logs.

You could make it work by using an artificial delimiter:

%let p=date wy my;
%let f=date9.@weeku5.@monyy5.;
%macro trend_perdiod;
%do I=1 %to %sysfunc(countw(&p.));
  %let pp=%scan(&p.,&I.);
  %let TF=%scan(&f.,&I.,@);
  %put pp="&pp.";
  %put tf="&tf.";
%end;
%mend;
%trend_perdiod

but I really prefer to work off datasets with call execute().

gamotte
Rhodochrosite | Level 12

Hello,

 

keeping the space as delimiter and using %scan(&f.,&I.,' ') or %scan(&f.,&I.,%str( )) also works.

Astounding
PROC Star

Here's some of the detail about why that would work:

 

https://v8doc.sas.com/sashtml/macro/z514scan.htm

 

By default, %SCAN uses a dot as a delimiter.  So %SCAN is not picking up the dot at the end of the words within &F, when computing a value for &TF.  It's possible this would also work, but I can't test it right now:

 

%scan(&f., &i, )

 

 

gamotte
Rhodochrosite | Level 12

Since the compiler ignores spaces, quotes or %str( ) are needed to tell the compiler that the space is significant.

 

114  %let x=a. b. c.;
115
116  %macro test_scan;
117      %do i=1 %to %sysfunc(countw(&x.));
118          %put ---;
119          %let a=%scan(&x.,&i.);
120          %put &=a.;
121
122          %let b=%scan(&x.,&i., );
123          %put &=b.;
124
125          %let c=%scan(&x.,&i.,' ');
126          %put &=c.;
127      %end;
128  %mend;
129
130  %test_scan;
---
A=a
B=a. b. c.
C=a.
---
A=b
B=
C=b.
---
A=c
B=
C=c.
Astounding
PROC Star

Thanks for testing.  What a strange result for &B.  I can't explain it off the top of my head, but I will try running some tests later.

 

Also note that using ' ' as the delimiter does use blanks as a delimiter.  But it also uses single quotes as a delimiter.  For practical purposes that almost never makes a difference.

gamotte
Rhodochrosite | Level 12

Maybe having a void third argument means that no delimiter will be used so we'll always

obtain the entire string with 1 as second argument and an empty string for higher values.

 

The behavior is different with the scan function :

 

197  data _NULL_;
198  str="a b c";
199  first=scan(str,1,'');
200  put first=;
201  run;

first=a
NOTE: DATA statement used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds

Edit : The result above is due to the fact that empty strings in SAS contain one space. I tried the same program, omitting

the third argument. I thought it would result in an error but it produces the same result as its macro counterpart :

 

202  data _NULL_;
203  str="a b c";
204  first=scan(str,1,);
205  put first=;
206  run;

first=a b c
NOTE: DATA statement used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds

 

From the documentation

http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000214639.htm

Syntax

SCAN(string, count<,charlist <,modifiers>>)

Using Null Arguments

The SCAN function allows character arguments to be null. Null arguments are treated as character strings with a length of zero. Numeric arguments cannot be null.


EBB
Obsidian | Level 7 EBB
Obsidian | Level 7

For other people looking for the same answer: It is also working is the . is left out of the list and added when the macro variable is called.

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 9 replies
  • 1382 views
  • 1 like
  • 6 in conversation