Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

SAS report output to Excel. 1

Status
Not open for further replies.

ChrisW75

Programmer
Dec 22, 2003
727
AU
OK, I've been playing around with trying to output a report to Excel using ODS Tagsets. I found this Tagset on the SAS Support site:-
which didn't work right off, I had to change references to "colwidth" (but not "$colwidth") to "width", and it's producing Excel output quite nicely. I've added in a style sheet to set up colours and stuff, and used options to set up various items (column width is a little problematic, I think the amendment I made wasn't correct, though it did at least allow me to run the template).
All good so far. The only problem I'm still having is setting up the wrapping on the cells.
Anyone got any ideas?

Here's the style template:-
Code:
ods path work.templat(update) sashelp.tmplmst(read) ;

proc template;
define style Styles.VwaExcel;
   parent = styles.default;
   style SystemTitle from SystemTitle /
      background = #FFFFFF
      foreground = #000000
      font_style = Roman
      font_weight = Bold
      font_size = 4
      font_face = "Arial, Helvetica, Sans Serif";
   style SystemFooter from SystemFooter /
      background = #FFFFFF
      foreground = #000000
      font_style = Italic
      font_weight = Bold
      font_size = 3
      font_face = "Arial, Helvetica, Sans Serif";
   style SysTitleAndFooterContainer from SysTitleAndFooterContainer /
      borderwidth = 0
      background = #FFFFFF
      foreground = #FFFFFF
      font_style = Italic
      font_weight = Bold
      font_size = 3
      font_face = "Arial, Helvetica, Sans Serif";
   style Body from Body /
      rightmargin = 8px
      leftmargin = 8px
      background = #FFFFFF
      foreground = #000000
      font_style = Roman
      font_weight = Medium
      font_size = 2
      font = ("Arial, Helvetica, Sans Serif");
   style RowHeader from RowHeader /
      background = #555555
      foreground = #FFFFFF
      font_style = Roman
      font_weight = Bold
      font_size = 2
      font = ("Arial, Helvetica, Sans Serif");
   style Header from Header /
      background = #555555
      bordercolor = #000000
      borderwidth = 1px
      foreground = #FFFFFF
      font_style = Roman
      font_weight = Bold
      font_size = 2
      font = ("Arial, Helvetica, Sans Serif");
   style Data from Data /
      borderwidth = 1px
      bordercolor = #000000
      background = #FFFFFF
      foreground = #000000
      font_style = Roman
      font_weight = Medium
      font_size = 2
      font = ("Arial, Helvetica, Sans Serif");
   style Table from Table /
      cellspacing = 1px
      bordercolor = #000000
      borderwidth = 2px
      background = #FFFFFF
      foreground = #000000
      font_style = Roman
      font_weight = Medium
      font_size = 2
      font = ("Arial, Helvetica, Sans Serif");
end;
run;

And here's the test code I'm using to view the results:-
Code:
ods path work.templat(update) sashelp.tmplmst(read) ;

ods listing close;
*ods tagsets.ExcelXP file="C:\test\daily_rep.xls";
ods tagsets.ExcelXP file="/apps/dw_house/reports/out/srm/chris/daily_rep2.xls"
    options(default_column_width='5,20,30'
            row_repeat='header'
            frozen_headers='yes')
    style=styles.vwaExcel;
*ods html file="C:\test\daily_rep.htm";

title 'Test Report';
title2 'Testing new Tagset';
title3 "EXCELXP";

proc report data=SASHELP.EISMSG
               nowd split='*';
  columns MSGID
          MNEMONIC
          TEXT
          ;
  define MSGID         /order 'Message*ID';
  define MNEMONIC      /display 'Mnemonic'  ;
  define TEXT         /display 'Text*Of*Message' ;
run;

*ods html close;
ods tagsets.ExcelXP close;
ods listing;

The ODS PATH statement is so that SAS writes the templates to my work library rather than my user library, it means that changes made aren't persistent betwen sessions, which means I don't have to worry about screwing up my user settings.

If anyone has played with this stuff and worked it out, or has time to play with it and get some results, please let me know.

If not, at lteast it's a jumping point for you guys to have some fun with ODS.
 
Found another issue though that I've managed to fix. Well, I think it's an issue, and I'm pretty sure it's fixed.

If in a proc report, you do a line statement to put a line out before the table, the tagset tries to calculate how many lines need to be repeated at the beginning (assuming you are using "repeat_rows='header'"). There seems to be a bug in it that causes it to try to start at row -1 instead of row 1. It looks like some brackets are missing from a line of code.
Under "event table_body" locate this line:-
Code:
eval $possible_row_repeat_start $worksheet_row - $row_count - 1;
and change it to
Code:
eval $possible_row_repeat_start $worksheet_row - ($row_count - 1);
This seems to fix this problem.
 
Hi Chris,

I had encountered this problem before and I was wondering what the round about was. Thanks to you, you seem to be going great guns on the EXCEL Tagset. May be this time they wud name the tagset after you :)

Sarav
 
Hi,

I was wondering if the EXCEL XP Tagset can be used to hide certain columns of a dataset akin to hiding certain columns in EXCEL.

I appreciate any suggestions as to accomplish this.

Thanks,
Sarav
 
How much digging have you done into the XML that is produced, and also into the tagset itself? It shouldn't be a major piece of work to implement something to do this for you.
As an example, a common issue I've had over the years exporting data to Excel is that Excel tends to take account numbers with leading zeros and strip them off, which is often not desirable. This tagset is unable to determine the type of a variable (Proc Report and tabulate does not pass this info to the output) so determines it from the contents of the data. So I wrote in a small piece of code to pick up the "HTMLCLASS" of a column so that I could specify a column as a string.

This was added to the end of "define event cell_start" in place of the commented line:-
Code:
 *set $cell_class lowcase(htmlclass);  * CHRISW - Cut in favour of the below section *;


            * CHRISW - put in check to see if HTMLCLASS has been *;
            *  set to TEXT.  This is an in-house fix.           *;
            *  A flag is set so that I can set the data type to STRING *;
            do /if cmp(lowcase(HTMLCLASS), 'text');
               set $cell_class 'data';
               set $cw_dtype 'Y';
            else;
               set $cell_class lowcase(HTMLCLASS);
            done;

and this was added to the end of the "define event value_type;" section :-

Code:
        * CHRISW if my datatype flag is set, set the type to STRING *;
        do /if cmp($cw_dtype, 'Y');
           set $type "String";
           unset $cw_dtype;
        done;

As a result of this, setting up a report like this:-
Code:
/* Open up ExcelXP tagset, set options */
ods tagsets.ExcelXP file="ExcelXP_test.xls"
    options(default_column_width='8,20,30'
            row_repeat='header'
            frozen_headers='yes'
            debug_level='0')
    style=styles.MyExcelStyle;

proc report data=SASHELP.EISMSG(obs=140)
               nowd split='*';
  columns MSGID
          MNEMONIC
          TEXT
          ;
  define MSGID         /order   'Message*ID' format=z8.  style(column)={HTMLCLASS="TEXT"};
  define MNEMONIC      /display 'Mnemonic'  ;
  define TEXT          /display 'Text Of Message' ;

  title 'Test Report';
  title2 'Testing new Tagset';
  title3 "tagset.EXCELXP";

run;

/* Close */
ods tagsets.ExcelXP close;
ods listing;
allows the tagset to change the characteristics of the columns and output the column as a string rather than a number.

If you want to hide a column, you could probably do the exact same thing, the difference being you'd need to find the XML code that tells a column that it is hidden (output your file, manually change the column to hidden, save, then view the file in a text editor) and work out what you need to change to get this to work.
I'd love to have a go at this, but I'm swamped at the moment, maybe one evening I can have a play at home, but I'm not very motivated to work at home. :)

 
Hi Chris,

Thanks for your response and valuable insights. I will certainly try to dig a bit reg. this issue and let you know.

I have in the past seen the issue with EXCEL abt the leading zeros getting truncated in the O/P. Your fix worked great!!!.

Thanks again for your help.

Sarav
 
No worries. Data vs Excel is always a hassle, it's a problem with alot of PC applications these days - They're just too damned clever. If you don't tell them specifically what to do, they'll make assumptions, and it can be very difficult to tell them not to.

As a pointer, here's the bit of the XML you are looking at:-
Code:
 <Worksheet ss:Name="Table 1 - Detailed and or summa">
  <Names>
   <NamedRange ss:Name="Print_Titles"
    ss:RefersTo="='Table 1 - Detailed and or summa'!R1"/>
  </Names>
  <Table ss:ExpandedColumnCount="18" ss:ExpandedRowCount="685" x:FullColumns="1"
   x:FullRows="1" ss:StyleID="s21">
   <Column ss:StyleID="s21" ss:Width="52.5"/>
   <Column ss:StyleID="s21" ss:Hidden="1" ss:AutoFitWidth="0" ss:Width="60"/>
   <Column ss:StyleID="s21" ss:Width="37.5" ss:Span="1"/>
   <Column ss:Index="5" ss:StyleID="s21" ss:Width="60"/>
   <Column ss:StyleID="s21" ss:Width="112.5"/>
   <Column ss:StyleID="s21" ss:Width="60" ss:Span="1"/>
   <Column ss:Index="9" ss:StyleID="s21" ss:Width="150" ss:Span="2"/>
   <Column ss:Index="12" ss:StyleID="s21" ss:Width="120"/>
   <Column ss:StyleID="s21" ss:Width="75"/>
   <Column ss:StyleID="s21" ss:Width="37.5"/>
   <Column ss:StyleID="s21" ss:Width="120"/>
   <Column ss:StyleID="s21" ss:Width="52.5"/>
   <Column ss:StyleID="s21" ss:Width="60" ss:Span="1"/>
   <Row ss:Height="38.25">
    <Cell ss:StyleID="s22"><Data ss:Type="String">......
As you can see, I've hidden the second column. If you do a search in the tagset on the keyword "column" you should be able to start to see where the string is built up. Work out where the Hidden="1" needs to be added, and put in your condition.
Good luck.

Oh, one thing. Make sure that you don't overwrite your "production" version of the tagset when testing, push it out to the work library. I had one version of this tagset that I completely bollixed, and it would cause my SAS session to crash when I tried to use it.
Code:
ods path work.templat(update) sashelp.tmplmst(read) ;
 
I noticed that I didn't address one of your earlier questions about creating multisheet workbooks from SAS. You can do this, but not (as far as I'm aware) using ODS. You can actually specify the Excel Workbook as a library, and write to each individual sheets as a member/dataset. To do this you need to have SAS/Access for PC Files.
Enjoy.
 
Hey Sarav - Did you ever manage to hide an Excel column from ODS?
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top