BookmarkSubscribeRSS Feed
deleted_user
Not applicable
I have the following report procedure

PROC REPORT HEADLINE HEADSKIP;
COLUMN NAM DTT TYP PPP DES SDC TIM;
DEFINE NAM / GROUP WIDTH=5 'Name';
DEFINE DTT / 'Date' DISPLAY FORMAT=mmddyy10. WIDTH=10 CENTER;
DEFINE TYP / 'Type';
DEFINE DES / 'Contact/Account' WIDTH=15;
DEFINE PPP / 'Entry Type' WIDTH=5;
DEFINE MEM / 'Type';
DEFINE SDC / DISPLAY WIDTH=65 FLOW 'Entry Description';
DEFINE TIM / 'Time Spent' ANALYSIS SUM;

* BREAK AFTER NAM / DOL SKIP SUPPRESS;
BREAK AFTER NAM / SUMMARIZE DOL SKIP SUPPRESS;
BY NAM;
TITLE1 'Weekly Sales Activities';
TITLE2 ' ';


Which simply outputs to text however it looks cluttered because the variable SDC can be up to 200 characters. Is there a way to insert a blank line or double blank line between each observation?
10 REPLIES 10
Cynthia_sas
SAS Super FREQ
Hi:
Are you going to stick with the LISTING destination or OUTPUT window text output??? Or are you going to be using the Output Delivery System (ODS RTF, ODS PDF or ODS HTML) to ultimately deliver your report????

PROC PRINT has the BLANKLINE option, that allows you to select where a blank line should be inserted into the output for ease of reading.

If you used ODS, your output would be put into a table structure and generally, the table's interior lines would supply a divider between observations without taking up the same amount of space as a blank line. If you still wanted a blank line, then you would have to either write the blank line with a LINE statement (based on the value of a group or order variable changing) or you'd have to use another BREAK AFTER with SKIP in the LISTING destination. The SKIP option (like the WIDTH, FLOW, HEADLINE and HEADSKIP options) does not work for ODS destinations, which is why you'd have to code the LINE statement to insert your own blank lines with ODS.

The approach you use will depend on the destination that you choose. In this previous forum posting, you indicated that you were interested in PDF output:
http://support.sas.com/forums/message.jspa?messageID=36424#36424

Is that still the case? Many of the options that you are using in this program are not used by ODS PDF.

cynthia
deleted_user
Not applicable
Hi Cynthia,

I've been digging around since that last thread and found out how the sys admin has the server setup. When I build a report it's actually only text output, possibly RTF although I'm I don't think so, then it uses a PDF converter from text to PDF and that's why I was under the impression that I was using ODS PDF.

I was not able to get the format I wanted using PROC PRINT that's why I went PROC REPORT and after a series of trial and error with different options I got the output that I wanted, somewhat anyway. The only thing left is been able to add the white line in between observations. So looks like BLANKLINE is not an option for me. Is writing a LINE statement as you mentioned an option for me? Could you explain that in more detail please if you think it's something that I can use. You also mentioned been able to use another BREAK AFTER with SKIP in the LISTING destination?
Cynthia_sas
SAS Super FREQ
Hi:
There's a difference between plain TEXT output, such as that which is written to the LISTING destination or OUTPUT window and the type of output created by ODS RTF (which is an ASCII text file, but which contains RTF control strings).

Before the introduction of the Output Delivery System, many companies captured their LISTING output to a plain ASCII text file and then used some other process or program (sometimes in SAS, sometimes not) to write RTF control strings around the output report lines. Once -this- RTF file was created, they then imported the file into publishing programs like Ventura Publishing or Adobe PageMaker or Quark in order to "tweak" the report or combine several different report pieces together and then this intermediate file was written to PDF format.

You really need to know whether you're creating LISTING output or RTF output from your program. If you do not see any ODS RTF statements in your program, then you might see a macro call something like %WRITERTF (as described here: http://www2.sas.com/proceedings/sugi28/143-28.pdf ), then it's possible you are not using the Output Delivery System at all.

If your LISTING output is being written to an ASCII text file, then you might see PROC PRINTTO statements in your program. Or you might see ODS LISTING FILE= statements. Or, if you are running on the mainframe, you might find that your SYSOUT DD statement is writing to a DSN= file instead of to SYSOUT=A.

The BOX option in PROC REPORT, in the LISTING destination, will draw a box around and between report rows. It draws the box with the FORMCHAR= characters --- primitive, but effective as a way to show dividers between report rows.

When you use PROC REPORT, you are building REPORT ROWS -- those report rows may contain observations (if you are creating a detail report), but what you want to do is put blank lines between report rows. You can only use the BREAK statement or ORDER or GROUP usage variables. So if your observation identifier (such as NAM) occurs one time for every observation, then you can simply use:
[pre]
BREAK AFTER NAM/ SKIP;
[/pre]

...to put a blank line after every value of NAM. But, if NAM is the same for a GROUP of observations, then this is not going to put a blank line between the report row that displays every observation. Since NAM is both a GROUP variable and a BY variable in your code, I suspect that more than one observation will be grouped for each NAM.

The issue now is that all the rest of your variables seem to be DISPLAY usage, except for TIM. This means that you cannot perform BREAK processing on any other variable. You can only have a BREAK statement or a COMPUTE AFTER block with a LINE statement for a GROUP or ORDER variable.

So, you know your DATA -- which variable in the data represents each observation?? DATE?? TYP??? MEM??? If you do not have an "observation level" variable to use, you might need to make one in a DATA step program prior to the PROC REPORT. Using _N_ is a handy way to capture a unique obs number. Something like what is shown in the (untested) program below.

Before you go too far down this road, I would recommend that you take the time to understand exactly what type of output you're creating and what is happening to it. It seem like a lot of extra work to bypass ODS PDF or ODS RTF in favor of writing and then post-processing an ASCII text file.

cynthia
[pre]
** capture the _n_ into a variable called OBSNO;
data final;
set mydata; /* you did not have DATA= in your PROC REPORT so I made this name up */
obsno = _n_;
run;

** Now, use OBSNO as an ORDER variable so a SKIP can be added;
** but use NOPRINT for OBSNO so you do not see it on the report.;
PROC REPORT data=final nowd HEADLINE HEADSKIP;
COLUMN NAM obsno DTT TYP PPP DES SDC TIM;
DEFINE NAM / GROUP WIDTH=5 'Name';
DEFINE obsno / ORDER NOPRINT;
DEFINE DTT / 'Date' DISPLAY FORMAT=mmddyy10. WIDTH=10 CENTER;
DEFINE TYP / 'Type';
DEFINE DES / 'Contact/Account' WIDTH=15;
DEFINE PPP / 'Entry Type' WIDTH=5;
DEFINE MEM / 'Type';
DEFINE SDC / DISPLAY WIDTH=65 FLOW 'Entry Description';
DEFINE TIM / 'Time Spent' ANALYSIS SUM;

BREAK AFTER NAM / SUMMARIZE DOL SKIP SUPPRESS;
BREAK AFTER OBSNO / SKIP;
BY NAM;
TITLE1 'Weekly Sales Activities';
TITLE2 ' ';
run;

[/pre]
deleted_user
Not applicable
Hi,

I'm sure this is just text output there are is no ODS RTF, PRCO PRINTTO or macros been used. This is a very old system and all of our reports use PROC PRINT to output a text file which then uses a perl program called ascii2pdf to the conversion. Due to the way the server is setup I'm unfortunately stuck with a limited set of tools. Below is the whole program

LIBNAME SAS '/home/sas/data';
*--------------------------------------------------------------------*;
* OPTIONS *;
*--------------------------------------------------------------------*;
OPTIONS MACROGEN SYMBOLGEN SOURCE SOURCE2;
**********************************************************************;
* GET TIME *;
**********************************************************************;
DATA TIM3A;
SET SAS.TIMECURR;
IF NAM EQ 'ABC' OR
NAM EQ 'DEF';
IF DTT GT TODAY()-WEEKDAY(TODAY())-7;

**********************************************************************;
* PERSON - DATE - MEMBER DES AND PRINT REPORT *;
**********************************************************************;
PROC SORT DATA=TIM3A;
BY NAM DTT MEM DES TYP;
DATA TIM1D;
SET TIM3A;
BY NAM DTT MEM DES TYP;
IF DTT GT TODAY() THEN DELETE;

IF FIRST.TYP THEN TIMT = 0;
TIMT + TIM;
WEEK = DTT - WEEKDAY(DTT) + 1;
IF LAST.TYP THEN DO;
TIM = TIMT;
OUTPUT;
END;
DROP TIMT;
FORMAT WEEK YYMMDD6.;

PROC REPORT HEADLINE HEADSKIP;
COLUMN NAM DTT TYP PPP DES SDC TIM;
DEFINE NAM / GROUP WIDTH=5 'Name';
DEFINE DTT / 'Date' DISPLAY FORMAT=mmddyy10. WIDTH=10 CENTER;
DEFINE TYP / 'Type';
DEFINE DES / 'Contact/Accoount' WIDTH=15;
DEFINE PPP / 'Entry Type' WIDTH=5;
DEFINE MEM / 'Type';
DEFINE SDC / DISPLAY WIDTH=65 FLOW 'Entry Description';
DEFINE TIM / 'Time Spent' ANALYSIS SUM;

BREAK AFTER NAM / SUMMARIZE DOL SKIP SUPPRESS;
BY NAM;
TITLE1 'Weekly Sales Activities';
TITLE2 ' ';


Sample output:(Hopefully it wont get distorted)



ABC 06/01/2010 CALL ACCT Chris Morgan Spoke with 0.2
06/01/2010 CALL ACCT Chrystie Doe Sometimes this can be very long output. It can be up 0.2
to 200 characters and usually will run up to 3 or 4
lines.
06/01/2010 CALL ACCT Dale Jame Left voicemail for Dale 0.2


So data is grouped by NAM but as you can see the 5th column, SDC, can be in multiple lines in the same column. Would using _N_ still sound like the way to go? You also mentioned that because TIM has computations I cannot BREAK after it, would there be a better way to compute this column?

What do you mean with "observation level"?
Cynthia_sas
SAS Super FREQ
Here's what I mean by "observation level" variable. Consider this "fake" data:
[pre]
var1 var2 longvar
AAA Janet abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
AAA Mary abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
AAA Philip abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
AAA Ronald abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
AAA William abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
BBB Janet abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
BBB Mary abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
BBB Philip abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
BBB Ronald abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
BBB William abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
[/pre]

There is nothing UNIQUE about an observation to distinguish it from any other observation. Possibly VAR2 taken with VAR1 would be unique enough but
I can't guarantee that I have only unique values in VAR2. For example, I could have 2 "Ronald" observations for VAR1 of 'AAA', which would then make
me need something to distinguish the first "AAA/Ronald" observation from another "AAA/Ronald" observation.

So, let's make some fake data where I have multiple "Ronald" observations.
Now this is what my data looks like:
[pre]
obsno var1 var2 longvar
1 AAA Janet abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
2 AAA Mary abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
3 AAA Philip abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
4 AAA Ronald abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
5 AAA Ronald abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
6 AAA William abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
7 BBB Janet abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
8 BBB Mary abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
9 BBB Philip abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
10 BBB Ronald abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
11 BBB Ronald abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
12 BBB William abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
13 BBB William abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234 abcdefghij klmnopqrst uvwxyz1234
[/pre]

If you run and compare the 3 PROC REPORT outputs, you will see the difference between how my "RONALD" rows are treated. In order for program 3 to work, I MUST create the OBSNO variable in a DATA step program because
_N_ only exists for the duration of a program. The _N_ is being captured in the DATA step. Also, when you say that SDC takes up multiple lines...you have to separate the way the DATA is stored from the way the DATA
looks on the REPORT. In the internal SAS data set storage, the variable SDC will look exactly as my variable LONGVAR looks -- like one long continuous string of text.
How SDC looks on the report is different from how SDC is internally stored in the dataset.

cynthia
[pre]
data makelong(keep=obsno var1 var2 longvar);
length obsno 8 var1 $3 var2 $8 longvar $200;
infile datalines;
input var1 $ var2 $ ;
obsno = _n_;
tmp = 'abcdefghij klmnopqrst uvwxyz1234 ';
longvar = tmp || tmp || tmp || tmp ||tmp || tmp;
output;
return;
datalines;
AAA Janet
AAA Mary
AAA Philip
AAA Ronald
AAA Ronald
AAA William
BBB Janet
BBB Mary
BBB Philip
BBB Ronald
BBB Ronald
BBB William
BBB William
;
run;

proc sort data=makelong;
by var1 var2;
run;

ods listing;
options nocenter;
proc print data=makelong noobs;
title 'What is in MAKELONG dataset';
run;

options linesize=256;
ods listing;

proc report data=makelong nowd HEADLINE HEADSKIP;
title '1) Use Break After for an ORDER variable (SKIP gives 1 blank line in the LISTING output)';
title2 'OK until we get to the multiple RONALD rows';
column var1 var2 longvar;
define var1 /order width=4;
define var2 /order;
define longvar / display width=50 flow;
break after var1 / dol skip suppress;
break after var2 / skip;
run;

proc report data=makelong nowd HEADLINE HEADSKIP;
title '2) Use a COMPUTE block to put 3 empty lines between each VAR2';
title2 'same problem as above';
column var1 var2 longvar;
define var1 /order width=4;
define var2 /order;
define longvar / display width=50 flow;
break after var1 / dol skip suppress;
compute after var2;
line ' ';
line ' ';
line ' ';
endcomp;
run;
title;

proc report data=makelong nowd HEADLINE HEADSKIP;
title '3) If you do not want to show your "observation level" variable';
title2 'you can NOPRINT the var, and still use it in a BREAK statement';
title3 'and the obsno variable makes sure that the dup RONALD rows are treated separately';
column var1 obsno var2 longvar;
define var1 /order width=4;
define obsno / order noprint;
define var2 /display;
define longvar / display width=50 flow;
break after var1 / dol skip suppress;
break after obsno / skip;
run;
title;


[/pre]
deleted_user
Not applicable
Thank you for the great example. Since I'm not using ODS can I still use the COMPUTE LINE or _N_ variables?
Cynthia_sas
SAS Super FREQ
Hi:
_N_ "belongs" to the DATA step. It is a counter for how many times the DATA step has looped through the statements or executed the statements. Generally, this _N_ corresponds to one loop for each observation, which is why it is so handy to use as a "counter" variable or "observation" counter. _N_ has nothing to do with ODS.

If you examine my program, the only ODS statment is ODS LISTING; which ensures that the LISTING destination is open and receiving output. The COMPUTE BLOCK and the LINE statement "belong" to PROC REPORT. They will work no matter which destination you use. They may work -differently- in the various destinations, but the statements themselves are part of PROC REPORT.

If you run my code, you should be able to compare the look from reports #1 and #2 with report output #3 in the LISTING destination or OUTPUT window.

cynthia
deleted_user
Not applicable
Hi,

I don't have a DATA step besides this


DATA TIM3A;
SET SAS.TIMECURR;
IF NAM EQ 'ABC' OR
NAM EQ 'DEF';
IF DTT GT TODAY()-WEEKDAY(TODAY())-7;

As you can tell I'm using a SAS dataset that was previously created by another process. Can I still use _N_? I tried incorporating to my existing program but I couldn't get it to work, how would I incorporate it?
Cynthia_sas
SAS Super FREQ
You would need to add this statement:
[pre]
obsno = _n_;
[/pre]

to your DATA step program that creates the TIM3A dataset (probably you'd add it right underneath the SET statement) ....OR you'd have to add a new DATA step program immediately before the PROC REPORT step. You will also have to modify the PROC REPORT step to use the new OBSNO variable.

cynthia
deleted_user
Not applicable
Cynthia,

_n_ did the trick! thank you for all your help.

SAS Innovate 2025: Register Now

Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!

What is Bayesian Analysis?

Learn the difference between classical and Bayesian statistical approaches and see a few PROC examples to perform Bayesian analysis in this video.

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