I am wondering if there is a way of defining when and where page breaks occur when using a macro to output data. I know within the various ODS tagests the "Startpage=NOW" can be used but that does not seem to work if a macro is used inside that tagset. So basically I want the two tables, and graph for each personal ID code to be on a single and the next page contains the same summary graphs, charts for that individual, and etc. Currently I can only get every table and chart to its own individual page which makes for a lengthy report! Any help/suggestions would be appreciated!
/*************************************************************************/
/* Create a macro variable of all the ID codes */
/* */
/*************************************************************************/
proc sql noprint;
select personal_id
into :varlist separated by ' ' /*Each identifier code in the list is sep. by a single space*/
from provider;
quit;
%let cntlist = &sqlobs; /*Store a count of the number of id codes*/
%put &varlist; /*Print the codes to the log to be sure our list is accurate*/
ods tagsets.rtf file="C:\USER\test.doc" style=sasdocprinter;
/* macro for generating the output table*/
%macro output(x);
proc print data=prov_&x;
run;
proc print data=prov_revCD_&x;
run;
/*Print graph to template defined earlier*/
ods graphics on / height=500px width=500px;
proc sgrender data=summary_&x template=corf_graphs;
run;
ods graphics on / reset=all;
%mend;
%macro loopit(mylist);
%let else=;
%let n = %sysfunc(countw(&mylist)); /*let n=number of codes in the list*/
data
%do I=0 %to &n;
%let val = %scan(&mylist,&I); /*Let val= the ith code in the list*/
%end;
/*Run a loop for each oscar code. Each code will enter the
%do j=0 %to &n;
%let val = %scan(&mylist,&j); /*Let val= the jth code in the list*/
/*Run the macro loop to generate the required tables*/
%runtab(&val);
%output(&val);
%end;
run;
%mend;
%loopit(&varlist)
/*Run the macro loop over the list of significant procedure code values*/
ods tagsets.rtf close;
Macros are nothing but source code generation devices. They have no impact on the effectiveness of any particular technique, except insomuch as their (mis)use may make it more difficult to see how to do it properly.
In this case, if you want the entirety of each single iteration of %output() to be on one page, then you simply need to add a startpage now before the %output(&val) call.
%runtab(&val);
ods tagsets.rtf startpage=now;
%output(&val);
%end;
That will produce a startpage instruction immediately before the output. You could just as easily include it in the %output macro, if you will always want a new page before a call of it.
I inserted the startpage=NOW where suggested and it inserted a blank page, after playing around with the placement and looking at what the code is doing this following snip it ended up working:
ods tagsets.rtf startpage=NOW;
%runtab(&val);
%output(&val);
ods tagsets.rtf startpage=no;
%end;
Beyond the use of macro, you can also consider the use of pagebywith proc print to split page for you.
See this link for PageBY
Related
I refer here to the the issue addressed 6 years ago in "SAS GRAPH and multiple charts, more meaningful names?" To me this issue needs a solution. I need to generate one chart by indicator (2) and country (27). The index is 1,3,5,7,9,...,107. Please try to find the file/worksheet (png/xls) for country x and indicator y. Good luck! Can somebody help me? Maybe the person who addressed the issue at that time found a solution (I really hope it)? Many thanks!
Below you have an example using sashelp library. It creates Picture1.png, Picture3.png and Picture5.png. Instead of this, I would like to have PictureIBM.png, PictureIntel.png and PictureMicrosoft.png.
proc sort data=SASHELP.STOCKS out=testfile; by stock date; run;
data testfile; set testfile;
by stock ;
retain n;
if first.stock then n=0; else n=n+1;
if n le 5;
run;
ods listing gpath='write the path' ;
ods listing /*style = styles.new_font */
gpath='/ec/prod/1eusilc/flashestimates/FLIPI/3_Output/FE/PERFORMANCE/overall 2022/TIME SERIES' ;
ods graphics on /
width=14in height=10in
outputfmt=gif
imagemap=on
imagename="Picture"
border=off reset=index ;
proc sgplot data=testfile noborder noopaque nowall dattrmap=DATTRMAP
title "write the title";
by stock ;
highlow x=date low=low high=high / type=bar ;
scatter x=date y=close ;
xaxis type=discrete;
run;
Here are two ways to do this, one uses the #byval as indicated in the previous solution and which you used in title but not the file name for some reason.
First make fake data:
data prdsummary;
set sashelp.prdsale;
where year=1993 and (country="GERMANY" or country="CANADA") and region="EAST"
and division="CONSUMER" and
(product="SOFA" or product="TABLE" or product="BED");
run;
proc sort data=prdsummary;
by country;
run;
Then use #BYVAL. However, if you run this code multiple times, SAS does not overwrite the previous files, it adds a 1 to the filename instead. So you need to make sure that the files don't already exist. There may be an option to control this feature, I'm unsure at the moment.
ods listing gpath='/home/fkhurshed/Demo1/';
title 'Graph for #byval1';
ods graphics / imagename="Country #byval1";
proc sgplot data=prdsummary;
by country;
vbar prodtype / group=product response=actual groupdisplay=stack;
run;
A second workaround is to use a macro instead and control the name explicitly.
*explicitly control the names via macro looping;
ods listing gpath='/home/fkhurshed/Demo1/';
%macro generate_country_graph(country=);
ods graphics / imagename="Graphic for &Country.";
proc sgplot data=prdsummary;
where country="&country";
vbar prodtype / group=product response=actual groupdisplay=stack;
run;
%mend;
data _null_;
set prdsummary;
by country;
if first.country then
do;
str=catt('%generate_country_graph(country=', country, ');');
call execute(str);
end;
run;
I'm trying to export one SAS table into multiple Excel worksheets based on the value of a field (parent_account). I want each worksheet to be named the same as the parent_account. I'm using the following code that I found at http://www.tek-tips.com/viewthread.cfm?qid=1335588, but I'm getting these error messages:
A character operand was found in the %EVAL function or %IF condition where a numeric operand is required.
Argument 2 to macro function %SCAN is not a number.
%macro export_to_excel();
%local varlist idx var;
proc sql noprint;
select distinct parent_account into: varlist separated by '||'
from todays_activity;
quit;
%let idx = 1;
%do %while ( %scan(&varlist, &idx, %str(||)) ne %str() );
%let var=%scan(&varlist, &idx, %str(||));
proc export data=sashelp.class (where=(parent_account="&var"))
outfile='My file location\Report.xls'
dbms=excel;
sheet="&var";
quit;
%let idx = %eval(&idx + 1);
%end;
%mend export_to_excel;
%export_to_excel;
You could try using ODS EXCEL. Here is an example use SASHELP.CLASS dataset.
First make sure the data is sorted by your grouping variables.
proc sort data=sashelp.class out=class ;
by sex ;
run;
Set up ODS to point to your target file. Tell it to make a new sheet for each BY group.
ods excel file="&path/class.xlsx" ;
ods excel options
(sheet_interval="bygroup"
suppress_bylines="yes"
sheet_name='GENDER'
);
You also might want to turn off other output destinations.
Then print the file using PAGEBY option. And close the ODS EXCEL destination
proc print data=class noobs;
by sex ;
pageby sex ;
var _all_;
run;
ods excel close;
To test it you could try reading it back in as data. But watch out that it will create member names with embedded spaces.
options validmemname=extend;
libname xx xlsx "&path/class.xlsx";
proc copy inlib=xx outlib=work; run;
libname xx clear ;
From SAS log
NOTE: The data set WORK.GENDER has 9 observations and 5 variables.
NOTE: The data set WORK.'GENDER 2'n has 10 observations and 5 variables.
This might be helpful
%macro export_to_excel;
proc sql noprint;
select distinct parent_account into: varlist separated by '#' from todays_activity;
select count(distinct parent_account) into:n from todays_activity;
quit;
%do i=1 %to &n;
%let var= %scan(&varlist,&i,"#");
proc export data=sashelp.class (where=(parent_account="&var"))
outfile='Your file location\Report.xls'
dbms=excel;
sheet="&var";
run;
%end;
%mend export_to_excel;
Is there a way to dynamically change the sheet names in SAS ODS using macros? Inside a macro, I have the following code:
%DO N = 1 %TO &SQLOBS;
ODS TAGSETS.ExcelXP
OPTIONS(SHEET_NAME = &&TABLEVAR&N
EMBEDDED_TITLES = 'NO'
EMBEDDED_FOOTNOTES = 'NO'
PRINT_HEADER = 'CURRENT &&TABLEVAR&N AS OF &D';
PROC PRINT
DATA = WORK.&&TABLEVAR&N
NOOBS;
RUN;
%END;
Which basically loops through an array of table names and for each table outputs a separate Excel Sheet. And by calling a macro variable &TABLEVAR, it is supposed to dynamically change the sheet name for each table. However, I get the following error:
ERROR 22-322: Expecting a quoted string.
So SAS does resolve the macro variable, I can see the table names are right in the log, but the problem is they are not a quoted string. So I tried it like this with quotes around it:
OPTIONS(SHEET_NAME = '&&TABLEVAR&N'
But then the macro variable is not being resolved by SAS and all the sheets are named &&TABLEVAR&1, 2, etc.
Is there a way around this? (And each sheet is a separate table with a different name so I can't use a BY group.) What good macros are if they can't interact with ODS?
I just found out placing %sysfunc and quote in front of &&TABLEVAR&N does the trick.
So the final code looks like this:
%DO N = 1 %TO &SQLOBS;
ODS TAGSETS.ExcelXP
OPTIONS(SHEET_NAME = %sysfunc(quote(&&TABLEVAR&N))
EMBEDDED_TITLES = 'NO'
EMBEDDED_FOOTNOTES = 'NO'
PRINT_HEADER = 'CURRENT &&TABLEVAR&N AS OF &D';
PROC PRINT
DATA = WORK.&&TABLEVAR&N
NOOBS;
RUN;
%END;
I don't think you need all that. If you use double quotes around the macro variable expression, you should get the same result as the %SYSFUNC(QUOTE()) function:
"&&TABLEVAR&N"
That is, unless you somehow plan to have double quotes in the sheet names....
I'm somewhat new to SAS. I'm trying to update a file name to write an excel file through a loop, but am having trouble assigning the file name. Here's my code:
%MACRO loop;
%DO year1 = 1995 %TO 2008;
DATA _NULL_;
dailyret = catx(STRIP(&year1),
'''/h1/usr11/angeli/finland/haz/phreg_dailyret_', '.csv''');
*to save output to excel;
ODS TAGSETS.EXCELXP
file= %QUOTE(dailyret)
STYLE=minimal
OPTIONS ( Orientation = 'landscape'
FitToPage = 'yes'
Pages_FitWidth = '1'
Pages_FitHeight = '100' );
*a block of code that runs the program, irrelevant to my question;
ods tagsets.excelxp close;
RUN;
%END;
%MEND loop;
%loop;
I have tried many variations of this, but every time, I always get an error message along the lines of "ERROR: No logical assign for filename DAILYRET".
Is there any way I can do this so that I don't have to put physical quotes in the line with "file=" and be able to update the year?
Thank you so much!
-Angel
To reference a macro variable prefix it with an &. Make sure to use double quote characters " to quote the filename since macro triggers are not resolved inside of single quotes.
You can also optionally append a period to the macro name so that the parser knows where the macro variable name ends and regular text starts again. This means that when you want to append an extension that starts with a period you need to have two periods since the first will be used to mark the end of the macro variable reference.
%MACRO loop;
%DO year1 = 1995 %TO 2008;
ODS TAGSETS.EXCELXP
file= "/h1/usr11/angeli/finland/haz/phreg_dailyret_&year1..xml"
STYLE=minimal
OPTIONS ( Orientation = 'landscape'
FitToPage = 'yes'
Pages_FitWidth = '1'
Pages_FitHeight = '100'
)
;
*-------------------;
*a block of code that runs the program, irrelevant to my question;
*-------------------;
ods tagsets.excelxp close;
%END;
%MEND loop;
I am looking if there is a macro that would export multiple datasets into separate excel worksheets within a workbook. Would be great if I could have 10 sheets per workbook.
I can do it the usual way as below, but I have more than 100 datasets to export:
PROC EXPORT DATA=HAVE;
OUTFILE= "S:\MYEXCEL.xlsx"
DBMS=EXCEL REPLACE;
SHEET="NEW_SHEET";
RUN;
Thank you!
The general concept is that you would do something like this:
%macro export_data(file=,data=,sheet=);
proc export data=&data.
outfile="&file."
dbms=excel replace;
sheet="&sheet.";
run;
%mend export_data;
Then you need to construct your export macro calls however you want. Get a dataset with one row per dataset (use dictionary.tables in SQL or sashelp.vtable in data step) and work out however you want to the logic of the sheet names and how many workbooks you need. Google data-driven macro calls for more information.
Provided you have 'SAS/Access to PC FIle formats' licensed, this little macro does it effortlessly:
%macro SASToExcel(ImportLibrary=, ExportLocation=);
ods output members = _Members;
proc datasets lib = &ImportLibrary; run; quit;
proc sql;
select count(Name) into :NumOfDatasets from _Members;
select Name into :Dataset1-:Dataset%trim(%left(&NumOfDatasets)) from _Members;
quit;
%do index = 1 %to &NumOfDatasets;
proc export data=&ImportLibrary..&&Dataset&index.
outfile="&ExportLocation"
dbms=excel replace;
sheet="&&Dataset&index";
run;
%end;
proc datasets;
delete _Members;
quit;
%mend;
%SASToExcel(ImportLibrary=raw, ExportLocation = c:\test.xlsx);