BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
jacksonan123
Lapis Lazuli | Level 10

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; 

 

OPTIONS NONOTES NOSTIMER NOSOURCE NOSYNTAXCHECK;
ERROR: Invalid macro parameter name 1. It should be a valid SAS identifier no longer than 32 characters.
ERROR: A dummy macro will be compiled.
 

 

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

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;

View solution in original post

10 REPLIES 10
ChrisNZ
Tourmaline | Level 20

The second parameter is the name of a macro variable. 1 is not a valid name.

ScottBass
Rhodochrosite | Level 12

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


Please post your question as a self-contained data step in the form of "have" (source) and "want" (desired results).
I won't contribute to your post if I can't cut-and-paste your syntactically correct code into SAS.
Shmuel
Garnet | Level 18

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 */
jacksonan123
Lapis Lazuli | Level 10
I used your code with the following data set below (newt1) and got the
following error.

100 %splitdsnbyobs(newt1,3); /* line added to execute the macro code
*/

NOTE: Line generated by the invoked macro "SPLITDSNBYOBS".

100 %RecsInDS(&dsn,no_obs); %macro finalobs; %if
%sysfunc(ceil(%eval(&i * &splitby.))) gt &no_obs %then &no_obs;

_

180

100 ! %else %sysfunc(ceil(%eval(&i * &splitby.))); %mend finalobs;

WARNING: Apparent invocation of macro RECSINDS not resolved.

ERROR 180-322: Statement is not valid or it is used out of proper order.

WARNING: Apparent symbolic reference NO_OBS not resolved.

WARNING: Apparent symbolic reference NO_OBS not resolved.

ERROR: Argument 1 to function CEIL referenced by the %SYSFUNC or %QSYSFUNC
macro function is not a number.

ERROR: Invalid arguments detected in %SYSCALL, %SYSFUNC, or %QSYSFUNC
argument list. Execution of %SYSCALL statement or %SYSFUNC

or %QSYSFUNC function reference is terminated.

ERROR: A character operand was found in the %EVAL function or %IF condition
where a numeric operand is required. The condition was:

%sysfunc(ceil(&no_obs/&splitby))

ERROR: The %TO value of the %DO I loop is invalid.

ERROR: The macro SPLITDSNBYOBS will stop executing.

101



DATA NEWt1;

INPUT SUBJ TRT $ AUCL AUCI CPEAK HALF TPEAK TLAG ;

CARDS;

1 A 2079.16 2087.26 2556.76 1.1 1.5 1

2 A 1377.26 . 777.38 . 8 8

3 A 1899.42 1908.64 2002.591 1.13 1.5 1

;

run;
Shmuel
Garnet | Level 18

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 */
jacksonan123
Lapis Lazuli | Level 10
I got the following log with the same errors as before with new code same
data set.

DATA NEWT1;

INPUT SUBJ TRT $ AUCL AUCI CPEAK HALF TPEAK TLAG ;

CARDS;

1 A 2079.16 2087.26 2556.76 1.1 1.5 1

2 A 1377.26 . 777.38 . 8 8

3 A 1899.42 1908.64 2002.591 1.13 1.5 1

;

RUN;


85 %macro splitdsnbyobs(dsn,splitby); /* defined macro with
arguments to be used in code */
86 %RecsInDS(&dsn,no_obs); /* Read more about this macro at
86 !
http://sastechies.blogspot.com/2009/11/ways-to-count-number-of-obs-in-datase
t.html */
87
88 /*calculate the final obs for each do loop iteration*/
89
90 %macro finalobs;
91 %if %sysfunc(ceil(&i * &splitby.)) gt &no_obs %then &no_obs;
92 %else %sysfunc(ceil(&i * &splitby.));
93 %mend finalobs;
94 /* keep the observations from firstobs= and obs=*/
95 %do i=1 %to %sysfunc(ceil(&no_obs/&splitby));
96 data &dsn.&i.;
97 set &dsn (firstobs=%sysfunc(floor(&i.-1)*&splitby.+1)
obs=%finalobs);
98 run;
99 %end;
100 %mend splitdsnbyobs;
101
102 %splitdsnbyobs(newt1,3); /* line added to execute the macro code
*/
NOTE: Line generated by the invoked macro "SPLITDSNBYOBS".
102 %RecsInDS(&dsn,no_obs); %macro finalobs; %if
%sysfunc(ceil(&i * &splitby.)) gt &no_obs %then &no_obs; %else
_
180
102 ! %sysfunc(ceil(&i * &splitby.)); %mend finalobs;
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.

WARNING: Apparent symbolic reference NO_OBS not resolved.
WARNING: Apparent symbolic reference NO_OBS not resolved.
ERROR: Argument 1 to function CEIL referenced by the %SYSFUNC or %QSYSFUNC
macro function is not a number.
ERROR: Invalid arguments detected in %SYSCALL, %SYSFUNC, or %QSYSFUNC
argument list. Execution of %SYSCALL statement or %SYSFUNC
or %QSYSFUNC function reference is terminated.
ERROR: A character operand was found in the %EVAL function or %IF condition
where a numeric operand is required. The condition was:
%sysfunc(ceil(&no_obs/&splitby))
ERROR: The %TO value of the %DO I loop is invalid.
ERROR: The macro SPLITDSNBYOBS will stop executing.
103
104 OPTIONS NONOTES NOSTIMER NOSOURCE NOSYNTAXCHECK;


Shmuel
Garnet | Level 18

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(.....);

           

 

 

Tom
Super User Tom
Super User

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);

 

jacksonan123
Lapis Lazuli | Level 10
This is the log but same error.



1 OPTIONS NONOTES NOSTIMER NOSOURCE NOSYNTAXCHECK;

72

73

74 DATA NEWt1;

75 INPUT SUBJ TRT $ AUCL AUCI CPEAK HALF TPEAK TLAG ;

76 CARDS;

NOTE: The data set WORK.NEWT1 has 3 observations and 8 variables.

NOTE: DATA statement used (Total process time):

real time 0.01 seconds

cpu time 0.02 seconds





80 ;

81 run;

82

83 %macro splitdsnbyobs(dsn,splitby);

84 %local no_obs firstobs finalobs;

85 %RecsInDS(&dsn,no_obs);

86

87 %do i=1 %to %sysfunc(ceil(&no_obs/&splitby));

88 /*calculate the first and final obs for each do loop iteration*/

89 %let firstobs=%sysfunc(floor((&i.-1)*&splitby.+1));

90 %let finalobs=%sysfunc(ceil(&i * &splitby.));

91 %let finalobs=%sysfunc(min(&finalobs,&no_obs));

92

93 /* keep the observations from firstobs= and obs=*/

94 data &dsn.&i.;

95 set &dsn (firstobs=&firstobs. obs=%finalobs.);

96 run;

97 %end;

98 %mend splitdsnbyobs;

99

100 %splitdsnbyobs(newt1,1);

NOTE: Line generated by the invoked macro "SPLITDSNBYOBS".

100 %RecsInDS(&dsn,no_obs);

_

180

WARNING: Apparent invocation of macro RECSINDS not resolved.

ERROR 180-322: Statement is not valid or it is used out of proper order.

ERROR: Argument 1 to function CEIL referenced by the %SYSFUNC or %QSYSFUNC
macro function is not a number.

ERROR: Invalid arguments detected in %SYSCALL, %SYSFUNC, or %QSYSFUNC
argument list. Execution of %SYSCALL statement or %SYSFUNC

or %QSYSFUNC function reference is terminated.

ERROR: A character operand was found in the %EVAL function or %IF condition
where a numeric operand is required. The condition was:

%sysfunc(ceil(&no_obs/&splitby))

ERROR: The %TO value of the %DO I loop is invalid.

ERROR: The macro SPLITDSNBYOBS will stop executing.

101

102 OPTIONS NONOTES NOSTIMER NOSOURCE NOSYNTAXCHECK;

114



User: sasdemo


Tom
Super User Tom
Super User

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;

SAS Innovate 2025: Call for Content

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!

Submit your idea!

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
  • 10 replies
  • 1297 views
  • 0 likes
  • 5 in conversation