SAS macro error: More positional parameters found than defined - macros

Given the following SAS data set and macro, could someone please provide an explanation as to why the first 3 calls to the macro execute without errors but the 4th call produces the error message as listed below. If I don't use a default value for "code" (change the “code=A11” to “code”) in the macro definition, then all 4 calls execute without error. Why is this? I have tried exiting the SAS session and restarting but it didn't help.
New to SAS. Thank you. Much Appreciated. :)
------------------- begin code block -----------------------------------
/* data set definition */
data dat5;
infile datalines truncover;
input Year Prod_Cd $ Sales;
datalines;
2001 A11 100
2001 B12 200
2002 C13 300
;
run;
/* macro definition */
%macro sel(code=A11);
proc sql;
select * from dat5
where Prod_Cd = "&code";
quit;
%mend;
/* calls to the macro */
%sel(); /* 1st call */
%sel(code=A11); /* 2nd call */
%sel(code=A12); /* 3rd call */
%sel(A11); /* 4th call */
--------------------end code block--------------------------------------
/* SAS log message for the 4th call to the macro */
128 %macro sel(code=A11);
129 proc sql;
130 select * from dat5
131 where Prod_Cd = "&code";
132 quit;
133 %mend;
134
135 %sel(A11);
MLOGIC(SEL): Beginning execution.
ERROR: More positional parameters found than defined.
MLOGIC(SEL): Parameter CODE has value A11
MLOGIC(SEL): Ending execution.
------------------------------------------------------------------------

When you define a macro you can specify whether or not the parameter can be called by position (that is without naming the parameter in the macro call). So called positional parameters are defined in the %MACRO statement without an = after the name. You can define both positional and named parameters, but the positional parameters must appear first. Note that you can specify values for any parameter by name in the macro call, but only parameters defined as positional can be called by position.
So if you want to specify a value for the CODE parameter in the macro call without including the name then you need to define the macro like this:
%macro sel(code);
Then all of the calls in your example will work, but the first one will set CODE to an empty string instead of A11. If you want to have a default value for parameters that are defined as positional you will need to add the logic to the macro itself. Such as this:
%macro sel(code);
%if not %length(&code) %then %let code=A11;
proc sql;
select * from dat5
where Prod_Cd = "&code"
;
quit;
%mend;

This is expected behaviour:
%sel(); /* 1st call - no parameters specified - SAS uses default values from macro definition */
%sel(code=A11); /* 2nd call - keyword parameter used correctly */
%sel(code=A12); /* 3rd call - keyword parameter used correctly */
%sel(A11); /* 4th call - No parameter name or =, so SAS interprets this as a positional parameter, but none are defined within the macro, hence the error*/

SAS® 9.4 Macro Language: Reference, Fifth Edition, %MACRO Statement
When you define a macro the parameters list is specified as a comma separated list of positional items (name) followed by keyword items (name=default-value).
When the macro is invoked, the arguments, specified as a comma separated list of values, are mapped to the parameters.
Positional arguments must occur in the same order as in the macro definition.
Keyword arguments can occur in any order after the positional arguments.
Positional arguments can also be specified as name=value. This is a good convention for ensuring readability in macro heavy code.
Any positional arguments that are skipped will be passed as an empty string.
Any unspecified keyword parameter is assigned the default value.
Error if the argument list tries to specify a parameter more than once (as can occur if you incorrectly mix positional and keyword or repeat a keyword)
Using A11 value as a positional argument causes an error because the parameter list for macro %sel does not have any positional parameters.

Related

SAS Macro operations

I need to create sum of 4 variables multiple times each time with new set of variables. For e.g. A1=sum(a1,a2,a3,a4),B1=sum(b1,b2,b3,b4) & so on. So , I am trying to write a macro that will help me do it easily. Following is the code:
%macro SUM2(VAR1,var2,var3,VAR4);
data Subs_60_new;
set Subs_60;
substr(&var1,1,10)=sum(&var1,&var2,&var3,&var4);
run;
%mend sum2;
options mprint mlogic;sum2(ADDITIONAL_INFO_Q1,ADDITIONAL_INFO_Q2,ADDITIONAL_INFO_Q3,ADDITIONAL_INFO_Q4);
I am using SAS EG for the same & when I run the macro I get the following note:
NOTE: Writing TAGSETS.SASREPORT13(EGSR) Body file: EGSR
& obviously when I try to execute the macro it throws an error.
Can some one help me out?
when calling a macro, you need to precede the macro name with a % symbol, eg as follows:
%macro SUM2(VAR1,var2,var3,VAR4);
data Subs_60_new;
set Subs_60;
substr(&var1,1,10)=sum(&var1,&var2,&var3,&var4);
run;
%mend sum2;
options mprint mlogic;
%sum2(ADDITIONAL_INFO_Q1,ADDITIONAL_INFO_Q2,ADDITIONAL_INFO_Q3,ADDITIONAL_INFO_Q4);
The NOTE is harmless. It is ERRORs and WARNINGs in general that you should be concerned with.
I'd point out that this will probably still throw an error, as you are trying to replace characters in a variable (&var1) that appears as though it should contain a numeric field (being part of a sum function). Given your description of what you are trying to achieve, I'd suggest adding the new variable name as another macro parameter - as follows:
%macro SUM2(VAR1,var2,var3,VAR4,varname);
data Subs_60_new;
set Subs_60;
&varname=sum(&var1,&var2,&var3,&var4);
run;
%mend sum2;
options mprint mlogic;
%sum2(ADDITIONAL_INFO_Q1,ADDITIONAL_INFO_Q2
,ADDITIONAL_INFO_Q3,ADDITIONAL_INFO_Q4
,MyNewVariable);

SPSS Macro - Generate dynamic Varnames

I am currently trying to create dynamic variable names based on the valuelabels of the passed Argument. Currently, I have something like this:
COMPUTE counter = 0.
APPLY DICTIONARY FROM *
/SOURCE VARIABLES = V601
/TARGET VARIABLES = counter.
DEFINE !macro1 (!POS !CMDEND).
STRING name (A20).
!DO !#i = 1 !TO 62
COMPUTE counter = #i
!IF (!POS !EQ !i)
!THEN
COMPUTE name = VALUELABEL(!POS)
COMPUTE !CONCAT('wasnot', name) = 1.
!ELSE
COMPUTE name = VALUELABEL(!counter).
COMPUTE !CONCAT('wasnot', name) = 0.
!IFEND
!DOEND
CROSSTABS v15 by !CONCAT('wasnot', name) /cells = column.
!ENDDEFINE.
The idea is, that for every unique value of V601 a flag variable will be created (e.g. "wasnotvaluelabel1"). This variable will either have value = 1 or 0 respectively. However, it seems that concat cannot be used the way I intended. I get these errors:
Error # 6843 in column 7. Text: !POS
The end of a macro expression occurred when an operand was expected.
Execution of this command stops.
Error # 6846 in column 7. Text: !POS
A macro expression includes an undefined macro variable or a macro operator
which is not valid within an expression.
Error # 6836 in column 12. Text: !EQ
In a macro expression, an operator was not preceded by an operand.
Error # 6846 in column 2. Text: !THEN
A macro expression includes an undefined macro variable or a macro operator
which is not valid within an expression.
Error # 6846 in column 28. Text: !POS
A macro expression includes an undefined macro variable or a macro operator
which is not valid within an expression.
Questions I have right now:
Is it even possible to generate dynamic names? I have tried
different attempts over the last hours but the SPSS macro "language"
seems very restricted.
Is there perhaps some other way to achieve this Task? It seems rather unconvenient.
Please note, working with the Python AddIn is sadly not an Option. I'm grateful for any received advice.
There is an extension command, SPSSINC CREATE DUMMIES, that will create all these dummy variables automatically. It's on the Transform menu. And it is implemented in Python.
Using Python you can easily read case data and do lots more.
Thanks for all the Help. In the end I did it with generating new syntax using Outfile.

Compare macro value with dataset and goto next level

I am having a dataset which one variable is date. I passed some value to macro variable 'frodate'. Now i want if value is exist in dataset variable date then goto to a label.
but my program does not perform match. It is show condition is false,but value is in the dataset. Please help to do the same.
DATA _NULL_ ;
SET STOCK.NIFTY_IDX;
%IF &FRODATE = DATE %THEN %GOTO NEXT ;
RUN;
I don't understand what mistake i am doing. I know this is a very small problem, but i can't understand the same.
You are confusing macro language (%if) with sas program syntax (if). SAS Macro language is used to write sas programs - and so all macro statements are essentially resolved BEFORE the sas program runs. Data steps cannot (easily) drive macro logic, during that data step.
Also, macro language statements (such as %if) must be wrapped in a macro (%macro x; %mend;), which I assume is the case if you are successfully resolving FALSE. This is expected, unless your "&FRODATE" macro variable actually contains the character string "DATE" (macro only works with text, and it cannot easily read dataset variables).
Anyway, my interpretation of your question leads me to think that you need logic as follows:
/* create a sas macro as we are using macro logic */
%macro some_macro(frodate);
/* first run datastep and convert the dataset variable into a macro variable */
data _null_;
set stock.nifty_idx;
call symputx('Nifty_dt',date);
stop;
run;
/* the above is parsed and the macro processor receives a value for NIFTY_DT */
%if &frodate=&nifty_dt %then %goto next; /* if a match, go to the "label" */
%else %return; /* else exit the macro or some other logic */
%next: /* this is the "label", note the colon - not a semicolon */
%put success!;
%mend;
/* now RUN the macro to execute the logic.. */
%some_macro;
I understand my error and resolved the same. I do it in different manner.
%LET TEMDATE=%SYSFUNC(PUTN(&FDATE,DATE9.));
proc sql noprint;
select left(put(count(*),8.)) into :EXIST
from STOCK.NIFTY_&IDXFUT
where date = "&TEMDATE"d;
quit;
%IF &EXIST = 0 %THEN %DO......
Thanks for your valuable answer..

Dynamically choose name of macro to call?

In SAS, the double/triple/etc ampersand can be used to dynamically choose which macro variable to resolve. Is there an equivalent syntax using double/triple/etc percents?
I tried %%%substr(&x,3) expecting the same resolution rules to be used (i.e. turn %% into %, then call the substring macro, then call the macro with the name of the result) but it is apparently invalid syntax.
You can use macro variable instead of fixed macro name, e.g.:
%let mname=test;
%macro test;
%put I am test.;
%mend;
%&mname

SPSS Macro: compute by variable name

I don't think SPSS macros can return values, so instead of assigning a value like VIXL3 = !getLastAvail target=VIX level=3 I figured I need to do something like this:
/* computes last available entry of target at given level */
define !compLastAvail(name !Tokens(1) /target !Tokens(1) /level !Tokens(1))
compute tmpid= $casenum.
dataset copy tmpset1.
select if not miss(!target).
compute !name= lag(!target, !level).
match files /file= * /file= tmpset1 /by tmpid.
exec.
delete variables tmpid.
dataset close tmpset1.
!enddefine.
/* compute last values */
!compLastAvail name="VIXCL3" target=VIXC level=3.
The compute !name = ...is where the problem is.
How should this be done properly? The above returns:
>Error # 4285 in column 9. Text: VIXCL3
>Incorrect variable name: either the name is more than 64 characters, or it is
>not defined by a previous command.
>Execution of this command stops.
When you pass tokens to the macro, they get interpreted literally. So when you specify
!compLastAvail name="VIXCL3"
It gets passed to the corresponding compute statement as "VIXCL3", instead of just a variable name without quotation marks (e.g. VIXCL3).
Two other general pieces of advice;
If you do the command set mprint on before you execute your macro, you will see how your tokens are passed to the macro. In this instance, if you had taken that step, you would have seen that the offending compute statement and error message.
Sometimes you do what to use quotation marks in tokens, and when that is the case the string commands !QUOTE and !UNQUOTE come in handy.