SAS: how to save multiple ODS output from a %DO loop - sas

data scores;
length name $ 12;
input name $ score1 score2;
datalines;
Riley 1132 1187
Henderson 1015 1102
;
%MACRO DO_MEAN;
%DO I = 1 %TO 10;
PROC MEANS data = scores;
VAR score1;
TITLE "Average math score";
RUN;
%END;
%MEND DO_MEAN;
ods output Summary = Summary;
%DO_MEAN;
I have a simple macro called %DO_MEAN that calculates the mean score1 in my dataset. I want the macro to run from i = 1 to 10 times.
Invoking the macro outputs the results of the MEANS procedure 10 times. And yes, I know the output is exactly the same, but I just wanted to use this reproducible example for the sake of illustration.
I tried using ods output Summary = Summary; before invoking the macro to store my 10 outputs. However, the resulting Summary dataset only has 1 row. How can I adjust my code so that I can store all 10 outputs?
I'd like the resulting ods dataset above to have 10 rows, each corresponding to one of %DO loop's iterations.

You can use the PERSIST option on the ODS OUTPUT statement, documented here:
http://documentation.sas.com/?docsetId=odsug&docsetTarget=p0oxrbinw6fjuwn1x23qam6dntyd.htm&docsetVersion=9.4&locale=en
ods output parameterestimates (persist=proc) = want;
proc reg data=sashelp.class;
where sex='F';
model height = weight;
run;
proc reg data=sashelp.class;
where sex='M';
model height = weight;
run;
quit;
ods output clear;
But the best answer is don't do this in the first place, use BY groups instead.
proc sort data=sashelp.class out=class; by sex; run;
ods output ParameterEstimates = want2;
proc reg data=class;
by sex;
model height = weight;
run;
EDIT: you can use ods output or ods table, but I used both in this example and modified it to be consistent.

If you want to store the output from each run; you will have to save them to the same data set by appending your results. So, use PROC APPEND.
Note: I moved Your ODS Output and Proc append inside your loop, so the ods table will be appended with each loop:
Code:
/*Create an empty base table*/
proc sql;
create table WORK.ods_output
(
score1_N num format=BEST1. label='N',
score1_Mean num format=BEST12. label='Mean',
score1_StdDev num format=BEST12. label='Std Dev',
score1_Min num format=BEST12. label='Minimum',
score1_Max num format=BEST12. label='Maximum'
);
quit;
%MACRO DO_MEAN;
%DO I = 1 %TO 10;
PROC MEANS data = scores;
VAR score1;
TITLE "Average math score";
RUN;
ods output Summary = Summary;
proc append base=ods_output data=summary; quit;
%END;
%MEND DO_MEAN;
%DO_MEAN;
Output table with 10 rows: (ignore the log warnings)
score1_N=2 score1_Mean=1073.5 score1_StdDev=82.731493399 score1_Min=1015 score1_Max=1132
score1_N=2 score1_Mean=1073.5 score1_StdDev=82.731493399 score1_Min=1015 score1_Max=1132
score1_N=2 score1_Mean=1073.5 score1_StdDev=82.731493399 score1_Min=1015 score1_Max=1132
score1_N=2 score1_Mean=1073.5 score1_StdDev=82.731493399 score1_Min=1015 score1_Max=1132
score1_N=2 score1_Mean=1073.5 score1_StdDev=82.731493399 score1_Min=1015 score1_Max=1132
score1_N=2 score1_Mean=1073.5 score1_StdDev=82.731493399 score1_Min=1015 score1_Max=1132
score1_N=2 score1_Mean=1073.5 score1_StdDev=82.731493399 score1_Min=1015 score1_Max=1132
score1_N=2 score1_Mean=1073.5 score1_StdDev=82.731493399 score1_Min=1015 score1_Max=1132
score1_N=2 score1_Mean=1073.5 score1_StdDev=82.731493399 score1_Min=1015 score1_Max=1132
score1_N=2 score1_Mean=1073.5 score1_StdDev=82.731493399 score1_Min=1015 score1_Max=1132

Related

Deleting and adding specific row/ column in SAS output

I have the following data
DATA HAVE;
input year dz $8. area;
cards;
2000 stroke 08
2000 stroke 06
2000 stroke 06
;
run;
After using proc freq
proc freq data=have;
table area*dz/ list nocum ;
run;
I get the below output
In this output
I want to delete the 'dz', what can I do to delete this column?
I want a row in the end that gives 'total', what can I do to get a 'total' row?
Thank you!
There must be a better way of doing this, but the following code creates the desired table:
data have;
input year dz $8. area;
cards;
2000 stroke 08
2000 stroke 06
2000 stroke 06
;
run;
ods output List=list;
proc freq data=have;
table area*dz / list;
run;
data stage1;
set list(keep= area frequency percent CumFrequency CumPercent) end=eof;
area_char = put(area,best.-l); /* Convert it to char to add the Total row */
if eof then do;
call symputx("cumFreq", cumfrequency);
call symputx("cumPerc", cumpercent);
end;
drop area;
run;
data want;
retain area frequency percent; /* Put the variables in the desired order */
set stage1(rename=(area_char=area) drop=cumfrequency cumpercent) end=eof;
output;
if eof then do; /* Manually create the Total row */
area = "Total";
Frequency = &cumfreq.;
Percent = &cumperc.;
output;
end;
run;
Output (want table):
You should subset your data with a where clause and use a title statement if a important partitioning variable is to be removed from output. If you didn't subset how would your audience know if a count contained say episodes of stroke and ministroke if ministroke was also in the data.
Compute the frequencies with freq and use a reporting procedure (print, report, tababulate) that summarizes to show a total line.
Example:
data have;
input year dz $ area;
cards;
2000 stroke 08
2000 stroke 06
2000 stroke 06
;
proc freq noprint data=have;
where dz = 'stroke';
table area / out=freqs;
run;
title 'Stroke dz';
title2 'print';
proc print data=freqs noobs label;
var area;
sum count percent;
run;
title2 'report';
proc report data=freqs;
columns area count percent;
define area / display;
define count / analysis;
rbreak after / summarize;
run;
title2 'tabulate';
proc tabulate data=freqs;
class area;
var count percent;
table area all, count percent;
run;
Thank you all for your valuable responses. The following code gives me the desired output in a concise way
proc freq data=HAVE;
tables area / list nocum out=a;
run;
proc sql;
create table b as
select * from a
union
select
'Total' as area,
sum(count) as count,
sum(percent) as percent
FROM a
;
quit;
proc print data=b; run;

SAS blanks between 2 proc reports

I have ods HTML email sender, in which i have included 2 proc reports:
ods html file=sendmail;
proc report data=h2 headskip headline spacing=100;
column d1 d2 d3;
define d3/group noprint;
define d1/display left;
define d2/display center;
break afetr gr/;
compute after gr;
line " ";
endcomp;
compute before _page_ /style=[color=red] line;
line "abcdefg";
endcomp;
run;
proc report data=h3 noheader nocenter spacing=100;
column r1;
define r1/display style=[color='green'];
compute before _page_ /style=[color=red] line;
line "qwerty123";
endcomp;
run;
ods html close;
ods listing;
As a result i get 2 reports, but there is a huge blanks between them, like extra blank rows or place for titles.
How can i put reports strictly one under another (with only 1 blank line)?
thx in advance
After removing some errors in the code, I don't see any big blanks between the two reports.
data h2;
input d1 $ d2 $ d3 $;
datalines;
aa ss ff
bb qq ff
cc yy dd
dd dd dd
;
run;
data h3;
input r1 $;
datalines;
gg
hh
jj
tt
;
run;
ods html file="thePath\sendmail.html";
proc report data=h2 headskip headline spacing=100;
column d1 d2 d3;
define d3/group noprint;
define d1/display left;
define d2/display center;
break after d3/;
compute after d3;
line " ";
endcomp;
compute before _page_ /style=[color=red];
line "abcdefg";
endcomp;
run;
proc report data=h3 noheader nocenter spacing=100;
column r1;
define r1/display style=[color=green];
compute before _page_ /style=[color=red];
line "qwerty123";
endcomp;
run;
ods html close;
ods listing;
Is that what are you looking for ?

Descending order for proc surveylogistic

I'm trying to do change from PROC logistic to a PROC surveylogistic but there is no descending option for the surveylogistic. Is there any trick to do it?
Here is my PROC logistic:
proc logistic data = myData desc;
model depVar = indepvar /expb CLPARM=WALD;
*weight weightVar;
format depVar depVarF.;
run; quit;
And my PROC surveylogistic :
proc surveylogistic data=myData order=internal ;
*strata var1 var2 var3 /list;
class depVar;
model depVar = indepVar /expb CLPARM ;
format depVar depVarF.;
*weight weightVar;
run; quit;
Any ideas will be appreciated :)
I found the answer after doing a little more research. I just need to add /desc for the class.
proc surveylogistic data=myData order=internal ;
*strata var1 var2 var3 /list;
class depVar /desc;
model depVar = indepVar /expb CLPARM ;
format depVar depVarF.;
*weight weightVar;
run; quit;

How to get minimum and maximum value of all the columns of a table?

How to get minimum and maximum value of all the columns of a table? Please note that the columns may be both numeric, date or character. We have to find min and max of all the variables in following format:
Name_of_columns, minimum, maximum
Here's a macro that will do what your asking for which doesn't require you to know the variable names or their type:
%macro maxmin;
/* get variable names */
proc contents noprint data = test.hashval out=test.contents;run;
proc sql noprint;
select count(*) into: cnt from test.contents;quit;
%let cnt = &cnt;
proc sql noprint;
select name into: name1 - : name&cnt from test.contents;quit;
/* get length of all variable names and results */
proc delete data = test.results; run;
%let name_len = 0;
%let max_len = 0;
%let min_len = 0;
%do i = 1 %to &cnt;
proc sql noprint;
select max(&&name&i),min(&&name&i) into: max&i, :min&i from test.hashval;quit;
%let max&i = %cmpres(&&max&i);
%let min&i = %cmpres(&&min&i);
%if (&name_len < %length(&&name&i)) %then %let name_len = %length(&&name&i);
%if (&max_len < %length(&&max&i)) %then %let max_len = %length(&&max&i);
%if (&min_len < %length(&&min&i)) %then %let min_len = %length(&&min&i);
%end;
/*create results */
%do i = 1 %to &cnt;
data temp;
length NAME $&name_len MAX $&max_len MIN $&min_len;
NAME = "&&name&i";
MAX = "&&max&i";
MIN = "&&min&i";
run;
proc append base = test.results data= temp force;run;
%end;
%mend maxmin;
%maxmin;
proc sql;
create view myExtrema_1 as
Select min(alphaVar) as alphaVar, ..., put(min(numVar),best32.) as numVar, ...
from myTable
Union
Select max(alphaVar), ..., put(max(numVar),best32.), ...
from myTable;
quit;
proc transpose data=myExtrema_1
out=myExtrema(rename=(
_name_ = Variable
col1 = Minimum
col2 = Maximum
));
var alphaVar ... numVar ...;
run;
On request of the commenter, I tested it with
proc sql;
create view Class_1 as
Select min(Name) as Name
, min(Sex) as Sex
, put(min(Age),best32.) as Age
, put(min(Height),best32.) as Height
, put(min(Weight),best32.) as Weight
from sasHelp.Class
Union
Select max(Name) as Name
, max(Sex) as Sex
, put(max(Age),best32.) as Age
, put(max(Height),best32.) as Height
, put(max(Weight),best32.) as Weight
from sasHelp.Class;
quit;
proc transpose data=Class_1
out=Class(rename=(
_name_ = Variable
col1 = Minimum
col2 = Maximum
));
var Name Sex Age Height Weight;
run;

Report using data _Null_

I'm looking for report using SAS data step :
I have a data set:
Name Company Date
X A 199802
X A 199705
X D 199901
y B 200405
y F 200309
Z C 200503
Z C 200408
Z C 200404
Z C 200309
Z C 200210
Z M 200109
W G 200010
Report I'm looking for:
Name Company From To
X A 1997/05 1998/02
D 1998/02 1999/01
Y B 2003/09 2004/05
F 2003/09 2003/09
Z C 2002/10 2005/03
M 2001/09 2001/09
W G 2000/10 2000/10
THANK you,
Tried using proc print but it is not accurate. So looking for a data null solution.
data _null_;
set salesdata;
by name company date;
array x(*) from;
From=lag(date);
if first.name then count=1;
do i=count to dim(x);
x(i)=.;
end;
count+1;
If first.company then do;
from_date1=date;
end;
if last.company then To_date=date;
if from_date1 ="" and to_date="" then delete;
run;
data _null_;
set yourEvents;
by Name Company notsorted;
file print;
If _N_ EQ 1 then put
#01 'Name'
#06 'Company'
#14 'From'
#22 'To'
;
if first.Name then put
#01 Name
#; ** This instructs sas to not start a new line for the next put instruction **;
retain From To;
if first.company then do;
From = 1E9;
To = 0;
end;
if Date LT From then From = Date;
if Date GT To then To = Date;
if last.Company then put
#06 Company
#14 From yymm7.
#22 To yymm7.
;
run;
I have done data step to calculate From_date and To_date
and then proc report to print the report by group.
proc sort data=have ;
by Name Company Date;
run;
data want(drop=prev_date date);
set have;
by Name Company date;
attrib From_Date To_date format=yymms10.;
retain prev_date;
if first.Company then prev_date=date;
if last.Company then do;
To_date=Date;
From_Date=prev_date;
end;
if not(last.company) then delete;
run;
proc sort data=want;
by descending name ;
run;
proc report data=want;
define Name/order order=data;
run;
IMHO, the simplest way is exploiting proc report and its analysis column type as the code below. Note that name and company columns are automatically sorted in alphabetical order (as most of the summary functions or procedures do).
/* your data */
data have;
infile datalines;
input Name $ Company $ Date $;
cards;
X A 199802
X A 199705
X D 199901
y B 200405
y F 200309
Z C 200503
Z C 200408
Z C 200404
Z C 200309
Z C 200210
Z M 200109
W G 200010
;
run;
/* convert YYYYMM to date */
data have2(keep=name company date);
set have(rename=(date=date_txt));
name = upcase(name);
y = input(substr(date_txt, 1, 4), 4.);
m = input(substr(date_txt, 5, 2), 2.);
date = mdy(m,1,y);
format date yymms7.;
run;
/****** 1. proc report ******/
proc report data=have2;
columns name company date=date_from date=date_to;
define name / 'Name' group;
define company / 'Company' group;
define date_from / 'From' analysis min;
define date_to / 'To' analysis max;
run;
The html output:
(tested on SAS 9.4 win7 x64)
============================ OFFTOPIC ==============================
One may also consider using proc means or proc tabulate. The basic code forms are shown below. However, you can also see that further adjustments in output formats are required.
/***** 2. proc tabulate *****/
proc tabulate data=have2;
class name company;
var date;
table name*company, date=' '*(min='From' max='To')*format=yymms7.;
run;
proc tabulate output:
/***** 3. proc means (not quite there) *****/
* proc means + ODS -> cannot recognize date formats;
proc means data=have2 nonobs min max;
class name company;
format date yymms7.; * in vain;
var date;
run;
proc means output (cannot output date format, dunno why):
You may leave comments on improving these alternative ways.