Hi All,
I have a lengthy comment getting into a macro variable from an external text file. Now, I am just writing the value of macro variable into a rtf file using PUT statement .
The requirement is, I need to align the data properly and also it should fit in a box. Thanks in advance.
Hi:
Most of what you describe can be produced by using a Macro program definition. Where I have my %let statements, you would have some kind of looping construct that would cause the program to create 1 RTF file for each of your subjects. That part is fairly straightforward.
Your requirement for suppressing null strings is easily done with IF statements, my program did not get that complicated, but instead of doing a simple CATX, as I show, you could include another test for the value of any line's macro variable to be missing or not. Something like this, if you were going to do your test inside a DATA step:
else if linenum = 5 then do;
if "&stdt1" = " " then linestr = " ";
else if "&stdt1" gt " " then linestr = catx(' ',linestr,"&stdt1")
end;
or, you might choose to make the test part of the macro code and use the presence or absence of the macro variable to determine the code that was written. That is more of a design approach.
For your question about having the comments in an Excel file, my suggestion would be that there are a couple of ways to deal with this. You could either read the subject file and the comment file separately and merge them together ahead of time (before your loop) so that you have the appropriate comment in the same data file with the subjects -- 100 subjects and 5 variables or 6 variables -- even lengthy strings is not going to be too big to process. Or, you could read the Excel file one time for each subject and extract only the comment you need for that subject inside the loop to produce the RTF file. My tendency would be to use the first approach. I'm a fan of getting all my data organized the way I want before I start a reporting step.
I envision 1 PROC REPORT step if you get all the data organized into 1 dataset. However, if the data is in 2 datasets and you need 2 different reports, then I would envision something like this:
** looping structure for each subject;
ods rtf file="&&subj&i..rtf" startpage=no;
** first proc report for main subject information;
** can use a WHERE statement to restrict the PROC REPORT to one subject;
** second proc report for information from Excel file, which you will;
** need to get into a SAS dataset before this step;
** can also use a WHERE statement to restrict the PROC REPORT to the comment ;
** for one subject;
ods rtf close;
** end looping structure;
It really comes down to what you feel comfortable with and what can be maintained over time. For example, you could want 2 types of report -- it's not clear to me what you might want to see produced. Here is an example:
cynthia
Examples?
There are two parts here, first importing the data from the text file into a SAS dataset. I assume that is fine from your side?
Next is outputting to the RTF file, is there a reason you cannot use proc report - as that has border controls, alignment and such like built in, would make life a lot easier?
Or is it for a title, if so you can apply styles also to that. Without some information its really hard to guess.
Thanks for reply.
I am writing some data (specific columns for each subject which are fetched from a dataset into macro variables) already through PUT statement into a external RTF file.
At last I have to append Comments (This value I will fetch it from a different file into a macro variable). I am trying to write the value from a macro variable using PUT statement after all columns printed through put statements as mentioned above.
for comments value, I need to display with proper alignment with in a box, just to look good. Now since comments value is a lengthy statement and its not getting displayed properly in the RTF file.
as you mentioned, we may try to display the macro variable with in TITLE/FOOTNTE statement with styles applied to it. please suggest how can we do this. Just assume a macro variable with lengthy single statement in it as a value and we have to write to a RTF file and the value should be with in box.
Please consider &comm. has the value, you can see Put "&comm."; in the code below.
%MACRO AEDATA();
%DO i=1 %to &total.;
data _NULL_;
file "XXXXXXXXX\testf&i..rtf" print ;
Title;
put ' ';
put ' ';
PUT 'SUBECT ID :' "&&sub&i.";
%IF %sysfunc(length("&&aetrm&i.")) > 2 %then %do;
put 'AE Term is :' "&&aetrm&i.";
%end;
%IF %sysfunc(length(&&llt&i.)) > 0 %then %do;
put 'AE Llt is :' "&&llt&i.";
%end;
put 'Start date is :' "&&stdt&i.";
put '*************************';
put '*************************';
put '*************************';
put '*************************';
put "&comm.";
run;
%END;
%MEND;
%aedata;
Without seeing any data it is difficult to suggest something, except: don't use macro language if you don't have to. RTF-files can be created by ods rtf, there is hardly any need to do this manually with data steps.
As @andreas_lds has said, its far simpler to use the tools built to generate outputs.
It looks to me like your creating some sort of patient profile as a text file. You can achieve the same thing, just get all your data into a dataset and then use proc report - you can add borders, change fonts etc. and with ods rtf create an rtf file from it. Just putting plain text out to a file, you would need to have a good level of RTF tagset knowledge to apply all the relevant tags.
Look at PROC ODSTEXT.
Or, in addition to Reeza's suggestion, you could use PROC REPORT to generate something like this:
Which was created using ODS RTF with PROC REPORT instead of using PUT statements.
Here's the sample code -- instead of putting the program in a loop, I just hardcoded values for the macro variables.
%let sub1 = Lewis Carroll ;
%let aetrm1 = Term 1 ;
%let llt1 = Something Else ;
%let stdt1 = January 4, 2018 ;
%let comm1= %str(Twas brillig and the slithy toves did gyre and gimble in the wabe. All mimsy were the borogroves and the momeraths outgrabe.);
data comment;
length linestr $1000;
infile datalines dlm=',' dsd;
input linenum linestr $;
if linenum = 1 then linestr = catx(' ',linestr,"&sub1");
else if linenum = 3 then linestr = catx(' ',linestr,"&aetrm1");
else if linenum = 4 then linestr = catx(' ',linestr,"&llt1");
else if linenum = 5 then linestr = catx(' ',linestr,"&stdt1");
else if linenum = 7 then linestr = catx(' ',linestr,"&comm1");
return;
datalines;
1,'^{style[fontweight=bold]SUBECT ID:}'
2,'^{newline 2}'
3,"^{style[fontweight=bold]AE Term is:} "
4,"^{style[fontweight=bold]AE Llt is:}"
5,"^{style[fontweight=bold]Start date is:}"
6,"*************************"
7,"^{style[fontweight=bold]Comment:}"
;
run;
ods escapechar='^';
options nodate number;
title;
ods rtf file="c:\temp\testf1.rtf" style=rtf;
proc report data=comment noheader
style(report)={rules=none frame=box cellspacing=0 width=5in};
column linenum linestr;
define linenum / order noprint;
define linestr / display;
run;
ods rtf close;
Cynthia
HI Cynthia,
Thanks a lot for your inputs.
I will give what exactly my requirement is,
1. I have a data set with 100 subjects and 5 variables.
2. For each subject I have to display a separate RTF file with the actual data as per data set in the format that you gave in the
previous reply. Also For ex: if there is any field, lets assume 'Start date' is one of the fields and for a specific subject start date is
Null, we should not get that row at all in the rtf file.
3. At last, I have another Excel input where we get reviewer comments in it in a single cell and it could be very lengthy. I have to
append those comments to the respective RTF file subject wise with a neat text alignment and borders to it (Comments should be With in a BOX at the end of the RTF file for each subject with proper format).
please help me on this requirement.
Thanks,
Hari
Hi:
Most of what you describe can be produced by using a Macro program definition. Where I have my %let statements, you would have some kind of looping construct that would cause the program to create 1 RTF file for each of your subjects. That part is fairly straightforward.
Your requirement for suppressing null strings is easily done with IF statements, my program did not get that complicated, but instead of doing a simple CATX, as I show, you could include another test for the value of any line's macro variable to be missing or not. Something like this, if you were going to do your test inside a DATA step:
else if linenum = 5 then do;
if "&stdt1" = " " then linestr = " ";
else if "&stdt1" gt " " then linestr = catx(' ',linestr,"&stdt1")
end;
or, you might choose to make the test part of the macro code and use the presence or absence of the macro variable to determine the code that was written. That is more of a design approach.
For your question about having the comments in an Excel file, my suggestion would be that there are a couple of ways to deal with this. You could either read the subject file and the comment file separately and merge them together ahead of time (before your loop) so that you have the appropriate comment in the same data file with the subjects -- 100 subjects and 5 variables or 6 variables -- even lengthy strings is not going to be too big to process. Or, you could read the Excel file one time for each subject and extract only the comment you need for that subject inside the loop to produce the RTF file. My tendency would be to use the first approach. I'm a fan of getting all my data organized the way I want before I start a reporting step.
I envision 1 PROC REPORT step if you get all the data organized into 1 dataset. However, if the data is in 2 datasets and you need 2 different reports, then I would envision something like this:
** looping structure for each subject;
ods rtf file="&&subj&i..rtf" startpage=no;
** first proc report for main subject information;
** can use a WHERE statement to restrict the PROC REPORT to one subject;
** second proc report for information from Excel file, which you will;
** need to get into a SAS dataset before this step;
** can also use a WHERE statement to restrict the PROC REPORT to the comment ;
** for one subject;
ods rtf close;
** end looping structure;
It really comes down to what you feel comfortable with and what can be maintained over time. For example, you could want 2 types of report -- it's not clear to me what you might want to see produced. Here is an example:
cynthia
Hi:
Without seeing YOUR code, it's hard to suggest how to center the first line. There were 2 different programs for 2 different reports in my screen shot. Basically, I'd need to do a style override and use JUST=C in the style override. But depending on this example you're asking about, I might make the change in an ODS ESCAPECHAR style override or I might have a style override in a DEFINE statement or might have it in a CALL DEFINE statement.
For the program that I used to make that example back in January, I would have needed a CALL DEFINE because I used a LINENUM variable to identify each string on the report. So in example #2 (on the right), I would have needed to do this:
In this example, I used 2 PROC REPORTs to generate the output. The SUBJECT ID: line would need to be centered with a style override in a CALL DEFINE in my program.
Cynthia
Thanks Cynthia. This is what I was expecting and It was helpful.
One more , I actually used Background color , however its coming entire line in the page. I want to restrict it only to the Text that is available . Could you please suggest how to make it.
Ex: My Output should have first line with Heading as "Your Complete Details" aligned Center with some Background color and BG Color should only fit to the Text not the entire Line.
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.