Im using the following loop to generate the sums of some columns using a class statement:
%macro do_mean;
%do i = 50 %to 100;
%let string1 = %eval(100-&i);
**%if string1 = 5 %then %let string1 = "05";**
%if &i = 95 or &i = 90 or &i = 80 or &i = 70 or &i = 60 or &i = 50 %then %do;
proc means data = risiko.risiko_Haus sum nway noprint;
var HA_Max_Neg HA_Max_Pers;
class C_ze_Risiko_&i._2014_&string1._2015;
output out=test_ze_Risiko_&i._2014_&string1._2015 (drop=_type_ _freq_)
sum=C_Risiko_&i._2014_05_2015_Max_Neg C_Risiko_&string1._2014_05_2015_Max_Per;
run;
%end;
%end;
%mend do_mean;
%do_mean;here
The names columns I want to use as a class are "C_ze_Risiko_50_2014_50_2015" "C_ze_Risiko_60_2014_40_2015" and so on.
Unfortunately the code produces "C_ZE_RISIKO_95_2014_5_2015" but I need "C_ZE_RISIKO_95_2014_05_2015" instead. I marked the line where I tried to change this. Unfortunately this doesnt work. Can somebody tell me why and suggest a solution?
Thanks in advance.
An alternative to Gregory's answer is to use putn and the z2. format, e.g.
%LET STRING1 = %SYSFUNC(putn(%EVAL(100-&I),z2.)) ;
What you can do is always append "0" first and then take only the last 2 characters of your string:
%let string1 = "0".%eval(100-&i);
%let string1 = %substr(&string1,%length(&string1)-1);
Related
Is it possible in SAS to invoke a call to a macro using a macro variable whose value is the macro's name? Something like the following:
%MACRO TEST (macroName, var1, var2, var3, var4, var5);
%put LOG: %¯oName(&var1);
%MEND;
%TEST(testName,testVar1);
In response to Richard's answer. I tried following your solution, but am still getting an "apparent invocation of macro YearMonthString not resolved" using the following code:
%MACRO YearMonthString(nYear,nMonth);
/* Builds a string in the format YYYYMM using passed nYear and nMonth */
%local returnVal;
/*Build the string */
%let returnVal = &nYear.%AddLeadingZerosToMakeNumXLength(2,&nMonth);
/*Write to LOG */
%put MACRO LOG: YearMonthString(nYear= &nYear, nMonth= &nMonth) will return &returnVal;
/* return returnVal */
&returnVal
%MEND;
%MACRO TEST (macroName, var1, var2, var3, var4, var5);
%local loopCount;
%let loopCount = 1;
%do i=1 %to 13;
%¯oName.(&var1,&loopCount);
%let loopCount = %eval(&loopCount+1);
%end;
%MEND;
%TEST(YearMonthString,2018,8,,,);
Yes you can! The construct is % ¯o-symbol. Here is a demonstration:
%macro one();
%put &SYSMACRONAME;
%mend;
%macro two();
%put &SYSMACRONAME;
%mend;
%macro dispatch (routine);
%&routine.()
%mend;
%dispatch (one)
%dispatch (two)
---- LOG ----
13 %dispatch (one)
ONE
14 %dispatch (two)
TWO
I don't have another analyst on my team at work and have a question about the most efficient way to run several proc freq concurrently.
My goal is to run about 160 different frequencies, and include formatting for all of them. I assume a macro is the fastest way, but I only have experience with basic macros. Below is my thought process assuming the data was already formatted:
%macro survey(question, formatA formatB);
proc freq;
table &question;
format &formatA &formatB;
%mend;
%survey (question, formatA, formatB);
"question", "formatA" and "formatB" will be strings of data for example:
-"question" would be KCI_1 KCI_2 through KCI_80
- "formatA" would be KCI_1fmt KCI_2fmt through KCI_80fmt
- "formatB" would be KCI_1fmt. KCI_2fmt. through KCI_80fmt.
Danielle:
You can use macro to assign known formats to variables that are not already formatted. The rest of the FREQ does not have to be macro-ized.
* make some survey data with unformatted responses;
data have;
do respondent_id = 1 to 10000;
array responses KCI_1-KCI_80;
do _n_ = 1 to dim(responses);
responses(_n_) = ceil(4*ranuni(123));
end;
output;
end;
run;
* make some format data for each question;
data responseMeanings;
length questionID 8 responseValue 8 responseMeaning $50;
do questionID = 1 to 80;
fmtname = cats('Q',questionID,'_fmt');
peg = ranuni (1234); drop peg;
do responseValue = 1 to 4;
select;
when (peg < 0.4) responseMeaning = scan('Never,Seldom,Often,Always', responseValue);
when (peg < 0.8) responseMeaning = scan('Yes,No,Don''t Ask,Don''t Tell', responseValue);
otherwise responseMeaning = scan('Nasty,Sour,Sweet,Tasty', responseValue);
end;
output;
end;
end;
run;
* create a custom format for the responses of each question;
proc format cntlin=responseMeanings(rename=(responseValue=start responseMeaning=label));
run;
* macro to associate variables with the corresponding custom format;
%macro format_each_response;
%local i;
format
%do i = 1 %to 80;
KCI_&i Q&i._fmt.
%end;
;
%mend;
* compute frequency counts;
proc freq data=have;
table KCI_1-KCI_80;
%format_each_response;
run;
I have the following code:
%macro initial (first=, second=, third=, fourth=, final=);
data &first;
set wtnodup.&first;
DATE1 = INPUT(PUT(Date,8.),YYMMDD8.);
format DATE1 monyy7.;
RUN;
proc freq data=&first order= freq;
tables date1*jobboardid / list out=&second (drop = percent rename=
(Count=CountNew));
run;
data &third;
set &second (firstobs=2);
if countnew le 49 then delete;
run;
proc sort data = &third;
by jobboardid Date1;
run;
data &fourth (keep = countnew oldcountnew Date1 rate from till jobboardid
rate);
set &third;
by jobboardid Date1;
format From Till monyy7.;
from = lag12(Date1);
oldcountnew = lag12(countnew);
if lag12(jobboardid) EQ jobboardid and
INTCK('month', from, Date1) EQ 12 then do;
till = Date1;
rate = ((countnew/oldcountnew)-1)*100;
output;
end;
run;
proc sort data = &fourth;
by Date1 rate;
proc means data=&fourth noprint;
by Date1;
output out=Result.&final median(rate)=medianRate;
run;
%mend initial;
%initial (first = Alabama, second = AlabamaOne, third =AlabamaTwo,
fourth = AlabamaThree, final=AL_10);
%initial (first = Alaska, second = AlaskaOne, third =AlaskaTwo,
fourth = AlaskaThree, final=AK_10);
%initial (first = Arizona, second = ArizonaOne, third =ArizonaTwo,
fourth = ArizonaThree, final=AZ);
%initial (first = Arkansas, second = ArkansasOne, third =ArkansasTwo,
fourth= ArkansasThree, final=AR_10);
What I am trying to do is that in the part that puts the condition:
if countnew < 10 then delete;
I want to create a sort of do-loop that would delete the data when countnew is <10,20,30....until 70, and creates a separate data-set for each of of the iteration of when countnew is <10, 20, etc.
So I would have a final data-set for of the different iteration of when countnew
What is the best way about doing this?
Why not do-looping, ten by ten, and adding the iteration extension to the dataset name like this?
** Sample dataset;
data try;
do i=1 to 1000;
value=1+ranuni(12345)*100;
output;
end;
drop i;
run;
** Macro iterator:
%macro iter(ds=);
%do i=10 %to 70 %by 10;
data &ds._&i;
set &ds;
if value le &i then delete;
run;
%end;
%mend;
%iter (ds=try)
you will have 7 dataset named try_10--try_70 where try will be replaced with the dataset name.
I'm trying to program a macro that will take in a string of variable names separated by | and perform a calculation on them (e.g. ab dc|def). I have tried the following code, but I get a strange error on the scan function: "Macro function %SCAN has too few arguments."
SYMBOLGEN tells me that &from. and &k. were resolved correctly: "FROM resolves to ab dc|def" and "K resolves to 1" so I'm not sure what the problem is. Initially I suspected that %str() masks the value until after macro execution time, resulting in the parameters not being resolved. But this doesn't seem to be the case, as %unquote(%str(..scan function..)) gives the same error.
%macro data_mapping_sum(from);
%let k=1;
%let temp_ind = "%scan(&from., &k.,"|")";
%let THIS_FAILS = %str(%'%scan(&from., &k.,"|")%'n);
%do %while( (&temp_ind. NE "") );
%unquote(&THIS_FAILS.) = 999;
%let k = %eval(&k. + 1);
%let temp_ind = "%scan(&from., &k.,"|")";
%let THIS_FAILS = %str(%'%scan(&from., &k.,"|")%'n);
%end;
%mend;
data test;
%data_mapping_sum(ab dc|def);
run;
Macro functions don't need quotes. This seems to work..as a start
%macro data_mapping_sum(from);
%let k=1;
%do %while (%scan(&from,&k,|)^=%str());
%let temp_ind = %scan(&from, &k,|);
%Put temp_ind(&k)= &temp_ind;
%let k = %eval(&k. + 1);
%end;
%mend;
%data_mapping_sum(ab dc|def);
I'll leave this question open as I don't think that I've fully answered my own question. However, if anyone needs a program with similar functionality, my working code is:
%macro data_mapping_sum(from, map_to);
%let k=1;
%let temp_ind1 = %scan(&from., &k.,"|");
%let temp_ind2 = %scan(&map_to., &k.,"|");
%do %while( ("&temp_ind1." NE "") AND ("&temp_ind2." NE "") AND &k. NE 1000);
%unquote(%str(%'&temp_ind2.%'n)) = sum(%unquote(%str(%'&temp_ind2.%'n)),%unquote(%str(%'TRAN_&temp_ind1.%'n)));
%let k = %eval(&k. + 1);
%let temp_ind1 = %scan(&from., &k.,"|");
%let temp_ind2 = %scan(&map_to., &k.,"|");
%end;
%mend;
data test;
a="%data_mapping_sum(abc d|f,ter|te cy)";
run;
I hope that it's helpful.
Here is the macro I'm running....
%macro ControlLoop(ds);
%global dset nvars nobs;
%let dset=&ds;
/* Open data set passed as the macro parameter */
%let dsid = %sysfunc(open(&dset));
/* If the data set exists, then check the number of obs ,,,then close the data set */
%if &dsid %then %do;
%If %sysfunc(attrn(&dsid,nobs))>0 %THEN %DO;;
%local dsid cols rctotal ;
%let dsid = %sysfunc(open(&DS));
%let cols=%sysfunc(attrn(&dsid, nvars));
%do %while (%sysfunc(fetch(&dsid)) = 0); /* outer loop across rows*/
/*0:Success,>0:NoSuccess,<0:RowLocked,-1:eof reach*/
%If fmt_start_dt<=&sysdate9 and fmt_end_dt>=sysdate9 %then %Do;
%do i = 1 %to &cols;
%local v t; /*To get var names and types using
varname and vartype functions in next step*/
%let v=%sysfunc(varname(&dsid,&i)); /*gets var names*/
%let t = %sysfunc(vartype(&dsid, &i)); /*gets variable type*/
%let &v = %sysfunc(getvar&t(&dsid, &i));/*To get Var values Using
GetvarC or GetvarN functions based on var data type*/
%end;
%CreateFormat(dsn=&dsn, Label=&Label, Start=&Start, fmtName=&fmtName, type=&type);
%END;
%Else %put ###*****Format Expired*****;
%END;
%END;
%else %put ###*****Data set &dset has 0 rows in it.*****;
%let rc = %sysfunc(close(&dsid));
%end;
%else %put ###*****open for data set &dset failed - %sysfunc(sysmsg()).*****;
%mend ControlLoop;
%ControlLoop(format_control);
FOrmat_Control Data:
DSN :$12. Label :$15. Start :$15. fmtName :$8. type :$3. fmt_Start_dt :mmddyy. fmt_End_dt :mmddyy.;
ssin.prd prd_nm prd_id mealnm 'n' 01/01/2013 12/31/9999
ssin.prd prd_id prd_nm mealid 'c' 01/01/2013 12/31/9999
ssin.fac fac_nm onesrc_fac_id fac1SRnm 'n' 01/01/2013 12/31/9999
ssin.fac fac_nm D3_fac_id facD3nm 'n' 01/01/2013 12/31/9999
ssin.fac onesrc_fac_id D3_fac_id facD31SR 'n' 01/01/2013 02/01/2012
oper.wrkgrp wrkgrp_nm wrkgrp_id grpnm 'n' 01/01/2013 12/31/9999
How Can i compare fmt_Start_dt and fmt_end_dt with sysdate ?
I tried something like %If fmt_start_dt<=&sysdate9 and fmt_end_dt>=sysdate9 %then %Do; in the code but values are not picking up in the loop....Any Idea???
Thanks in advance....
I'm not entirely sure what you want, but I think this might work:
%if &fmt_start_dt <= %sysfunc(today()) and &fmt_end_dt >= %sysfunc(today())
Your FETCH function will copy dataset variables to macro variables, so you need to reference them with an ampersand. Also, you should use the TODAY() function rather than the SYSDATE9 macro variable.