BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
SASdevAnneMarie
Barite | Level 11

Hello Experts,

 

My data is  :

ID X1 X2 X3 X4 nmbr
1 lb_1 lb_2 lb_3 lb_4 4
2 lb_1 lb_2 lb_3   3
3 lb_1 lb_2     2
4 lb_1       1

 

I would like to create a macro varibale in this way :

 


proc sql noprint;
	select count(distinct ID) into :nb_obs  from mydata;
quit;

%macro myvariables;
	%do i=1 %to &nb_obs.;

		data _NULL_;
			set mydata (obs=&i);
			CALL SYMPUTX(COMPRESS('nmbr'),nmbr);
			%do j=1 %to &nmbr.;
				CALL SYMPUTX(COMPRESS('Macro_var&j.'),X&j.);
			%end;
		run;

		/***Another STEP***/
	%end;
%mend;

The Macro_var&j. are not created, do you know please, another way to create the macro variables in this case of incremeting suffix?

 

Thank you !

1 ACCEPTED SOLUTION

Accepted Solutions
PaigeMiller
Diamond | Level 26
CALL SYMPUTX("Macro_var&j",X&j.);

Note the double quotes. Also, COMPRESS is not needed with SYMPUTX.

 

From now on, @SASdevAnneMarie, when your code isn't working properly, you need to show us the log for the macro or DATA step or PROC that isn't working. All of the log for that step, every single line, do not select portions of the log for that step to show us, and not show us other parts of the log for that step. You need to do this every single time from now on. WHY? Because the log already shows the problem, so that's a definitive result, whereas if we have to read the code you sent, we have to figure it out ourselves and sometimes get it wrong. 

 

For macro debugging, you also need to turn on the macro debugging option:

 

options mprint;
--
Paige Miller

View solution in original post

13 REPLIES 13
PaigeMiller
Diamond | Level 26
CALL SYMPUTX("Macro_var&j",X&j.);

Note the double quotes. Also, COMPRESS is not needed with SYMPUTX.

 

From now on, @SASdevAnneMarie, when your code isn't working properly, you need to show us the log for the macro or DATA step or PROC that isn't working. All of the log for that step, every single line, do not select portions of the log for that step to show us, and not show us other parts of the log for that step. You need to do this every single time from now on. WHY? Because the log already shows the problem, so that's a definitive result, whereas if we have to read the code you sent, we have to figure it out ourselves and sometimes get it wrong. 

 

For macro debugging, you also need to turn on the macro debugging option:

 

options mprint;
--
Paige Miller
Kurt_Bremser
Super User

In about 99% of cases that I've seen, such "pseudo-arrays" of macro variables are not needed, as there are better methods to achieve your goal.

What is it that you do in "Another Step"?

yabwon
Onyx | Level 15

Apart from @PaigeMiller suggestions - @SASdevAnneMarie, with your data, how your expected result should look like? 

We cannot guess it from the code (since it is not working...) and we also cannot figure it out from your post.

 

Bart 

_______________
Polish SAS Users Group: www.polsug.com and communities.sas.com/polsug

"SAS Packages: the way to share" at SGF2020 Proceedings (the latest version), GitHub Repository, and YouTube Video.
Hands-on-Workshop: "Share your code with SAS Packages"
"My First SAS Package: A How-To" at SGF2021 Proceedings

SAS Ballot Ideas: one: SPF in SAS, two, and three
SAS Documentation



Kurt_Bremser
Super User

Just as a very basic lesson in macro coding:

data _NULL_;
set mydata (obs=&i);
  CALL SYMPUTX(COMPRESS('nmbr'),nmbr);
  %do j=1 %to &nmbr.;
    CALL SYMPUTX(COMPRESS('Macro_var&j.'),X&j.);
  %end;
run;

The macro processor is a PREprocessor, creating code which is then sent to the SAS interpreter for execution. Macro statements are executed and macro variables resolved while code is prepared for execution.

CALL SYMPUTX is part of the interface between data step code and the macro facility, and is executed (like any other data step function) while the data step executes. So the macro variable &nmbr is created long after the %DO loop tries to use it, which means that the %TO value is never available, and the %DO therefore fails. You will see this in the log (Maxim 2!)

To create your (most probably not needed, see my previous post) "macro array", you only need data step code:

data _null_;
set mydata (point=&i.);
array x {*} x1-x4;
do j = 1 to nmbr;
  call symputx(cats("macro_var",j),x{j});
end;
stop;
run;

Using POINT= and STOP means that only a single observation needs to be read in every iteration of the outer macro loop (which is also not necessary in 99% of cases, as you can use a data step to repeatedly execute code with CALL EXECUTE in one sweep of a controlling dataset.

SASdevAnneMarie
Barite | Level 11

Thank you, Kurt,

 

I would like to use this code to make the ods pulishing.


ODS PDF FILE = "XXXX\&id..pdf" dpi=1800 startpage=no;

	data _NULL_;
			declare odsout obj();
			/****Lines of Code ods ****/
            %do i=k %to %nmbr.;
            obj.row_start();  
            obj.format_cell(text: " XXXX: ",  overrides: "just=left color=black font_size=10pt font_weight=medium cellheight=0.4cm leftmargin=1cm cellwidth=8.9cm fontfamily=arial");
			obj.format_cell(data: "&&Macro_var&k.", overrides: "color=black font_size=10pt font_weight=medium just=left cellheight=0.4cm fontfamily=arial");
            obj.row_end();
			%end;
run;

%end;
Tom
Super User Tom
Super User

@SASdevAnneMarie wrote:

Thank you, Kurt,

 

I would like to use this code to make the ods pulishing.


ODS PDF FILE = "XXXX\&id..pdf" dpi=1800 startpage=no;

	data _NULL_;
			declare odsout obj();
			/****Lines of Code ods ****/
            %do i=k %to %nmbr.;
            obj.row_start();  
            obj.format_cell(text: " XXXX: ",  overrides: "just=left color=black font_size=10pt font_weight=medium cellheight=0.4cm leftmargin=1cm cellwidth=8.9cm fontfamily=arial");
			obj.format_cell(data: "&&Macro_var&k.", overrides: "color=black font_size=10pt font_weight=medium just=left cellheight=0.4cm fontfamily=arial");
            obj.row_end();
			%end;
run;

%end;

So you took the data out of a dataset and put it into macro variables so you could use it in a data step?  That seems like a waste of effort.  Just have that last data step read in the values from the dataset where they were originally.

SASdevAnneMarie
Barite | Level 11

Thank you, Tom.

I do that, beacause I would like to create one document per observation :

 

proc sql noprint;
	select count(distinct ID) into :nb_obs  from mydata;
quit;

%macro myvariables;
	%do i=1 %to &nb_obs.;

		data _NULL_;
			set mydata (obs=&i);
			CALL SYMPUTX(COMPRESS('nmbr'),nmbr);
			%do j=1 %to &nmbr.;
				CALL SYMPUTX(COMPRESS('Macro_var&j.'),X&j.);
			%end;
		run;

		ODS PDF FILE = "XXXX\&id..pdf" dpi=1800 startpage=no;

	data _NULL_;
			declare odsout obj();
			/****Lines of Code ods ****/
            %do i=k %to %nmbr.;
            obj.row_start();  
            obj.format_cell(text: " XXXX: ",  overrides: "just=left color=black font_size=10pt font_weight=medium cellheight=0.4cm leftmargin=1cm cellwidth=8.9cm fontfamily=arial");
			obj.format_cell(data: "&&Macro_var&k.", overrides: "color=black font_size=10pt font_weight=medium just=left cellheight=0.4cm fontfamily=arial");
            obj.row_end();
			%end;
run;
	%end;
%mend;

I didn't understand your suggestion :"Just have that last data step read in the values from the dataset where they were originally."

PaigeMiller
Diamond | Level 26

@SASdevAnneMarie wrote:

I do that, beacause I would like to create one document per observation :

 


One document per observation, without creating macro variables from a data set and then using those macro variables in the next data set:

 

You can use CALL EXECUTE or DOSUBL.

 

There are plenty of examples using CALL EXECUTE to generate and execute code based upon the data in the current line; search the forums. Here are examples of doing a similar thing using DOSUBL: https://communities.sas.com/t5/SAS-Programming/Use-cases-for-DOSUBL-other-than-finding-lists-of-vari... and https://blogs.sas.com/content/sasdummy/2016/02/15/using-proc-iomoperate/

 

Adding: another great example of the XY problem in action. The OP is so narrowly focused on this task of getting the macro to work, that the idea that there may be better ways (without this macro and without the complications of macros) to get to the final result aren't mentioned until someone asks and tries to point the OP in that direction. So, @SASdevAnneMarie , please from now on give us some idea of the goal of your efforts, rather than just jump right into the details of your coding issues.

--
Paige Miller
Tom
Super User Tom
Super User

Your code as written cannot work. 

You cannot create a macro variable whose name includes ampersand and period characters.

You cannot execute a %DO loop using a starting value of the letter K.  You need to use a NUMBER, not a LETTER there.

You are generating the same ODS PDF statement multiple times.  What is the value of the macro variable ID?  That is not an input the macro.  There is no %GLOBAL statement or even a comment to explain where its values is supposed to come from.

 

Assuming the MYDATA has the variables ID, NMBR and X1 to X4 that it looks like your code is trying to use then perhaps you meant to do something like this?  If ID is a character variable then will need to add quotes around the value when generating WHERE clause so it will be valid SAS syntax: where id="&id";

%macro mymacro(dsname,id);
ODS PDF FILE = "XXXX\&id..pdf" dpi=1800 startpage=no;

data _null_;
  set &dsname;
  where id=&id;
  array data x1-x4 ;
  if _n_=1 then declare odsout obj();
  do index=1 to nmbr ;
    obj.row_start();  
    obj.format_cell(text: " XXXX: "
                   ,overrides: "just=left color=black font_size=10pt font_weight=medium cellheight=0.4cm leftmargin=1cm cellwidth=8.9cm fontfamily=arial")
    ;
    obj.format_cell(data: data[index]
                   ,overrides: "color=black font_size=10pt font_weight=medium just=left cellheight=0.4cm fontfamily=arial")
    ;
    obj.row_end();
 end;
run;
%mend;

data _null_;
  set mydata ;
  call execute(cats('%nrstr(%mymacro)(mydata,',id,')'));
run;

So basically create a macro to produce the results for one value of ID.

Then call it once for each value of ID in the data.

 

 

SASdevAnneMarie
Barite | Level 11

Thank you, Tom,

 

Finally, I did that and this works :


%macro myvariables;
	%do i=1 %to &nb_obs.;

	data _NULL_;
			set mydata (obs=&i);
			CALL SYMPUTX(COMPRESS('nmbr'),nmbr);
		run;


		data _NULL_;
			set mydata (obs=&i);
			CALL SYMPUTX(COMPRESS('nmbr'),nmbr);
			%do j=1 %to &nmbr.;
				CALL SYMPUTX(COMPRESS("Macro_var&j."),X&j.);
			%end;
		run;

		ODS PDF FILE = "XXXX\&id..pdf" dpi=1800 startpage=no;

	data _NULL_;
			declare odsout obj();
			/****Lines of Code ods ****/
            %do i=k %to %nmbr.;
            obj.row_start();  
            obj.format_cell(text: " XXXX: ",  overrides: "just=left color=black font_size=10pt font_weight=medium cellheight=0.4cm leftmargin=1cm cellwidth=8.9cm fontfamily=arial");
			obj.format_cell(data: "&&Macro_var&k.", overrides: "color=black font_size=10pt font_weight=medium just=left cellheight=0.4cm fontfamily=arial");
            obj.row_end();
			%end;
run;
	%end;
%mend;

 

Tom
Super User Tom
Super User

So now you are reading the first &I observations of MYDATA and writing the macro variable NMBR &I times.  So only the last value is left when the data step ends.

Then you are reading the first N observations of MYDATE another time and this time writing &I*&NMBR macro variables, only the last &NMBR versions will have any meaning.

 

If you really wanted to create macro variables named NMBR and MACRO_VAR1 to &I then you only need to run only ONE iteration of the data step.

data _null_;
  set mydata (firstobs=&i);
  CALL SYMPUTX('nmbr',nmbr);
  array myvars x: ;
  do index=1 to nmbr ;
    CALL SYMPUTX(cats('macro_var'),index),myvars[index]);
  end;
  stop;
run;

Before trying to use the macro language to generate SAS code you need to understand what SAS code you want to generate.

Kurt_Bremser
Super User

So you want to create a series of reports from data. Is the data originally in one dataset? And is your "control" dataset somehow extracted from this dataset?

 

Don't get fixated on a method. Instead, take a step back and let us look at the big picture: how does the the source data look like, and what kind of report(s) do you need?

 

Supplying usable example data (in data step(s) with datalines) will greatly speed up the process of finding a solution.

SASdevAnneMarie
Barite | Level 11

Thank you, Kurt,

 

Finally, I did that  and this works :

 


%macro myvariables;
	%do i=1 %to &nb_obs.;

	data _NULL_;
			set mydata (obs=&i);
			CALL SYMPUTX(COMPRESS('nmbr'),nmbr);
		run;


		data _NULL_;
			set mydata (obs=&i);
			CALL SYMPUTX(COMPRESS('nmbr'),nmbr);
			%do j=1 %to &nmbr.;
				CALL SYMPUTX(COMPRESS("Macro_var&j."),X&j.);
			%end;
		run;

		ODS PDF FILE = "XXXX\&id..pdf" dpi=1800 startpage=no;

	data _NULL_;
			declare odsout obj();
			/****Lines of Code ods ****/
            %do i=k %to %nmbr.;
            obj.row_start();  
            obj.format_cell(text: " XXXX: ",  overrides: "just=left color=black font_size=10pt font_weight=medium cellheight=0.4cm leftmargin=1cm cellwidth=8.9cm fontfamily=arial");
			obj.format_cell(data: "&&Macro_var&k.", overrides: "color=black font_size=10pt font_weight=medium just=left cellheight=0.4cm fontfamily=arial");
            obj.row_end();
			%end;
run;
	%end;
%mend;

 

SAS Innovate 2025: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

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.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 13 replies
  • 2576 views
  • 10 likes
  • 5 in conversation