I have a very simple data set newt1 and I get the following error. What name should
I have used since I want it split by one record without the error?
subj time cp
1 0 0
2 0 0
%macro splitdsnbyobs(newt1,1);
%RecsInDS(&dsn,no_obs); /* Read more about this macro at http://sastechies.blogspot.com/2009/11/ways-to-count-number-of-obs-in-dataset.html */
/*calculate the final obs for each do loop iteration*/
%macro finalobs;
%if %sysfunc(ceil(%eval(&i * &splitby.))) gt &no_obs %then &no_obs;
%else %sysfunc(ceil(%eval(&i * &splitby.)));
%mend finalobs;
/* keep the observations from firstobs= and obs=*/
%do i=1 %to %sysfunc(ceil(&no_obs/&splitby));
data &dsn.&i.;
set &dsn (firstobs=%sysfunc(floor(%eval((&i.-1)*&splitby.+1))) obs=%finalobs);
run;
%end;
%mend splitdsnbyobs;
You cannot call a macro that you haven't defined. You either need to include the macro definition (or have it in your autocall library).
But you don't need that macro. Replace
%RecsInDS(&dsn,no_obs);
with
data _null_;
call symputx('no_obs',no_obs);
stop;
set &dsn nobs=no_obs;
run;
The second parameter is the name of a macro variable. 1 is not a valid name.
There are many ways to tackle this, mine is but one...
%macro splitds(data,parts);
data parts;
if 0 then set &data nobs=nobs;
do num=1 to &parts;
if num=1 then do;
firstobs=1;
obs=ifn(num=&parts,nobs,int(nobs/&parts)*num);
output;
end;
else do;
firstobs=obs+1;
obs=ifn(num=&parts,nobs,int(nobs/&parts)*num);
output;
end;
end;
stop;
keep num firstobs obs;
run;
%* inner helper macro ;
%macro code;
data dsn#
set &data (firstobs=&firstobs obs=&obs);
run;
%mend;
%loop_control(control=parts);
%mend;
%splitds(sashelp.zipcode,1)
%splitds(sashelp.zipcode,2)
%splitds(sashelp.zipcode,3)
%splitds(sashelp.zipcode,4)
%loop_control and other utility macros:
https://github.com/scottbass/SAS/blob/master/Macro/loop_control.sas
You have not posted your full code.
You defined next line with two arguments that should include variable names
%macro splitdsnbyobs(newt1,1);
suppose you want to split into 3 datasets and your dataset name is newt1
then your code should be:
%macro splitdsnbyobs(dsn,splitby); /* defined macro with arguments to be used in code */
%RecsInDS(&dsn,no_obs); /* Read more about this macro at http://sastechies.blogspot.com/2009/11/ways-to-count-number-of-obs-in-dataset.html */
/*calculate the final obs for each do loop iteration*/
%macro finalobs;
%if %sysfunc(ceil(%eval(&i * &splitby.))) gt &no_obs %then &no_obs;
%else %sysfunc(ceil(%eval(&i * &splitby.)));
%mend finalobs;
/* keep the observations from firstobs= and obs=*/
%do i=1 %to %sysfunc(ceil(&no_obs/&splitby));
data &dsn.&i.;
set &dsn (firstobs=%sysfunc(floor(%eval((&i.-1)*&splitby.+1))) obs=%finalobs);
run;
%end;
%mend splitdsnbyobs;
%splitdsnbyobs(newt1,3); /* line added to execute the macro code */
I think that you don't need the %eval macro function under %sysfunc.
Try next code:
%macro splitdsnbyobs(dsn,splitby); /* defined macro with arguments to be used in code */
%RecsInDS(&dsn,no_obs); /* Read more about this macro at http://sastechies.blogspot.com/2009/11/ways-to-count-number-of-obs-in-dataset.html */
/*calculate the final obs for each do loop iteration*/
%macro finalobs;
%if %sysfunc(ceil(&i * &splitby.)) gt &no_obs %then &no_obs;
%else %sysfunc(ceil(&i * &splitby.));
%mend finalobs;
/* keep the observations from firstobs= and obs=*/
%do i=1 %to %sysfunc(ceil(&no_obs/&splitby));
data &dsn.&i.;
set &dsn (firstobs=%sysfunc(floor(&i.-1)*&splitby.+1) obs=%finalobs);
run;
%end;
%mend splitdsnbyobs;
%splitdsnbyobs(newt1,3); /* line added to execute the macro code */
Pay attention to next lines in the log:
WARNING: Apparent invocation of macro RECSINDS not resolved. MPRINT(SPLITDSNBYOBS): %RecsInDS(newt1,no_obs); ERROR 180-322: Statement is not valid or it is used out of proper order.
Macro RECSINDS - you need to copy it from the URL and include it into your code.
I accept @Tom's note and suggest to reorganize the code:
%macro RecsInDS( ....);
... copy macro fro the URL ...
%mend;
%macro finalobs;
%if %sysfunc(ceil(%eval(&i * &splitby.))) gt &no_obs %then &no_obs;
%else %sysfunc(ceil(%eval(&i * &splitby.)));
%mend finalobs;
%macro splitdsnbyobs(dsn,splitby);
%RecsInDS(&dsn,no_obs);
%do i=1 %to %sysfunc(ceil(&no_obs/&splitby));
%finalobs;
data &dsn.&i.;
set &dsn (firstobs=%sysfunc(floor(%eval((&i.-1)*&splitby.+1)))
obs=%finalobs);
run;
%end;
%mend splitdsnbyobs;
%splitdsnbyobs(newt1,3);
Pay attention, usage of a macro language is in two steps:
1) Macro definition with macro variables as arguments
%macro macro_name(arg_name, arg2_name, ...);
.... code uses &arg_name ...
%mend;
2) Macro execution with values supplied in order of arguments:
%macro_name(.....);
The main issue is that mistakenly modified the DEFINITION of the macro by changing the parameter NAMES in the %MACRO statement. Instead you want to pass in the values to use when you CALL the macro.
The macro is a little silly (over complicated? confused?) because it is trying to define another macro inside the body of the macro definition. It never makes sense to define a macro inside of another macro. The name space for macros is flat, not nested. So a macro defined inside of another macro is the same as it would be if it was defined outside. You don't need that macro as you can just do the %IF statement as part of the outer macro definition. And you don't even need the %IF statement as all you doing is taking the MIN() of two numbers.
So first simplify the macro definition.
%macro splitdsnbyobs(dsn,splitby);
%local no_obs i firstobs finalobs;
%RecsInDS(&dsn,no_obs);
%do i=1 %to %sysfunc(ceil(&no_obs/&splitby));
/*calculate the first and final obs for each do loop iteration*/
%let firstobs=%sysfunc(floor((&i.-1)*&splitby.+1));
%let finalobs=%sysfunc(ceil(&i * &splitby.));
%let finalobs=%sysfunc(min(&finalobs,&no_obs));
/* keep the observations from firstobs= and obs=*/
data &dsn.&i.;
set &dsn (firstobs=&firstobs. obs=%finalobs.);
run;
%end;
%mend splitdsnbyobs;
Then try calling it.
%splitdsnbyobs(newt1,1);
You cannot call a macro that you haven't defined. You either need to include the macro definition (or have it in your autocall library).
But you don't need that macro. Replace
%RecsInDS(&dsn,no_obs);
with
data _null_;
call symputx('no_obs',no_obs);
stop;
set &dsn nobs=no_obs;
run;
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
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.