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

Hi,

 

As a way of trying to get my head around Proc TEMPLATE and what it can do for me I have been experimenting a bit, but have now got stuck. In short, I need a hand to amend the ODS table template that controls how the Attributes report in a Proc CONTENTS appears.

 

I started off tinkering with the ODS table template that controls how the report from Proc MEANS appears to the user (shown left, below).

 

I have successfully created a private copy of an out-of-the-box table template for Base.Summary and tweeked the definition so that when I invoke that private template with a Proc MEANS, the output shows my minor customisations (shown below, right).

2019-01-01_13h07_01.png2019-01-01_13h07_18.png

 

So far, so good.I've applied a modified table header (text and background colour), also changed the column header for [N Obs] to read [Count of Rows] and assigned magenta to the colour for the values in that column. Finally, I applied a nice COMMA14.2 format to the values in the [Sum] column.

 

The changes to the format of the values shown in the [N Obs] and [Sum] columns and the table headings were done very much by looking at the source code of the original table template and then feeling around in the dark, but I was pretty chuffed by what I achieved.

 

In order to apply the comma separators to the values in the [Sum] column, I made the following customisation to the table template:

The out-of-the-box table template had this definition for the [Sum] column:

      define sum;                                                             
         header = "Sum";  
         generic;                                                             
      end;

My private table template then had the format line added and all was well when it was invoked:

      define sum;                                                             
         header = "Sum";  
         format=comma14.2;
         generic;                                                             
      end;

 

Now, what I really want to do next is to create a private table template that will allow me to customise the values that I see in the output from a simple Proc CONTENTS. The screenshot below is from the Attributes report of Proc CONTENTS.

2019-01-01_13h17_02.png

 

I would like to find out how I can amend the out-of-the-box table template Base.Contents.Attributes so that the value adjacent to the [Observations] label appears with comma separators and the values adjacent to the [Created] and [Last Modified] labels appear in a different datetime format to what is shown above. For this exercise, I am not worried about the actual format, I just what to know how I can amend the out-of-the-box table template to satisfy my needs.

 

I am a bit stuck because the out-of-the-box table template Base.Contents.Attributes defines two pairs of columns as LABEL1, CVALUE1 and LABEL2, CVALUE2 and as each of the CVALUEn columns contain a mixture of numeric values (such as Observations, Variables, Observation Length) and character values (such as Compressed and Sorted), I have been unsuccessful in pulling off my simple trick from Proc MEANS whereby I simply apply a numeric format to the definition.

 

If anyone out there in the SAS world has either done this already or might be able to explain how to do it I would greatly appreciate it.

 

Many thanks,

 

Downunder Dave

Wellington

 

1 ACCEPTED SOLUTION

Accepted Solutions
Cynthia_sas
SAS Super FREQ

Hi:
  I'm not sure I'd bother with a Table Template solution to this. And, honestly, I'm not sure it's possible to do what you want. You can change cell styles conditionally in a Table template, but you can't change cell values conditionally in a Table Template.

  A Table template does not have conditional logic which is what you'd need to apply a different format to the nVALUE1 column based on the value of the Label1 column or changing nvalue2 based on the Label2 column. A tagset template has that type of conditional logic, but not every destination uses a tagset template and cell style has a syntax for changing that is limited to style overrides.

  I would be more likely to write the output to a dataset using ODS OUTPUT and then use PROC REPORT to customize the various nValue columns based on the Label1 and Label2 columns.

  You did a good job figuring out the Table templates for your PROC MEANS. So you should be proud of that accomplishment.

  When I got deeply into TABLE templates was more for use in the DATA step, as shown here:
 http://www2.sas.com/proceedings/sugi30/088-30.pdf -- otherwise, most of my template work has been in STYLE templates, with a little side journey into TAGSET templates and GRAPH templates: https://support.sas.com/resources/papers/proceedings09/227-2009.pdf . However, these are older papers and although the template syntax hasn't changed that much -- I wouldn't bother to fiddle with Tagset templates anymore since the developers started using Lua for them. And, since the Report Writing Interface became production, I hardly find a need to fiddle with Table Templates anymore.

  I'll see if I have an example of using PROC REPORT after the fact to change values and post it.

Cynthia

 

1) starting attributes object for SASHELP.HEART and whole object from ODS OUTPUT (yellow highlights were added to screen shot to highlight where changes will be made):

reformat_contents_sashelp_heart1.png

 

2) using formats and changing cell values conditionally using PROC REPORT (yellow highlight shows what was changed):

reformat_contents_sashelp_heart2.png

 

3) code

ods output attributes=work.attr;
ods select attributes;
proc contents data=sashelp.heart;
  title 'Original Attributes Object for SASHELP.HEART';
run;
 
proc print data=work.attr;
  title 'what is in the attributes object';
run;
 
ods select variables;
proc contents data=work.attr;
  title 'notice length of cvalue2 -- sashelp.heart has 5209 obs, so length is $4';
  title2 'which means that cvalue2 is too small to hold a comma formatted number';
run;
 
title 'PROC REPORT changing values in COMPUTE block';
title2 'using nvalue1 and nvalue2 to set new values for cvalue1 and showvalue2';
title3 'cannot use cvalue2 because length is too small to add comma';
proc report data=work.attr noheader;
  column Label1 nValue1 nValue2 cValue1  Label2 cValue2 showvalue2;
  define label1 / style(column)=Header;
  define nvalue1 / display noprint;
  define nvalue2 / display noprint;
  define cvalue1 / display;
  define label2 / style(column)=Header;
  define cvalue2 / display noprint;
  define showvalue2 / computed;
  compute showvalue2/character length=20;
     ** change all values in the COMPUTE block for the 
        last item on the report row;
     showvalue2 = cvalue2; 
     if label2='Observations' then do;
        showvalue2 = put(nvalue2,comma10.);
	 end;
	 if label1='Created' or label1 = 'Last Modified' then do;
	    cvalue1 = put(datepart(nvalue1),date9.);
	 end;
  endcomp; 
run;
title;

View solution in original post

2 REPLIES 2
Cynthia_sas
SAS Super FREQ

Hi:
  I'm not sure I'd bother with a Table Template solution to this. And, honestly, I'm not sure it's possible to do what you want. You can change cell styles conditionally in a Table template, but you can't change cell values conditionally in a Table Template.

  A Table template does not have conditional logic which is what you'd need to apply a different format to the nVALUE1 column based on the value of the Label1 column or changing nvalue2 based on the Label2 column. A tagset template has that type of conditional logic, but not every destination uses a tagset template and cell style has a syntax for changing that is limited to style overrides.

  I would be more likely to write the output to a dataset using ODS OUTPUT and then use PROC REPORT to customize the various nValue columns based on the Label1 and Label2 columns.

  You did a good job figuring out the Table templates for your PROC MEANS. So you should be proud of that accomplishment.

  When I got deeply into TABLE templates was more for use in the DATA step, as shown here:
 http://www2.sas.com/proceedings/sugi30/088-30.pdf -- otherwise, most of my template work has been in STYLE templates, with a little side journey into TAGSET templates and GRAPH templates: https://support.sas.com/resources/papers/proceedings09/227-2009.pdf . However, these are older papers and although the template syntax hasn't changed that much -- I wouldn't bother to fiddle with Tagset templates anymore since the developers started using Lua for them. And, since the Report Writing Interface became production, I hardly find a need to fiddle with Table Templates anymore.

  I'll see if I have an example of using PROC REPORT after the fact to change values and post it.

Cynthia

 

1) starting attributes object for SASHELP.HEART and whole object from ODS OUTPUT (yellow highlights were added to screen shot to highlight where changes will be made):

reformat_contents_sashelp_heart1.png

 

2) using formats and changing cell values conditionally using PROC REPORT (yellow highlight shows what was changed):

reformat_contents_sashelp_heart2.png

 

3) code

ods output attributes=work.attr;
ods select attributes;
proc contents data=sashelp.heart;
  title 'Original Attributes Object for SASHELP.HEART';
run;
 
proc print data=work.attr;
  title 'what is in the attributes object';
run;
 
ods select variables;
proc contents data=work.attr;
  title 'notice length of cvalue2 -- sashelp.heart has 5209 obs, so length is $4';
  title2 'which means that cvalue2 is too small to hold a comma formatted number';
run;
 
title 'PROC REPORT changing values in COMPUTE block';
title2 'using nvalue1 and nvalue2 to set new values for cvalue1 and showvalue2';
title3 'cannot use cvalue2 because length is too small to add comma';
proc report data=work.attr noheader;
  column Label1 nValue1 nValue2 cValue1  Label2 cValue2 showvalue2;
  define label1 / style(column)=Header;
  define nvalue1 / display noprint;
  define nvalue2 / display noprint;
  define cvalue1 / display;
  define label2 / style(column)=Header;
  define cvalue2 / display noprint;
  define showvalue2 / computed;
  compute showvalue2/character length=20;
     ** change all values in the COMPUTE block for the 
        last item on the report row;
     showvalue2 = cvalue2; 
     if label2='Observations' then do;
        showvalue2 = put(nvalue2,comma10.);
	 end;
	 if label1='Created' or label1 = 'Last Modified' then do;
	    cvalue1 = put(datepart(nvalue1),date9.);
	 end;
  endcomp; 
run;
title;
DaveShea
Lapis Lazuli | Level 10

Cynthia,

 

Thank you very much for posting this example code. After playing around with it  for a couple of days I have now got a much better understanding of how ODS and Proc CONTENTS work together. It is one of the pieces of the ODS puzzle that I had not been able to find up until now. I now have a rock-hard example to play with to explore ODS further.

 

I agree, that the trying to tweek the table template for the Attributes report of Proc CONTENTS is going to be a dead-end, but your example code that pipes the underlying procedure data out to a dataset and then using Proc REPORT is genius.

 

Thank you very much for all of your help.

 

Downunder Dave

Wellington

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
  • 2 replies
  • 1593 views
  • 1 like
  • 2 in conversation