Create columns with an array in a loop in sas - loops

I want to create five target year columns to the work.komposit_prod throug a loop.
I got following code:
proc sql noprint;
select distinct year into :targetyears1 - FROM work.QE_Target
ORDER by year;
quit;
proc sql noprint;
select distinct Count(Jahr) into :Count_targetyears
FROM
(select distinct year FROM work.QE_Target);
quit;
%let max = &Count_targetyears;
data test ;
set work.komposit_prod;
Do i=1 to &max;
"ZZ_&&targetyears&i"n = .;
end;
run;
Somehow the variable "ZZ_&&targetyears&i"n reference couldn't be resolved.
Can someone give me a hint?
Thank you.
Kind regars,
Ben

Your do loop references a data step variable rather than a macro variable:
Do i=1 to &max;
"ZZ_&&targetyears&i"n = .;
end;
You will need to convert this to a macro to run it:
%macro target_years;
data test ;
set work.komposit_prod;
%do i=1 to &max;
"ZZ_&&targetyears&i"n = .;
%end;
run;
%mend;
%target_years

Related

Iterate over a custom set in SAS

First of all, apologize for my poor english but that's because I'm not native. I'm a newbie in SAS programming too, and I need someone to help me with this problem struggling me.
I have one dataset A containing a numeric field YM representing year and month (e.g., 200902) that I'm using to filter the dataset. In particular, I want to get N filtered datasets using N differents values YM.
A_filtered_200901 = A.filter(YM == 200901)
A_filtered_200902 = A.filter(YM == 200902)
A_filtered_200903 = A.filter(YM == 200903)
...
My idea was to generate the sequence of YM used to filter and then give it as an argument to a %macro containing a PROC SQL. In code/pseucode:
data ym_dataset;
date = input(put(20090201, 8.), yymmdd8.);
do i = 1 to 3;
aux1 = intnx('MONTH', date, i);
aux2 = put(aux1, yymmddn8.);
list_of_ym_values = substr(aux2 , 1, 6);
output;
end;
run;
%macro my_macro(list_of_ym_values);
proc sql;
%do i = 1 %to dim(&list_of_ym_values)
select *
from A
where YM = &list_of_ym_values(i)
%end
quit;
%mend my_macro;
%my_macro(ym_dataset[list_of_ym_values])
I know that this is not the correct approach, but I hope that someone could shed me some light about doing it properly.
Thank you!!
you need loop through list of variables and this values can be created in a macro variable. But as #richard suggested in comments is not great idea to split datasets.
/* create macrovariable with all values*/
proc sql;
select list_of_ym_values into :List
separated by "|" from ym_dataset;
%put &list;
/* scan through each variable and create new dataset*/
%macro one;
%do i=1 %to %sysfunc(countw(&list),"|") ;
%let val= %scan(&list,&i,|);
proc sql;
create table want_&val as
select * from ym_dataset
where list_of_ym_values = "&val";
%end;
%mend;
%one;

SAS-multiple datasets merging

I want to merge several individual datasets through following code. However, it reports error as:
How could I solve this problem?
%macro test(sourcelib=,from=);
proc sql noprint; /*read datasets in a library*/
create table mytables as
select *
from dictionary.tables
where libname = &sourcelib
order by memname ;
select count(memname)
into:obs
from mytables;
%let obs=&obs.;
select memname
into : memname1-:memname&obs.
from mytables;
quit;
data full;
set
%do i=1 %to &obs.;
&from.&&memname&i;
%end;
;
run;
%mend;
%test(sourcelib='RESULT',from=RESULT.);
Your %DO loop is generating extra semi-colons in the middle of your SET statement.
set
%do i=1 %to &obs.;
&from.&&memname&i
%end;
;
Also why do you have two macro parameters to pass the same information? You should be able to just pass in the libref. Also why make so many macro variables when one will do?
%macro test(sourcelib=);
%local memlist ;
proc sql noprint;
select catx('.',libname,memname) into :memlist separated by ' '
from dictionary.tables
where libname = %upcase("&sourcelib")
order by 1
;
quit;
data full;
set &memlist ;
run;
%mend;
%test(sourcelib=ReSulT);

Automated Sorting in SAS

I have lots of tables which I would like to sort with Proc Sort. (The names of the tables are written in a text file.) To avoid repeating the same code all over again I have tried creating a macro that would import the text file, create an array consisting of those table names and finally sort all the tables. However, I came across a few problems. In Python, I would easily be able to loop through an array. But in SAS, I am not sure how to do it.
%MACRO SORT_TABLES();
PROC IMPORT
DATAFILE = 'TABLES_LIST.txt'
OUT = WORK.TABLES_LIST (RENAME = VAR1 = TABLE_NAME)
DBMS = TAB
REPLACE;
GETNAMES = NO;
QUIT;
/* GET THE LIST OF TABLE NAMES: */
PROC SQL NOPRINT;
SELECT
DISTINCT TABLE_NAME
INTO :TABLEVAR1 - :TABLEVAR&SYSMAXLONG
FROM
WORK.TABLES_LIST;
QUIT;
DATA _NULL_;
ARRAY TABLE_NAMES $ &TABLEVAR1 - &TABLEVAR&SYSMAXLONG;
RUN;
%DO %OVER TABLE_NAMES
PROC SORT
DATA = &TABLEVAR1 /* how can I iterate here???? */
OUT = 'WORK.'||&TABLEVAR1;
BY A B C;
QUIT;
%END;
%MEND;
Just use an iterative %DO loop to loop over your "array" of macro variables.
proc sql noprint ;
select distinct table_name
into :tablevar1 -
from table_list
;
quit;
%do i=1 %to &sqlobs ;
proc sort data=&&tablevar&i ; by _all_ ; run;
%end;
But you don't need a macro for this. There are easier ways to generate code.
filename code temp;
data _null_;
set table_list ;
put 'PROC SORT DATA = ' table_name '; BY _all_; run;' ;
run;
%include code / source2 ;

how to excute sas macro iteratively in another macro?

i would like to get result of brand_channel macro.
macro is not working on i=2,3,4 in %do-loop statement.
How can I execute doing_scoring macro iteratively?
thanks!
%doing_scoring;
...
...
...
%mend doing_scoring;
%macro brand_channel;
proc sql noprint;
create table oneb_onec as
select unique x1, x2
from mydata_all;
quit;
data seq_oneb_onec;
set oneb_onec;
seqno = _N_;
run;
%let num=4;
%do i=1 %to #
%put doing number is &i;
%put end doing number is #
proc sql noprint;
create table onebc_table&i as
select a.*
from mydata_all a, seq_oneb_onec b
where b.seqno = &i
and b.x1 = a.x1
and b.x2 = a.x2;
quit;
%doing_scoring(mydata=onebc_table&i, setnumber = &i);
%end;
%mend brand_channel;
%brand_channel;
Your code is fine, except for the initial line (declaration of doing_scoring), but that's likely transcription error I suppose.
Below I have a functional test version.
However, I have a better way to do the same thing. Fundamentally, macro driven iteration is a bad idea; there is a better way to do almost every task you might want to attempt.
In this case, you can call the doing_scoring calls directly from the seq_ dataset, and either move the creation of the sub-dataset to the macro (should be easy) or, perhaps better, keep the dataset in one piece.
First the better way: call execute. (Or, you can create the macro calls in SQL using select into.)
proc sort data=sashelp.class out=class;
by age sex;
run;
%macro doing_scoring(data=,age=,sex=,setnumber=);
data mydata;
set class;
where age=&age. and sex="&sex.";
run;
*whatever else you are doing;
%mend doing_scoring;
data _null_;
set class;
by age sex;
if first.sex then seqno+1;
callstr=cats('%doing_scoring(data=class,age=',age,',sex=',sex,',setnumber=',seqno,')');
call execute(callstr);
run;
Now, the original way with same test data.
%macro doing_scoring(mydata=,setnumber=);
%put doing_scoring &mydata. &setnumber.;
%mend doing_scoring;
%macro brand_channel;
proc sql noprint;
create table oneb_onec as
select distinct age,sex
from sashelp.class;
quit;
data seq_oneb_onec;
set oneb_onec;
seqno = _N_;
run;
%let num=4;
%do i=1 %to #
%put -------------------;
%put doing number is &i;
%put end doing number is #
proc sql noprint;
create table onebc_table&i as
select a.*
from sashelp.class a, seq_oneb_onec b
where b.seqno = &i
and b.age = a.age
and b.sex = a.sex;
quit;
%doing_scoring(mydata=onebc_table&i, setnumber = &i);
%put -------------------;
%end;
%mend brand_channel;
%brand_channel;

Efficiently concatenate many sas datasets

I have over 200k small datasets with the same variables (n<1000 and usually n<100) that I want to concatenate into a master dataset. I have tried using a macro that uses a data step to just iterate through all of the new datasets and concatenate with the master with "set master new:", but this is taking a really long time. Also, if I try to run at the same time, the call execute data step says that I am out of memory on a huge server box. For reference, all of the small datasets together are just over 5 Gigs. Any suggestions would be greatly appreciated. Here is what I have so far:
%macro catDat(name, nbr) ;
/*call in new dataset */
data new ;
set libin.&name ;
run ;
/* reorder names */
proc sql noprint;
create table new as
select var1, var2, var3
from new;
quit;
%if &nbr = 1 %then %do ;
data master;
set new;
run;
%end;
%if &nbr > 1 %then %do ;
data master;
set master new ;
run;
%end ;
%mend;
/* concatenate datasets */
data runthis ;
set datasetNames ;
call execute('%catdat('||datasetname||','||_n_||')');
run;
Resolved: see Bob's comments below.
Try using PROC APPEND instead of your "new" dataset; that will be much, much faster:
%macro DOIT;
proc sql noprint;
select count(*) into : num_recs
from datasetNames;
quit;
%do i=1 %to &num_recs;
data _null_;
i = &i;
set datasetNames point=i;
call symput('ds_name',datasetname);
stop;
run; /* UPDATE: added this line */
%if &i = 1 %then %do;
/* Initialize MASTER with variables in the order you wish */
data master(keep=var1 var2 var3);
retain var1 var2 var3;
set libin.&ds_name;
stop;
run;
%end;
proc append base=master data=libin.&ds_name(keep=var1 var2 var3);
run;
%end;
%mend DOIT;
PROC APPEND will add each dataset into your new "master" without rebuilding it each time as you are doing now.
This also avoids using CALL EXECUTE, removing that memory issue you were running into (caused by generating so much code into the execution stack).

Resources