How do i print the mean of a variable to the log in SAS?
data fruit;
input zip fruit & $22. pounds;
datalines;
10034 apples, grapes kiwi 123456
92626 oranges 97654
25414 pears apple 987654
;
This is what I've tried:
data _null_;
set fruit;
put mean(zip);
run;
You can use the MEANS procedure to calculate the mean of the pounds variable, followed by a CALL SYMPUT routine to assign the value to a macro variable, and finally a %PUT to print it in the log.
proc means data=fruit;
var pounds;
output out=testmean mean=fruit_mean;
run;
data _null_;
set testmean;
call symput("fruit_avg",fruit_mean);
run;
%put mean of x is &fruit_avg;
You can use PROC SQL.
proc sql noprint;
/*noprint as you don't want to print to the defined output location, just the log*/
/*format, formats the value. into :<> puts the value into a macro variable named <>*/
select mean(zip) format=best32.
into :zip_mean
from fruit;
/*%put writes a message to the log*/
%put Mean of Zip: &zip_mean;
quit;
If you are OK writing the value to the open output location then just use:
proc sql;
select mean(zip) format=best32.
from fruit;
quit;
In data step, you can use putlog statement to print the value on log. Based on #cherry_bueno 's answer, the putlog version is:
proc means data=fruit;
var pounds;
output out=testmean mean=fruit_mean;
run;
data _null_;
set testmean;
putlog 'mean of x is ' fruit_mean;
run;
Related
I am having two data sets. The first data set has airport codes (JFK, LGA, EWR) in a variable 'airport'. The second dataset has the list of all major airports in the world. This dataset has two variables 'faa' holding the FAA Code (like JFG, LGA, EWR) and 'name' holding the actual name of the airport (John. F Kennedy, Le Guardia etc.).
My requirement is to create value labels for in the first data set, so that instead of airport code, the actual name of the airport comes up. I know I can use custom formats to achieve this. But can I write SAS code which can read the unique airport codes, then get the names from another data set and create a value label automatically?
PS: Other wise, the only option I see is to use MS Excel to get the unique list of FAA codes in dataset 1, and then use VLOOKUP to get the names of the airports. And then create one custom format by listing each unique FAA code and the airport name.
I think "value label" is SPSS terminology. Looks like you want to create a format. Just use your lookup table to create an input dataset for PROC FORMAT.
So if your second table looks like this:
data table2;
length FAA $4 Name $40 ;
input FAA Name $40. ;
cards;
JFK John F. Kennedy (NYC)
LGA Laguardia (NYC)
EWR Newark (NJ)
;
You can use this code to convert it into a dataset that PROC FORMAT can use to create a format.
data fmt ;
fmtname='$FAA';
hlo=' ';
set table2 (rename=(faa=start name=label));
run;
proc format cntlin=fmt lib=work.formats;
run;
Now you can use that format with your other data.
proc freq data=table1 ;
tables airport ;
format airport faa. ;
run;
Firstly, consider if it is really a format what is needed. For example, you may just do a left join to retrieve the column (airport) name from table2 (FAA-Name table).
Anyway, I believe the following macro does the trick:
Create auxiliary tables:
data have1;
input airport $;
datalines;
a
d
e
;
run;
data have2;
input faa $ name $;
datalines;
a aaaa
b bbbb
c cccc
d dddd
;
run;
Macro to create Format:
%macro create_format;
*count number of faa;
proc sql noprint;
select distinct count(faa) into:n
from have2;
quit;
*create macro variables for each faa and name;
proc sql noprint;
select faa, name
into:faa1-:faa%left(&n),:name1-:name%left(&n)
from have2;
quit;
*create format;
proc format;
value $airport
%do i=1 %to &n;
"&faa%left(&i)" = "&name%left(&i)"
%end;
other = "Unknown FAA code";
run;
%mend create_format;
%create_format;
Apply format:
data want;
set have1;
format airport $airport.;
run;
I am looking to create multiple datasets from city_variables dataset. There are a total of 58 observations that I summed up into macrovariable (&count) to stop the do loop.
The city_variables dataset looks like (vertically ofcourse):
CITY_NAME
City1
City2
City3
City4
City5
City6
City7
City8
City9
City10
..........
City58
I created macrovariable &name from a data null statement in order to input the cityname into the dataset name.
Any help would be great on how to automate the creation of the 48 files by name (not number). Thanks again.
/Create macro with number of observations in concordinate file/
proc sql;
select count(area_name);
into :count
from main.state_all;
quit;
%macro repeat;
data _null_;
set city_variables;
%do i= 1 %UNTIL (i = &count);
call symput('name',CITY_NAME);
run;
data &name;
set dataset;
where city_name = &name;
run;
%end;
%mend repeat;
%repeat
Well, if you're going to do
proc sql;
select count(area_name);
into :count
from main.state_all;
quit;
Then why not go all the way? Make a macro that does one dataset output, given the criteria as parameters, then make one call for each separate whatever-name. This might be close to what you're looking at.
%macro make_data(data_name=, set_name=, where=);
data &data_name.;
set &set_name.;
where &where.;
run;
%mend make_data;
proc sql;
select
cats('%make_data(data_name=',city_name,
', set_name=dataset, where=city_name="',
city_name,
'" )')
into :make_datalist
separated by ' '
from main.state_all;
quit;
&make_datalist.;
Some other options that I'll just link to:
Chris Hemedinger # SAS Dummy blog How to Split One Data Set Into Many shows a similar concept except he doesn't put the macro wrapper where I do.
Paul Dorfman, Data Step Hash Objects as Programming Tools is the seminal paper on using a hash table to do this. This is the "fastest" way to do this, likely, if you understand hash tables and have the memory available.
You don't need to use a macro to automate splitting up your data in this way. Since your example is really simple, I would consider using call execute in a null data step:
data test;
infile datalines ;
input city_name $20.;
datalines;
City1
City2
City2
City3
City3
City3
;
run;
data _null_;
set test;
call execute("data "||strip(city_name)||";"||"
set test;
where city_name = '"||strip(city_name)||"';"||"
run;");
run;
I have a null dataset such as
data a;
if 0;
run;
Now I wish to use proc report to print this dataset. Of course, there will be nothing in the report, but I want one sentence in the report said "It is a null dataset". Any ideas?
Thanks.
You can test to see if there are any observations in the dataset first. If there are observations, then use the dataset, otherwise use a dummy dataset that looks like this and print it:
data use_this_if_no_obs;
msg = 'It is a null dataset';
run;
There are plenty of ways to test datasets to see if they contain any observations or not. My personal favorite is the %nobs macro found here: https://stackoverflow.com/a/5665758/214994 (other than my answer, there are several alternate approaches to pick from, or do a google search).
Using this %nobs macro we can then determine the dataset to use in a single line of code:
%let ds = %sysfunc(ifc(%nobs(iDs=sashelp.class) eq 0, use_this_if_no_obs, sashelp.class));
proc print data=&ds;
run;
Here's some code showing the alternate outcome:
data for_testing_only;
if 0;
run;
%let ds = %sysfunc(ifc(%nobs(iDs=for_testing_only) eq 0, use_this_if_no_obs, sashelp.class));
proc print data=&ds;
run;
I've used proc print to simplify the example, but you can adapt it to use proc report as necessary.
For the no data report you don't need to know how many observations are in the data just that there are none. This example shows how I would approach the problem.
Create example data with zero obs.
data class;
stop;
set sashelp.class;
run;
Check for no obs and add one obs with missing on all vars. Note that no observation are every read from class in this step.
data class;
if eof then output;
stop;
modify class end=eof;
run;
make the report
proc report data=class missing;
column _all_;
define _all_ / display;
define name / order;
compute before name;
retain_name=name;
endcomp;
compute after;
if not missing(retain_name) then l=0;
else l=40;
msg = 'No data for this report';
line msg $varying. l;
endcomp;
run;
I am trying to do a simple thing - write a PROC REPORT procedure within a DATA sentence. My main idea is - if the condition in data step is true - lets execute PROC REPORT, if it is false - do not execute PROC REPORT. Any ideas? Code runs without errors for now, but I see that condition in IF statement is not applied and PROC REPORT is ececute despite the fact that condition is not fulfilled.
Thank you in Advance.
%let DATO = 13062016;
PROC IMPORT OUT= WORK.auto1 DATAFILE= "C:\Users\BC1554\Desktop\andel.xlsx"
DBMS=xlsx REPLACE;
SHEET="sheet1";
GETNAMES=YES;
RUN;
data want;
set WORK.auto1;
rownum=_n_;
run;
DATA tbl2;
SET want;
if (rownum => 1 and rownum <=6 ) then output work.tbl2 ;
RUN;
ODS NORESULTS;
ods LISTING close;
ODS RTF FILE="C:\Users\BC1554\Desktop\Statistik_andel_&DATO..rtf";
title "Statistics from monthly run of DK shares of housing companies (andelsboliger)";
data Tbl21 ;
set work.Tbl2;
where (DKANDEL='Daekning_pct_24052016' or DKANDEL='Daekning_pct_18042016') ;
difference = dif(Andel);
difference1 = dif(Total);
run;
data Tbl211 ;
set work.Tbl21;
where (DKANDEL='Daekning_pct_18042016') ;
run;
data Tbl2111 ;
set work.Tbl211;
where (DKANDEL='Daekning_pct_18042016') ;
if abs(difference) > 10 and abs (difference1) > 107 then ;
run;
proc report data= work.Tbl2 spanrows;
columns DKANDEL Andel Total Ukendt ;
title2 "-";
title3 "We REPORT numbers on p.4-5".;
title4 "-";
title5 "The models coverage";
title6 "Run date &DATO.";
footnote1 "Assets without currency code not included";
define DKANDEL / order;
define Andel / order;
define Total / order;
define Ukendt / order;
define DKANDEL/ display;
define Andel / display;
Compute DKANDEL;
call define (_col_,"style","style={background=orange}");
endcomp;
Compute Andel;
call define (_col_,"style","style={background=red}");
endcomp;
run; title; footnote1;
ODS RTF close;
ODS LISTING;
title;
run;
To conditionally execute code you need to use a macro so that you can use macro logic like %IF to conditionally generate the code.
But for your simple problem you can use a macro variable to modify the RUN; statement on your PROC REPORT step. Create a macro variable and set it to the value CANCEL when you don't want the step to run.
%let cancel=CANCEL;
...
if abs(difference) > 10 and abs (difference1) > 107 then call symputx('cancel','');
...
proc report ... ;
...
run &cancel ;
Simple example. Produce report if anyone is aged 13.
%let cancel=CANCEL;
data _null_;
set sashelp.class ;
if age=13 then call symputx('cancel',' ');
run;
proc report data=sashelp.class ;
run &cancel;
Tom's answer is a good one, and probably what I'd do. But, an alternative that is more exactly what you suggested in the question seems also appropriate.
The way you execute a PROC REPORT in a data step (or execute any non-data-step code in a data step) is with call execute. You can use call execute to execute a macro, or just a string of code; up to you how you want to handle it. I would make it a macro, because that makes development much easier (you can write the macro just like regular code, and you can test it independently).
Here's a simple example that is analogous to what Tom put in his answer.
%macro print_report(data=);
proc report data=&data.;
run;
%mend print_report;
data _null_;
set sashelp.class ;
if age=13 then do;
call execute('%print_report(data=sashelp.class)');
stop; *prevent it from donig this more than once;
end;
run;
I used the sashelp library data sets as an example
proc datasets library=sashelp;
contents
data=company
out=work.var_names;
quit;
run;
Now i using proc sql into to store the variable names
proc sql noprint;
select name into: varname separated by ' '
from var_names;
quit;
%put &varname;
Now i would like to print the variables name on the log
data newdata (drop=i);
array temp{*} &varname;
do i=1 to dim(temp);
put temp{i};
end;
run;
While it print . instead of the variable names.
Thanks
log:
337 %put &varname;
DEPTHEAD JOB1 LEVEL1 LEVEL2 LEVEL3 LEVEL4 LEVEL5 N
While i want
DEPTHEAD
JOB1
LEVEL1
LEVEL2
LEVEL3
LEVEL4
LEVEL5
N
Suppose the variables in the company are name, salary, the varname macro variable is "name salary". When the last segments were run, it is acually like this:
array temp{*} name salary;
The name and salary are set to default numeric variables with missing value '.'.
Why not just put the variables in the var_names:
data _null_;
set var_names;
put name;
run;
I don't really understand the premise of this question, as you're doing something waaaaay more complicated than you need to.
First of all, you don't need proc contents.
proc sql noprint;
select name into :varname separated by ' '
from dictionary.columns
where libname='SASHELP' and memname='COMPANY';
quit;
Second, this is a much easier way to get the variable names, even assuming you don't want them as %put.
data _null_;
set sashelp.company;
array _names &varname.;
do _i = 1 to dim(_names);
_v = vname(_names[_i]);
put _v=;
end;
run;
I don't see what value a _temporary_ array has here at all. vname will happily give you the names of the variables in the array, if that's what you're after. Honestly this seems like a contrived example though as Reese and others pointed out.
Not sure why your method is not working, but for now you can use scan function to extract the elements, code is pretty much self explanatory, let me know if you don't get it
data _null_;
array temp{*} &varname;
do i=1 to dim(temp);
temp_1 = scan("&varname.",i);
put temp_1;
end;
second_element=scan("&varname.",2);
put second_element=;
run;
EDIT:
Your method
array temp{*} &varname
which eventually translates to
array temp{*} DEPTHEAD JOB1 LEVEL1 LEVEL2 LEVEL3 LEVEL4 LEVEL5 N;
is not working because by doing that array variable names are defined and not the values are initialized
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000201956.htm
Look for this statement
array simple{3} red green yellow;
If you want to do it your way then you have to do something like this
data _null_;
array tempar{8} $ tempar1-tempar8 ("DEPTHEAD" "JOB1" "LEVEL1" "LEVEL2" "LEVEL3" "LEVEL4" "LEVEL5" "N");
do i=1 to dim(tempar);
put tempar(i)=;
end;
run;
Notice first I have defined the individual elements of the array - tempar1-tempar8 and have defined it char using $ and then I have intialized the values that too in quotes.
EDIT2:
Tested
If you absolutely can't deviate from your approach, below is the code(which is less efficient than what I have suggested above)
proc datasets library=sashelp;
contents
data=company
out=work.var_names;
quit;
run;
proc sql noprint;
select quote(compress(name)) into: varname separated by ' '
from var_names;
select count(name) into: Numofelements from var_names;
quit;
%put %quote(&varname.);
%put &Numofelements;
data _null_;
array tempar{%sysfunc(compress(&Numofelements.))} $ tempar1-tempar%sysfunc(compress(&Numofelements.)) (%quote(&varname.));
do i=1 to dim(tempar);
put tempar(i)=;
end;
run;
This seems like a long way to do something that may be accomplished more easily, if you explained why you wanted to do it :).
Here's a solution that uses a stores the values using quotes and then puts them into a temporary array.
proc sql noprint;
select quote(trim(name)) into: varname separated by ' '
from var_names;
quit;
%put &varname;
data new data;
array var_names{&sqlobs} $ _temporary_ (&varname);
put var_names(2);
do i=1 to dim(var_names);
put var_names(i);
end;
run;
Here's a second solution that uses your dataset, since I find it a good practice to avoid macros wherever possible.
data newdata;
set var_names;
if _n_=2 then put name;
run;
And a note that you can use the
_all_, _numeric_ or _character_
shortcut to reference variable lists. An array can only have one type, so I assume that all variables are numeric or character. As well as the fact that SASHELP.VCOLUMN contains various metadata about all SAS library and tables.