I have one macro, can be invoked. The number 10295 is the outcome which is right.
Somehow when I run inside the macro test() below, the macro appears not invoked and complains.
Anyone can help?!
%get_min_st_ind(zz500, 0827, 6, 0.1);
25006 %macro get_min_st_ind(indexout, dt, i_mm_ind, th_pct);
25007 %local _st_ind_lp_off_mm;
25008 %let _st_ind_lp_off_mm=0;
25009 proc sql noprint;
25010 select mm_ind into: _t_mm_ind from &indexout._&dt._mm_ind where i=&i_mm_ind.;
25011 select lp_st_ind into: _t_st_ind from &indexout._&dt._mm_ind where i=&i_mm_ind.;
25012 select lp_end_ind into: _t_end_ind from &indexout._&dt._mm_ind where i=&i_mm_ind.;
25013 quit;
25014
25015 data _index_temp;
25016 set &indexout._&dt._pout_wt(where=(ind between &_t_end_ind. and &_t_mm_ind.));
25017 run;
25018
25019 proc sort data=_index_temp ; by DESCENDING ind; run;quit;
25020
25021 data _index_temp; set _index_temp;
25022 retain index_max index_off_max_max;
25023 if _N_=1 then do;
25024 index_max=&indexout.; index_off_max_max=0;
25025 end;
25026 else do;
25027 if &indexout.>index_max then index_max=&indexout.;
25028 index_off_max=index_max-&indexout.;
25029 if index_off_max>index_off_max_max then index_off_max_max=index_off_max;
25030 end;
25031 run;quit;
25032
25033 /*proc sgplot data=_index_temp;
25034 series x=ind y=index_off_max_max / lineattrs=( color= black thickness=2
25034! pattern=solid);
25035 series x=ind y=index_off_max / lineattrs=( color= blue thickness=2 pattern=solid);
25036 series x=ind y=index_max / lineattrs=( color=red thickness=2 pattern=solid);
25037 series x=ind y=&indexout. / y2axis lineattrs=( color=lime thickness=2
25037! pattern=solid);
25038 refline 0.1/axis=y lineattrs=(color=lime thickness=2 pattern=solid);
25039 run;quit;
25040 proc sort data=_index_temp ; by ind; run;quit;*/
25041
25042 proc sql noprint;
25043 select ind into: _st_ind_lp_off_mm from _index_temp where index_off_max_max<&th_pct. having
25043! &indexout.=max(&indexout.);
25044 quit;
25045
25046 &_st_ind_lp_off_mm
25047 %mend;
25048
25049 %get_min_st_ind(zz500, 0827, 6, 0.1);
MLOGIC(GET_MIN_ST_IND): Beginning execution.
MLOGIC(GET_MIN_ST_IND): Parameter INDEXOUT has value zz500
MLOGIC(GET_MIN_ST_IND): Parameter DT has value 0827
MLOGIC(GET_MIN_ST_IND): Parameter I_MM_IND has value 6
MLOGIC(GET_MIN_ST_IND): Parameter TH_PCT has value 0.1
MLOGIC(GET_MIN_ST_IND): %LOCAL _ST_IND_LP_OFF_MM
MLOGIC(GET_MIN_ST_IND): %LET (variable name is _ST_IND_LP_OFF_MM)
MPRINT(GET_MIN_ST_IND): proc sql noprint;
MPRINT(GET_MIN_ST_IND): select mm_ind into: _t_mm_ind from zz500_0827_mm_ind where i=6;
MPRINT(GET_MIN_ST_IND): select lp_st_ind into: _t_st_ind from zz500_0827_mm_ind where i=6;
MPRINT(GET_MIN_ST_IND): select lp_end_ind into: _t_end_ind from zz500_0827_mm_ind where i=6;
MPRINT(GET_MIN_ST_IND): quit;
NOTE: PROCEDURE SQL used (Total process time):
real time 0.01 seconds
cpu time 0.00 seconds
MPRINT(GET_MIN_ST_IND): data _index_temp;
MPRINT(GET_MIN_ST_IND): set zz500_0827_pout_wt(where=(ind between 7449 and 10747));
MPRINT(GET_MIN_ST_IND): run;
NOTE: There were 3299 observations read from the data set WORK.ZZ500_0827_POUT_WT.
WHERE (ind>=7449 and ind<=10747);
NOTE: The data set WORK._INDEX_TEMP has 3299 observations and 25 variables.
NOTE: DATA statement used (Total process time):
real time 0.01 seconds
cpu time 0.01 seconds
MPRINT(GET_MIN_ST_IND): proc sort data=_index_temp ;
MPRINT(GET_MIN_ST_IND): by DESCENDING ind;
MPRINT(GET_MIN_ST_IND): run;
NOTE: There were 3299 observations read from the data set WORK._INDEX_TEMP.
NOTE: The data set WORK._INDEX_TEMP has 3299 observations and 25 variables.
NOTE: PROCEDURE SORT used (Total process time):
real time 0.02 seconds
cpu time 0.00 seconds
MPRINT(GET_MIN_ST_IND): quit;
MPRINT(GET_MIN_ST_IND): data _index_temp;
MPRINT(GET_MIN_ST_IND): set _index_temp;
MPRINT(GET_MIN_ST_IND): retain index_max index_off_max_max;
MPRINT(GET_MIN_ST_IND): if _N_=1 then do;
MPRINT(GET_MIN_ST_IND): index_max=zz500;
MPRINT(GET_MIN_ST_IND): index_off_max_max=0;
MPRINT(GET_MIN_ST_IND): end;
MPRINT(GET_MIN_ST_IND): else do;
MPRINT(GET_MIN_ST_IND): if zz500>index_max then index_max=zz500;
MPRINT(GET_MIN_ST_IND): index_off_max=index_max-zz500;
MPRINT(GET_MIN_ST_IND): if index_off_max>index_off_max_max then index_off_max_max=index_off_max;
MPRINT(GET_MIN_ST_IND): end;
MPRINT(GET_MIN_ST_IND): run;
NOTE: There were 3299 observations read from the data set WORK._INDEX_TEMP.
NOTE: The data set WORK._INDEX_TEMP has 3299 observations and 28 variables.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
MPRINT(GET_MIN_ST_IND): quit;
MPRINT(GET_MIN_ST_IND): proc sql noprint;
MPRINT(GET_MIN_ST_IND): select ind into: _st_ind_lp_off_mm from _index_temp where
index_off_max_max<0.1 having zz500=max(zz500);
NOTE: The query requires remerging summary statistics back with the original data.
MPRINT(GET_MIN_ST_IND): quit;
NOTE: PROCEDURE SQL used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
MPRINT(GET_MIN_ST_IND): 10295
MLOGIC(GET_MIN_ST_IND): Ending execution.
%macro test();
%if %checkds(_index_temp) eq 1 %then %do;
%put "file exists";
%end;
%if %get_min_st_ind(zz500, 0827, 6, 0.1) eq 10295 %then %do;
%put "FINE";
%end;
%mend;
%test();
25050 %macro test();
25051 %if %checkds(_index_temp) eq 1 %then %do;
25052 %put "file exists";
25053 %end;
25054 %if %get_min_st_ind(zz500, 0827, 6, 0.1) eq 10295 %then %do;
25055 %put "FINE";
25056 %end;
25057 %mend;
25058
25059 %test();
MLOGIC(TEST): Beginning execution.
MLOGIC(CHECKDS): Beginning execution.
MLOGIC(CHECKDS): Parameter DSN has value _index_temp
MLOGIC(CHECKDS): %LOCAL DS_EXIST
MLOGIC(CHECKDS): %LET (variable name is DS_EXIST)
MLOGIC(CHECKDS): %IF condition %sysfunc(exist(&dsn)) is TRUE
MLOGIC(CHECKDS): %LET (variable name is DS_EXIST)
MLOGIC(CHECKDS): Ending execution.
MLOGIC(TEST): %IF condition %checkds(_index_temp) eq 1 is TRUE
MLOGIC(TEST): %PUT "file exists"
"file exists"
MLOGIC(GET_MIN_ST_IND): Beginning execution.
MLOGIC(GET_MIN_ST_IND): Parameter INDEXOUT has value zz500
MLOGIC(GET_MIN_ST_IND): Parameter DT has value 0827
MLOGIC(GET_MIN_ST_IND): Parameter I_MM_IND has value 6
MLOGIC(GET_MIN_ST_IND): Parameter TH_PCT has value 0.1
MLOGIC(GET_MIN_ST_IND): %LOCAL _ST_IND_LP_OFF_MM
MLOGIC(GET_MIN_ST_IND): %LET (variable name is _ST_IND_LP_OFF_MM)
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand
is required. The condition was: %get_min_st_ind(zz500, 0827, 6, 0.1) eq 10295
MLOGIC(GET_MIN_ST_IND): Ending execution.
ERROR: The macro TEST will stop executing.
MLOGIC(TEST): Ending execution.
Hello @hellohere,
Your approach fails because the macro call %get_min_st_ind(...) resolves to SAS code (DATA and PROC steps), whereas your intention is that it resolves to a single value (&_st_ind_lp_off_mm), as if macro get_min_st_ind was a function-style macro.
I would probably move the %LOCAL statement
%local _st_ind_lp_off_mm;
to macro test, remove the macro variable reference at the end of macro get_min_st_ind and use it in the %IF condition in macro test after a macro call %get_min_st_ind(...).
Here is a simplified example showing this structure:
%macro inner(firstname); proc sql noprint; select age into :mvar from sashelp.class where name="&firstname"; quit; %mend inner; %macro outer; %local mvar; %inner(Alfred) %if &mvar=14 %then %put FINE; %mend outer; %outer
Hello @hellohere,
Your approach fails because the macro call %get_min_st_ind(...) resolves to SAS code (DATA and PROC steps), whereas your intention is that it resolves to a single value (&_st_ind_lp_off_mm), as if macro get_min_st_ind was a function-style macro.
I would probably move the %LOCAL statement
%local _st_ind_lp_off_mm;
to macro test, remove the macro variable reference at the end of macro get_min_st_ind and use it in the %IF condition in macro test after a macro call %get_min_st_ind(...).
Here is a simplified example showing this structure:
%macro inner(firstname); proc sql noprint; select age into :mvar from sashelp.class where name="&firstname"; quit; %mend inner; %macro outer; %local mvar; %inner(Alfred) %if &mvar=14 %then %put FINE; %mend outer; %outer
%macro test();
%if %checkds(_index_temp) eq 1 %then %do;
%put "file exists";
%end;
%get_min_st_ind(zz500, 0827, 6, 0.1)
%if &_st_ind_lp_off_mm. eq 10295 %then %do;
%put "FINE";
%end;
%mend;
%test();
I remove the Mvar at the end and define this: %global _st_ind_lp_off_mm.
It surely work. I just curious why/what cause not working.
I have the macro alike, checkds(ds), which works fine. Why not this one?!
%macro checkds(dsn);
%local ds_exist;
%let ds_exist=0;
%if %sysfunc(exist(&dsn)) %then
%do;
/*%put The data set &dsn DOES exist.;*/
%let ds_exist=1;
%end;
%else
%put The data set &dsn does NOT exist.;
&ds_exist
%mend ;
@hellohere wrote:I remove the Mvar at the end and define this: %global _st_ind_lp_off_mm.
Sure, you can also define mvar as a global macro variable. My approach was to keep it local to the calling macro.
@hellohere wrote:
I have the macro alike, checkds(ds), which works fine. Why not this one?!
Macro checkds is a function-style macro. It consists of macro code only and resolves to a single value.
An alternative approach is to modify your macro get_min_st_ind so that it becomes a function-style macro. For example, you can "hide" the DATA and PROC steps in a %SYSFUNC(DOSUBL(...)) call:
%macro inner(firstname);
%local rc;
%let rc=%sysfunc(dosubl('proc sql noprint;
select age into :mvar from sashelp.class
where name="&firstname";
quit;'
));
&mvar
%mend inner;
%macro outer;
%if %inner(Alfred)=14 %then %put FINE;
%mend outer;
%outer
Note to make a macro that wants to return a value via a macro variable more flexible do not hardcode either %LOCAL or %GLOBAL statements.
Instead have the macro check if the macro variable it wants to use to return the value exists or not and only when it does not already exist make a new GLOBAL macro variable.
You might even want to add a parameter to the macro so the user can tell it what macro variable name to use.
%macro mymacro(in1,in1,out=mvarname);
%if not %symexist(&out) %then %global &out ;
....
%let &out = ....
%mend mymacro;
Your first macro, %get_min_st_ind() has non-macro steps (DATA and PROC steps) inside it. When you try to call this macro inside your logic in %test(), you are trying to run these non-macro steps inside a %if ... %then statement:
%if %get_min_st_ind(zz500, 0827, 6, 0.1) eq 10295 %then %do;
I think this may be confusing if you're coming from another language and under the impression that SAS macros are just like functions in another language, like Python or whatever. Macros do not "return" a value in the same sense as a true function - instead, they literally write out the text generated by the macro wherever they are run, and that means that this text has to be legal at this point in the code.
In order for the call to %get_min_st_ind() to work as you have it above, you would need to find a way to remove all non-macro steps from inside that macro (again, the PROC and DATA steps), which may or may not be possible given what you're trying to do (I really haven't looked closely at what's happening).
If you can't do that, then you could instead just have that macro generate a %global macro variable (_st_ind_lp_off_mm), and then, in %test(), check the value of that global macro variable in your %if...%then... logic.
%get_min_st_ind() has non-macro steps (DATA and PROC steps) inside it ... So the DATA/Proc are non-macro steps?! Glad to know. Thanks.
Because you ran it with the MPRINT option turned on the first LOG you shared shows the problem .
MPRINT(GET_MIN_ST_IND): proc sql noprint;
First a string like proc sql noprint is never going to the same as the digit string in your %IF statement. And the semi-colons are going to cause all kinds of havoc also.
Another way to test a macro to see if might be possible to use it that way is just use a %PUT statement.
%put "%get_min_st_ind(zz500, 0827, 6, 0.1)";
It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.
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.
Ready to level-up your skills? Choose your own adventure.