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;
April 27 – 30 | Gaylord Texan | Grapevine, Texas
Walk in ready to learn. Walk out ready to deliver. This is the data and AI conference you can't afford to miss.
Register now and lock in 2025 pricing—just $495!
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.