sas ods.rtf output headline w/in macro - macros

i´m having trouble getting a headline for a macro since for some reason the title produced by the gplot routine overwrites the predefined headline (title1) what am i missing?
data one;
input state $2. sales ##;
datalines;
VA 5200
SC 9800
NC 7500
GA 12500
NY 17600
;
run;
goptions reset=all ftext='calibri' htext=2 gunit=pct;
symbol1 i=none f='calibri' v='$' h=4 c=green;
axis1 order=(0 to 20000 by 5000) offset=(0,0) label=none;
axis2 minor=none offset=(3,3) label=none;
* place white space above the title using the LS option;
title1 h=4 'Sales by State --- SAS/Graph-Controlled Order on X-axis' ls=1;
* place white space on left and right of the graph;
title2 a=90 ls=1;
title3 a=-90 ls=1;
footnote1 'BASED ON: http://support.sas.com/kb/24/916.html' ls=1;
footnote2 ls=1;
%macro gplot2(data);
proc gplot data=&data;
plot sales*state / vaxis=axis1 autovref haxis=axis2;
format sales dollar8.;
run;
quit;
%mend;
OPTIONS nonumber nodate orientation="landscape";
ODS RTF FILE='C:\temp\SAS_output_test.rtf' startpage=no
keepn;
ODS RTF NOTOC_DATA;
ODS escapechar='^';
ODS noproctitle;
ODS RTF TEXT='^S={just=left} Dataexport XX-XX-XXXX';
title1 '^S={just=right font=("Arial", 10pt, bold)} test'
'^S={just=right font=("Arial", 10pt, bold)} Page ^{pageof}';
%gplot2(one);
quit;
ods rtf close;
the page layout should look like this:
OPTIONS nonumber nodate orientation="landscape";
ODS RTF FILE='C:\temp\SAS_output_test.rtf' startpage=no
keepn;
ODS RTF NOTOC_DATA;
ODS escapechar='^';
ODS noproctitle;
title1 '^S={just=right font=("Arial", 10pt, bold)} test'
'^S={just=right font=("Arial", 10pt, bold)} Page ^{pageof}';
ODS RTF TEXT='^S={just=left} Dataexport XX-XX-XXXX';
ODS RTF startpage=now;
ods rtf close;
thanks a lot, fleischfressende

Related

SAS proc import guessingrows issue

I'm trying to import csv file to SAS using proc import; I know that guessingrows argument will determine automatically the type of variable for each column for my csv file. But there is an issue with one of my CSV file which has two entire columns with blank values; those columns in my csv file should be numeric, but after running the below code, those two columns are becoming character type, is there any solutions for how to change the type of those two columns into numeric during or after importing it to SAS ?
Here below is the code that I run:
proc import datafile="filepath\datasetA.csv"
out=dataA
dbms=csv
replace;
getnames=yes;
delimiter=",";
guessingrows=100;
run;
Thank you !
Modifying #Richard's code I would do:
filename csv 'c:\tmp\abc.csv';
data _null_;
file csv;
put 'a,b,c,d';
put '1,2,,';
put '2,3,,';
put '3,4,,';
run;
proc import datafile=csv dbms=csv replace out=have;
getnames=yes;
run;
Go to the LOG window and see SAS code produced by PROC IMPORT:
data WORK.HAVE ;
%let _EFIERR_ = 0; /* set the ERROR detection macro variable */
infile CSV delimiter = ',' MISSOVER DSD lrecl=32767 firstobs=2 ;
informat a best32. ;
informat b best32. ;
informat c $1. ;
informat d $1. ;
format a best12. ;
format b best12. ;
format c $1. ;
format d $1. ;
input
a
b
c $
d $
;
if _ERROR_ then call symputx('_EFIERR_',1); /* set ERROR detection macro variable */
run;
Run this code and see that two last columns imported as characters.
Check it:
ods select Variables;
proc contents data=have nodetails;run;
Possible to modify this code and load required columns as numeric. I would not drop and add columns in SQL because this columns could have data somewhere.
Modified import code:
data WORK.HAVE ;
%let _EFIERR_ = 0; /* set the ERROR detection macro variable */
infile CSV delimiter = ',' MISSOVER DSD lrecl=32767 firstobs=2 ;
informat a best32. ;
informat b best32. ;
informat c best32;
informat d best32;
format a best12. ;
format b best12. ;
format c best12;
format d best12;
input
a
b
c
d
;
if _ERROR_ then call symputx('_EFIERR_',1); /* set ERROR detection macro variable */
run;
Check table description:
ods select Variables;
proc contents data=have nodetails;run;
You can change the column type of a column that has all missing value by dropping it and adding it back as the other type.
Example (SQL):
filename csv 'c:\temp\abc.csv';
data _null_;
file csv;
put 'a,b,c,d';
put '1,2,,';
put '2,3,,';
put '3,4,,';
run;
proc import datafile=csv dbms=csv replace out=have;
getnames=yes;
run;
proc sql;
alter table have
drop c, d
add c num, d num
;

Facing problem importing a csv file while using macro variable in SAS

I wrote a code in unix SAS to import multiple csv files from current folder. The macro variables are being assigned correct values but somehow the relevant files are not being imported. I am getting the following error message
ERROR: Physical file does not exist, /work/pricepromo/modeler/tolapa01/pawan/&j..csv.
ERROR: Import unsuccessful. See SAS Log for details.
Below is the code.
OPTIONS MERROR MPRINT SERROR MLOGIC SYMBOLGEN ;
X ls *.csv > list;
data name ;
infile 'list' delimiter = ',' MISSOVER DSD lrecl=32767 firstobs=1 ;
informat name_list $9. ;
format name_list $9. ;
input
name_list $
;
run;
data name2;
set name;
name_mod=translate(name_list,'','.csv');
run;
proc sql;
select name_mod into :name separated by '*' from name2;
%let count2 = &sqlobs;
quit;
%macro yy;
%do i = 1 %to &count2;
%let j = %scan(&name,&i,*);
proc import out = &j datafile='./&j..csv'
dbms=csv replace;
run;
%end;
%mend;
%yy;
Try using double quotes
datafile="./&j..csv"
not
datafile='./&j..csv'
With all those options it should have been obvious from reading the SAS log.

In SAS, how to concatenate multiple rows into 1 by some ID

I have a table like this
org_ID linenr text
811558672 10 Legevirksomhet.
811560782 10 Clavier Classics er et musikkselskap som produserer komposisjoner og
811560782 20 arrangementer av svært høy kvalitet. De kombinerer den klassiske
811560782 30 musikktradisjonen med moderne teknikker og deres kunder spenner fra
811560782 40 individuelle musikere til ensembler, festivalarrangører, konserthus,
811560782 50 kulturinstitusjoner, eventskapere og mediaprodusenter.
811560812 10 Grafisk design, illustrasjon og nærliggende virksomhet.
811561592 10 Sosial- og helsetjenesten. Konsulentvirksomhet: Veiledning til
811561592 20 foreldre, fosterhjem, skole og barnehage.
As you can see, for some org_ID, they appear multiple times because one line of text is not enough for them. When this happens, the linenr shows multiple numbers. Now I want to concatenate multiple lines of text into one when org_ID is the same. How shall I do this? Many thanks in advance.
Use SAS Retain functionality to concatenate the text and only output when an new org_ID is read.
Note: The two IF statements handles the cases of first row and last row; where there is no Previous ID or no Next ID.
Working Code: (Your Input data must be sorted)
data have;
infile datalines dlm=',' dsd;
length org_ID 8. linenr 8. text $200.;
input org_ID linenr text $;
datalines;
811558672,10, "Legevirksomhet."
811560782,10, "Clavier Classics er et musikkselskap som produserer komposisjoner og"
811560782,20, "arrangementer av svært høy kvalitet. De kombinerer den klassiske"
811560782,30, "musikktradisjonen med moderne teknikker og deres kunder spenner fra"
811560782,40, "individuelle musikere til ensembler, festivalarrangører, konserthus,"
811560782,50, "kulturinstitusjoner, eventskapere og mediaprodusenter."
811560812,10, "Grafisk design, illustrasjon og nærliggende virksomhet."
811561592,10, "Sosial- og helsetjenesten. Konsulentvirksomhet: Veiledning til"
811561592,20, "foreldre, fosterhjem, skole og barnehage."
;
run;
data want;
set have nobs=nobs;
retain longtext;
retain id;
if(_N_=1) then do; longtext=text; id=org_ID; end;
else if org_ID ne id then do; output; longtext=text; id=org_ID; end;
else longtext=cats(longtext,text);
if (_N_=nobs) then do; output; end;
keep org_ID longtext;
run;
Output:
org_ID=811560782 longtext=Legevirksomhet.
org_ID=811560812 longtext=Clavier Classics er et musikkselskap som produserer komposisjoner ogarrangementer av svært høy kva
org_ID=811561592 longtext=Grafisk design, illustrasjon og nærliggende virksomhet.
org_ID=811561592 longtext=Sosial- og helsetjenesten. Konsulentvirksomhet: Veiledning tilforeldre, fosterhjem, skole og barneha
A DOW loop can accumulate each line of text in the org_ID group into a final longtext. The longtext should be assigned a specific length in order to prevent truncations that may occur if default lengths are used. You may or may not want a space separator between lines that are concatenated.
data want(keep=org_ID longtext);
do until (last.org_ID);
set have;
by org_ID;
length longtext $2000;
longtext = catx(' ', longtext, text);
end;
run;
If the data is not sorted, but the org_ID rows are contiguous, you can use
by org_ID notsorted;
So what is happening ?
Longtext is a non dataset variable, so it is automatically reset to missing at the top of the data step.
The data step iterates for each row in the group until the last row in the group.
The length of the variable longtext is specified after the set statement so it be the last variable in program data vector (pdv), and thus be the second column of the kept variables.
catx is used accumulate the concatenations of the text data within the group. A space is used to separate the text data parts.
If you do not want the space separator, accumulate using
longtext = cats(longtext, text);

SAS macros to print multiple reports

I have a data set that looks like this:
data have;
input name $ class $ time score;
cards;
chewbacca wookie 1 97
chewbacca wookie 2 100
chewbacca wookie 3 95
saruman wizard 1 79
saruman wizard 2 85
saruman wizard 3 40
gandalf wizard 1 22
gandalf wizard 2 50
gandalf wizard 3 87
bieber canadian 1 50
bieber canadian 2 45
bieber canadian 3 10
;
run;
I'm creating a program that does two things:
1. prints the data for each distinct class
2. creates a scatterplot x=time y=score for each name.
Executing the code below will illustrate my desired output:
data chewbacca saruman gandalf bieber;
set have;
if name='chewbacca' then output chewbacca;
else if name='saruman' then output saruman;
else if name='gandalf' then output gandalf;
else if name='bieber' then output bieber;
run;
title 'Report for wookie';
proc print data=have;
where class='wookie';
run;
title 'Plot Chewbacca';
proc sgplot data=chewbacca;
scatter x=time y=score;
run;
title 'Report for wizard';
proc print data=have;
where class='wizard';
run;
title 'Plot Saruman';
proc sgplot data=saruman;
scatter x=time y=score;
run;
title 'Plot Gandalf';
proc sgplot data=gandalf;
scatter x=time y=score;
run;
title 'Report for canadian';
proc print data=have;
where class='canadian';
run;
title 'Plot Bieber';
proc sgplot data=bieber;
scatter x=time y=score;
run;
Ideally, I'd like to automate this. I've been trying to set this up, but am missing something. Here is my attempt:
proc sql;
select count(distinct name) into :numname
from have;
%let numname=&numname;
select distinct name into :name1 - :name&numname
from have;
select count(distinct class) into :numclass
from have;
%let numclass=&numclass;
select distinct class into :class1 - :class&numclass
from have;
quit;
%macro printit;
%do i = 1 %to &numclass;
title 'Report for &&class&i';
proc print data=have;
where class=&&class&i;
run;
/*insert sgplot here*/
%end;
%mend;
%printit;
Please help here. Can't get the syntax sorted....
Thanks.
I see 4 issues.
Macros will only resolve inside double quotes. Single quotes mask the resolution. So change the title statement to:
title "Report for &&class&i";
The class variable is a string. You need to quote the string in the where clause:
where class="&&class&i";
You don't need to generate the separate data sets. You can add a where clause when you specify the data for SGPLOT
proc sgplot data=have(where=(name="&&name&i"));
The number of names and classes are different, so you need two loops.
EDIT: Also look at SGPANEL and/or SGRENDER. You can generate all the charts in 1 call.
The print procedure and most ODS procedures support by-group processing, which might be a lot simpler and save a lot of time depending on what you require.
proc sort data=have; by class;
proc print data=have;
by class;
run;
and
proc sort data=have; by name;
proc sgplot data=have;
by name;
scatter x=time y=score;
run;

sas macro index or other?

I have 169 towns for which I want to iterate a macro. I need the output files to be saved using the town-name (rather than a town-code). I have a dataset (TOWN) with town-code and town-name. Is it possible to have a %let statement that is set to the town-name for each iteration where i=town-code?
I know that I can list out the town-names using the index function, but I'd like a way to set the index function so that it sets a %let statement to the TOWN.town-name when i=TOWN.town-code.
All the answers below seem possible. I have used the %let = %scan( ,&i) option for now. A limitation is that the town names can be more than one word, so I've substituted underscores for spaces that I correct later.
This is my macro. I output proc report to excel for each of the 169 towns. I need the excel file to be saved as the name of the town and for the header to include the name of the town. Then, in excel, I merge all 169 worksheets into a single workbook.
%MACRO BY_YEAR;
%let townname=Andover Ansonia Ashford Avon ... Woodbury Woodstock;
%do i = 1999 %to 2006;
%do j = 1 %to 169;
%let name = %scan(&townname,&j);
ods tagsets.msoffice2k file="&ASR.\Town_Annual\&i.\&name..xls" style=minimal;
proc report data=ASR nofs nowd split='/';
where YR=&i and TWNRES=&j;
column CODNUM AGENUM SEX,(dths_sum asr_sum seasr_sum);
define CODNUM / group ;
define agenum / group ;
define sex / across ;
define dths_sum / analysis ;
define asr_sum / analysis ;
define seasr_sum / analysis ;
break after CODNUM / ul;
TITLE1 "&name Resident Age-Specific Mortality Rates by Sex, &i";
TITLE2 "per 100,000 population for selected causes of death";
run;
ods html close;
%end;
%end;
%MEND;
My guess is that the reason why you want to look up the town name by town index is to repeatedly call a macro with each town name. If this is the case, then you don't even need to get involved with the town index business at all. Just call the macro with each town name. There are many ways to do this. Here is one way using call execute().
data towns;
infile cards dlm=",";
input town :$char10. ##;
cards;
My Town,Your Town,His Town,Her Town
;
run;
%macro doTown(town=);
%put Town is &town..;
%mend doTown;
/* call the macro for each town */
data _null_;
set towns;
m = catx(town, '%doTown(town=', ')');
call execute(m);
run;
/* on log
Town is My Town.
Town is Your Town.
Town is His Town.
Town is Her Town.
*/
If you do need to do a table lookup, then one way is to convert your town names into a numeric format and write a simple macro to retrieve the name, given an index value. Something like:
data towns;
infile cards dlm=",";
input town :$char10. ##;
cards;
My Town,Your Town,His Town,Her Town
;
run;
/* make a numeric format */
data townfmt;
set towns end=end;
start = _n_;
rename town = label;
retain fmtname 'townfmt' type 'n';
run;
proc format cntlin=townfmt;
run;
%macro town(index);
%trim(%sysfunc(putn(&index,townfmt)))
%mend town;
%*-- check --*;
%put %town(1),%town(2),%town(3),%town(4);
/* on log
My Town,Your Town,His Town,Her Town
*/
Or how about you just pass both the code and the name to the macro as parameters? Like this?
%MACRO DOSTUFF(CODE=, NAME=);
DO STUFF...;
PROC EXPORT DATA=XYZ OUTFILE="&NAME."; RUN;
%MEND;
DATA _NULL_;
SET TOWNS;
CALL EXECUTE("%DOSTUFF(CODE=" || STRIP(CODE) || ", NAME=" || STRIP(NAME) || ");");
RUN;