SAS Latex Destination: Suppress SAS escape chars - sas

When I run the following and output to the ODS Latex desination, SAS escapes the dollar sign and all the brackets, which won't compile in Latex.
How can I output what's in each cell verbatim?
ods escapechar='^';
Proc format;
picture sigstar (round)
low-0.01="***" (NOEDIT)
0.01<-0.05="** " (FILL=' ' PREFIX='')
0.05<-0.10="* " (FILL=' ' PREFIX='')
other= " " (FILL=' ' PREFIX='');
run;
data test;
input mean pvalue;
datalines;
2.50 0.001
3.50 0.05
4.25 0.12
5.00 0.01
;
run;
data test;
set test;
output = cats( put(mean,7.2) , '{$^{', put(pvalue,sigstar.),'}$}' );
format pvalue sigstar.;
drop mean pvalue;
run;
ods tagsets.simplelatex file="test.tex" (notop nobot);
proc print data=test;run;
ods tagsets.simplelatex close;
This outputs the following .text code:
\sassystemtitle[c]{~}
\sascontents[1]{The Print Procedure}
\sascontents[2]{Data Set WORK.TEST}
\begin{longtable}{|r|l|r|}\hline
Obs & output & pvalue\\\hline
\endhead
1 & 2.50\{\$\^\{***\}\$\} & ~\\\hline
2 & 3.50\{\$\^\{**\}\$\} & ~\\\hline
3 & 4.25\{\$\^\{\}\$\} & ~\\\hline
4 & 5.00\{\$\^\{***\}\$\} & ~\\\hline
\end{longtable}
How can I ensure that the second cell in the first row shows 2.50{$^{***}$} and not 2.50\{\$\^\{***\}\$\}? The package I'm using in Latex requires that the stars are surrounded by the chars above, so I just want to output what's in the dataset verbatim.

I don't think you would generally do that in latex tagsets. The point of tagsets is to take normal SAS data and reformat it to make a latex file; what you're doing is manually doing the latex file itself.
This paper shows an example of how you can do something close to what you want; basically, you write the latex file sans-SAS provided graphs or charts, mark where you want the SAS part to go, and effectively paste it in via SAS code. You should also be able to do the writing of the initial latex file in SAS - but don't do it with the tagset, just write it directly in a data step put.
The easiest way, though, may be to simply customize your own latex tagset. Edit the one closest to what you are looking for, and add in your own code as needed. An example is this question.

To avoid these characters being escaped it suffices to edit the following lines at the end of the SimpleLatex tagset:
mapsub = %nrstr("/\%%/$/\&/\~/\\#/{\textunderscore}");
map = %nrstr("%%$&~#_");

Related

SAS - Output text file doesn't contain the exact spaces

I am trying to write a SAS output file from a work table. I inserted blank values ' ' (contains 4 spaces) for the output column. However, when I output to a text file, it shows only one space '' (Example below, Sequence_number)
Is there a way I can include a format and have all the four spaces as output:
Something like below(sequence number should have 4 blank spaces)
create table work.mech_temp_final as
(select put(mt.chq_srl_nbr, z12.) as chq_srl_nbr
, " " as sequence_number
from work.settl mt);
output as
12345| |
rather than
12345||
Instead of using PROC EXPORT, use a data step with a put statement to generate the pipe-delimited file. If you have many variables to output, you can generate the required code using dictionary tables, save it to a macro variable, and use it in the put statement.
data _null_;
set sashelp.cars(rename=(horsepower = chq_srl_nbr) );
file "C:\test.txt";
sequence_number = ' ';
/* Create a header */
if(_N_ = 1) then put 'chq_srl_nbr|sequence_number';
/* Write data in the desired format */
put chq_srl_nbr z12. '|' sequence_number $char4.;
run;
You can write directly write a fixed layout file from Proc SQL by using ODS LISTING. Not recommended for many use cases.
Example:
title;
options nocenter nodate nonumber ps=max formchar=' ';
ods listing file='c:\temp\listing.txt';
proc sql;
select
name format=$CHAR12.
, ' '
, age format=z9.
from sashelp.class;
quit;
ods listing close;

SAS ODS RTF - Best option to embed Title/Footnote into Table

Looking for advice to embed title/footnote as part of the table (see below - making it easier to copy & paste into the different document)
Options explored so far
1) PROC REPORT - COMPUTE PAGE BEFORE (COMPUTE doesn't support justification option and did not find any reliable option to right-align "page x of y" text in title1 e.g. calculating and inserting BLANK space. In addition, I have a need to center align the title)
2) ODS RTF - BODYTITLE and BODYTITLE_AUX option (displays title/footnote as part of the body but not exactly as part of the table - not easy to select as one object)
The SAS ODS inline styling directive ^{PAGEOF} will produce Page x of y output in the output file. Since the output is a Word field you might need to Ctrl-A, F9 to compute the field values when the document is opened.
The RTF destination renders TITLE and FOOTNOTE in the header and footer part of the documents, so tricks are needed to produce per-table 'titles' and 'footers'. From my perspective as a document reader, the best place for PAGEOF would be in the header section and not as part of a table header.
ods escapechar = '^';
title justify=right '^{PAGEOF}';
ODS TEXT= can be used to add paragraphs before and after a Proc TABULATE that does statistical reporting. ODS TEXT= will process inline ODS formatting directives (including PAGEOF). See SAS® 9.4 Output Delivery System: User’s Guide, Fifth Edition, ODS ESCAPECHAR Statement for more info.
ods text='
Narrative before tabular output via ODS TEXT=
^{NEWLINE} Inline styling directives will be processed.
^{NEWLINE} ^{STYLE [color=green]Mix and match}
^{NEWLINE} Let''s see why.
';
ods text='Here you are at ^{STYLE ^{PAGEOF}}';
* This might require Word field computation or print preview to get proper numbers;
Depending on the destination STYLE=, such as ods rtf style=plateau … ODS TEXT might have some outlining or other styling artifacts.
If you are rtf hardcore, ODS TEXT= can also inject rtf codes directly into the destination stream to produce any rtf possible production. In the following example:
legacy style function ^S{attr-name=attr-value} is used to force the text container to be 100% of the page width.
current style function ^{RAW function is used to introduce raw rtf coding. Each { of the actual rtf is introduced using {RAW. Note how the RAWs can be (and are) nested.
ods text = '^S={outputwidth=100% just=c} ^{RAW \rtf1\ansi\deff0^{RAW \fonttbl^{RAW \f0 Arial;}}
\qc\f0\fs20\i\b
This output created with ODS TEXT=\line
Injecting raw RTF coding\line
Such as ^{RAW \cf11\ul colored and underlined}\line
Not for the casual coder.
}';
Some procedures, such as Proc PRINT have style options such as style(table)=[ … ] in which a pretext= and posttext= can be specified, and such texts will be rendered before and after the rtf table -- the texts are not part of the table and would not be 'picked up' in a single click of Word's 'table select' icon (). Also, unfortunately, the pretext= and posttext= values are not processed for ODS styling directives. However, the values can be raw rtf!
PRETEXT demonstrating inline styling is not honored:
proc print
data=sashelp.class (obs=3)
noobs
style(table)=[
pretext='^{STYLE [color=Red]Above 1 ^{NEWLINE}Above 2 - Pretext= is unprocessed ODS directives}'
posttext='^{STYLE [color=Green] Below}'
]
;
run;
PRETEXT demonstrating as raw rtf passed through (when first character is {)
proc print
data=sashelp.class (obs=3)
noobs
style(table)=[
pretext='{\rtf1\ansi\deff0{\fonttbl{\f0 Arial;}}
\qc\f0\fs20\i\b This output created with SAS PRETEXT=
\line Injecting raw RTF coding
\line Not for the casual coder.
}'
posttext='{\rtf1\ansi\deff0{\fonttbl{\f0 Arial;}}
\qc\f0\fs20\i\b This output created with SAS POSTTEXT=
\line Injecting raw RTF coding
\line Not for the casual coder.
}'
]
;
run;
Proc TABULATE does not have a style(table) option, but the TABLE statement does have options:
/ CAPTION= (rendered when OPTION ACCESSIBLETABLE; active)
Note: Caption value is rendered in the page dimension container, so the caption value be overwritten if your TABLE statement has a page dimension in it's crossings.
/ STYLE=[PRETEXT='...' POSTTEXT='...']
Same caveats as mentioned earlier
TABULATE does not:
have any feature that will let you annotate a column header with a row statistic (in this case your (N=###) as part of the category value). A precomputation step that summarizes the crossings to be tabulated will allow you to place a statistic there.
provide any mechanism for inserting a blank or label row that spans the table (such as the LINE statement in Proc REPORT)
Consider this tabulate example with accessibletable on for some medical data. Some of the variables are for:
categorical demographics (such as sex),
continuous measures (such as age cost),
binary flags about state.
data have;
call streaminit(123);
do _n_ = 1 to 1e3 - 300 + rand('uniform',600);
patientId + 1;
category = ceil(rand('uniform',4));
age = 69 + floor(rand('uniform',20));
cost = 500 + floor(rand('uniform',250));
length sex $1;
sex = substr('MF', 1+rand('uniform',2));
array flags flag1-flag3; * some flags asserting some medical state is present;
do over flags; flags = rand('uniform', 4) < _i_; end;
output;
end;
label age = 'Age' cost = 'Cost' sex = 'Sex';
run;
* Precomputation step;
* Use SQL to compute the N= crossings and make that part of a
* new variable that will be in the tabulation column dimension;
proc sql;
create table for_tabulate as
select
*
, catx(' ', 'Category', category, '( n =', count(*), ')')
as category_counted_columnlabel
from have
group by category
;
quit;
Tabulation report
options accessibletable;
proc tabulate data=for_tabulate ;
class
category_counted_columnlabel
sex
/
missing style=[fontsize=18pt]
;
var age cost flag1-flag3;
table
/* row dimension */
age * ( mean std min max median n*f=8.) * [style=[cellwidth=0.75in]]
N='Sex' * sex
cost * ( mean std min max median n*f=8. )
(flag1 - flag3) * mean = '%' * f=percent5.1
,
/* column dimension */
category_counted_columnlabel = ''
/
/* options */
nocellmerge
caption = "This is caption text is ^{STYLE [color=green]from Mars}^{NEWLINE}Next line"
;
run;
For SAS versions before 9.4M6 you can add a page dimension variable (whose value is inlined ODS stylized text to be the caption) to the output of the precomputation step and specify that variable in the table statement. Something like
, '^{NEWLINE}title1' /* SQL step */
|| '^{NEWLINE}title2'
|| '^{NEWLINE}title3'
|| '^{NEWLINE}title4'
as pagedim_title
and
/* tabulate step */
class pagedim_title / style = [background=lightgray fontfamily=Arial textalign=right];
table pagedim_title, …, category_counted_columnlabel = '' … ;
It might be easier to edit the RTF.
read the file and count the occurrences of cf1{Page
{\field{*\fldinst { PAGE }}} of {\field{*\fldinst { NUMPAGES
}}}\cell}
then read again and write modify those lines as you write the new
file.

SAS Export Issue as it is giving additional double quote

I am trying to export SAS data into CSV, sas dataset name is abc here and format is
LINE_NUMBER DESCRIPTION
524JG 24PC AMEFA VINTAGE CUTLERY SET "DUBARRY"
I am using following code.
filename exprt "C:/abc.csv" encoding="utf-8";
proc export data=abc
outfile=exprt
dbms=tab;
run;
output is
LINE_NUMBER DESCRIPTION
524JG "24PC AMEFA VINTAGE CUTLERY SET ""DUBARRY"""
so there is double quote available before and after the description here and additional doble quote is coming after & before DUBARRY word. I have no clue whats happening. Can some one help me to resolve this and make me understand what exatly happening here.
expected result:
LINE_NUMBER DESCRIPTION
524JG 24PC AMEFA VINTAGE CUTLERY SET "DUBARRY"
There is no need to use PROC EXPORT to create a delimited file. You can write it with a simple DATA step. If you want to create your example file then just do not use the DSD option on the FILE statement. But note that depending on the data you are writing that you could create a file that cannot be properly parsed because of extra un-protected delimiters. Also you will have trouble representing missing values.
Let's make a sample dataset we can use to test.
data have ;
input id value cvalue $ name $20. ;
cards;
1 123 A Normal
2 345 B Embedded|delimiter
3 678 C Embedded "quotes"
4 . D Missing value
5 901 . Missing cvalue
;
Essentially PROC EXPORT is writing the data using the DSD option. Like this:
data _null_;
set have ;
file 'myfile.txt' dsd dlm='09'x ;
put (_all_) (+0);
run;
Which will yield a file like this (with pipes replacing the tabs so you can see them).
1|123|A|Normal
2|345|B|"Embedded|delimiter"
3|678|C|"Embedded ""quotes"""
4||D|Missing value
5|901||Missing cvalue
If you just remove DSD option then you get a file like this instead.
1|123|A|Normal
2|345|B|Embedded|delimiter
3|678|C|Embedded "quotes"
4|.|D|Missing value
5|901| |Missing cvalue
Notice how the second line looks like it has 5 values instead of 4, making it impossible to know how to split it into 4 values. Also notice how the missing values have a minimum length of at least one character.
Another way would be to run a data step to convert the normal file that PROC EXPORT generates into the variant format that you want. This might also give you a place to add escape characters to protect special characters if your target format requires them.
data _null_;
infile normal dsd dlm='|' truncover ;
file abnormal dlm='|';
do i=1 to 4 ;
if i>1 then put '|' #;
input field :$32767. #;
field = tranwrd(field,'\','\\');
field = tranwrd(field,'|','\|');
len = lengthn(field);
put field $varying32767. len #;
end;
put;
run;
You could even make this datastep smart enough to count the number of fields on the first row and use that to control the loop so that you wouldn't have to hard code it.

How do I insert vertical space into a PDF document using ODS PDF?

I'm using ODS PDF to create a simple PDF report, but I'm having trouble inserting space between the tables into the PDF file. This is the code so far:
ODS PDF FILE = "test.pdf" STARTPAGE = NEVER;
DATA CLINIC;
INPUT ID $ 1-3
GENDER $ 4
RACE $ 5
HR 6-8
SBP 9-11
DBP 12-14
N_PROC 15-16;
AVE_BP = DBP + (SBP - DBP)/3;
DATALINES;
001MW08013008010
002FW08811007205
003MB05018810002
004FB 10806801
005MW06812208204
006FB101 07404
007FW07810406603
008MW04811207006
009FB07719011009
010FB06616410610
;
ODS PDF TEXT = "MEANS PROCEDURE FOR EVERYONE";
PROC MEANS DATA=CLINIC N STD MEAN;
VAR SBP DBP;
RUN;
ODS PDF TEXT = "TEXT FOR ANALYSIS GOES HERE";
* Vertical space should be inserted here;
ODS PDF TEXT = "MEANS PROCEDURE FOR MEN ONLY";
PROC MEANS DATA=CLINIC N STD MEAN;
WHERE GENDER = "M";
VAR SBP DBP;
RUN;
ODS PDF TEXT = "TEXT FOR ANALYSIS GOES HERE";
ODS PDF CLOSE;
I know that if I remove STARTPAGE = NEVER; the tables will appear on separate pages, but since these are short tables, it doesn't make sense to have each small table on a separate page.
I'm just trying to insert some vertical space into the file where the comments indicate (between the text after the first table and the text before the first table). How can I do this?
You could just add some newlines like:
ods pdf text="^{newline 1}";
The 1 can be replaced with how many lines of white space you want added.
This is assuming your escape character is the same as mine. If not, set it like:
ods escapechar="^";

Creating vertical detail tables

I'm looking for a way to create vertical tables in SAS where the variables are each treated as rows (as opposed to each row being an observation).
For example lets say I have some data for a bunch of companies, some of which is more important than others. It is easy to make proc report spit out a summary table with a few variables like this:
Name Price Shares MarketCap
co1 $5 100 $500
co2 $1 100 $100
co3 $2 200 $400
What I want to do after this is print a page of detailed information for each company which is essentially a table with a column for the description and a column for the value (and maybe a third column for the calculation).
Company 1
Location: CA
CEO: Bob Johnson
Industry: Semiconductors
Shares: 100
Share Price: $5
Market Cap: $500
The only way I can think of to do this in SAS is to basically transpose everything, create a new character variable that has the label (Location, Stock Price, Etc) and a second character variable that has the value and then make a two column report BY company to get a page for each. This is messy since some of the values are numeric and others are character so to get them to display on one column requires creating a new character variable and filling it with text versions of the numeric variables.
I figure there has got to be an easier way to create a vertical table since there are so many easy ways to create the horizontal tables.
There is also this solution which is probably better for your needs.
First create a HTML file that will be used as a template. Wherever you want to put a value, use a macro variable as a placeholder like so:
<html>
<h1> My title is &title </h1><br>
Name: &name <br>
Value of Blah: &blah
</html>
Make it as attractive looking as you like.
Next create a macro that will import the HTML template, replace the placeholders with actual values and save the result to a new file:
/*****************************************************************************
** PROGRAM: MACRO.RESOLVE_FILE.SAS
**
** READS IN A FILE AND REPLACES ANY MACRO REFERENCES IN THE FILE WITH THE
** ACTUAL MACRO VALUES. EG. IF THE FILE WAS AN HTML FILE AND IT CONTAINED
** THE FOLLOWING HTML:
**
** <TITLE>&HTML_TITLE</TITLE>
**
** THEN THE PROGRAM WOULD READ THE FILE IN AND RESOLVE IT SO THAT THE OUTPUT
** LOOKED LIKE THIS:
**
** <TITLE>ROB</TITLE>
**
** ... WHEN THE MACRO VARIABLE "HTML_TITLE" EXISTED AND CONTAINED A VALUE OF
** "ROB". THIS IS USEFUL WHEN YOU NEED TO CREATE "DYNAMIC" HTML FILES FROM
** SAS BUT DONT WANT TO DO IT FROM A DATASTEP USING PUT STATEMENTS. DOING
** IT THIS WAY IS MUCH CLEANER.
**
** PARAMETERS: NONE
**
******************************************************************************
** HISTORY:
** 1.0 MODIFIED: 22-JUL-2010 BY:RP
** - CREATED.
** 1.1 MODIFIED: 18-FEB-2011 BY:RP
** - ADDED LRECL OF 32K TO STOP TRUNCATION
*****************************************************************************/
%macro resolve_file(iFileIn=, iFileOut=);
data _null_;
length line $32767;
infile "&iFileIn" truncover lrecl=32767;
file "&iFileOut" lrecl=32767;
input;
line = resolve(_infile_);
len = length(line);
put line $varying. len;
run;
%mend;
Create some test data. Also create some commands to call the above macro and pass in the values from the dataset:
data mydata;
attrib name length=$10 format=$10. label='FirstName'
blah length=6 format=comma6. label='SomeValue'
cmd1 length=$1000
cmd2 length=$1000
;
title = 1;
name = "Rob" ;
blah = 1000;
cmd1 = cats('%let title=',title,';',
'%let name=',name,';',
'%let blah=',blah,';');
cmd2 = cats('%resolve_file(iFileIn=c:\template.html, iFileOut=c:\result',title,'.html);');
output;
title = 2;
name = "Pete";
blah = 100 ;
cmd1 = cats('%let title=',title,';',
'%let name=',name,';',
'%let blah=',blah,';');
cmd2 = cats('%resolve_file(iFileIn=c:\template.html, iFileOut=c:\result',title,'.html);');
output;
run;
Use call execute to run the cmd1 and cmd2 that we created in the prior dataset. We have to only execute call execute on 1 row at a time so that the correct macro variables are used so do it using a loop. First calculate the number of rows in your dataset using your preferred technique:
proc sql noprint;
select count(*) into :nobs from mydata;
quit;
Then iterate through the dataset executing the commands one at a time and building each row to a new file:
%macro publish;
%local tmp;
%do tmp = 1 %to &nobs;
data _null_;
set mydata(firstobs=&tmp obs=&tmp);
call execute (cmd1);
call execute (cmd2);
run;
%end;
%mend;
%publish;
That should do the trick.
Perhaps I'm missing something but didn't you answer your own question? It should be as easy as:
Create some sample data. Be sure that every column has a format and label applied:
data mydata;
attrib name length=$10 format=$10. label='FirstName'
blah length=6 format=comma6. label='SomeValue';
bygroup = 1; name = "Rob" ; blah = 1000; output;
bygroup = 2; name = "Pete"; blah = 100 ; output;
run;
Transpose the data to make it tall:
proc transpose data=mydata out=trans;
by bygroup;
var _all_;
run;
Print it Out:
data _null_;
set trans2;
by bygroup;
if first.bygroup then do;
put bygroup;
put "------";
end;
put _label_ ":" value;
run;
Result:
1
------
FirstName :Rob
SomeValue :1,000
2
------
FirstName :P
SomeValue :100
How about one of these solutions instead then... Open a table in Bases SAS to view it. Go to View->Form View. That puts it in the layout you requested. It may not look exactly the way you want but it's a quick option.
The alternative is to write your own. Create a macro that takes a dataset and anything else you want to specify as parameters and display it using ODS, the put statement, or whatever other technique you would like.
I'm not aware of any other built in method in SAS to do this.