forked from Jiangtang/SAS_ListProcessing
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFor.sas
409 lines (392 loc) · 15.3 KB
/
For.sas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
%macro for(macro_var_list,in=,do=);
/*
http://www.sascommunity.org/wiki/Streamlining_Data-Driven_SAS_With_The_%25FOR_Macro
http://www.wuss.org/proceedings08/08WUSS%20Proceedings/papers/app/app04.pdf
Function: This macro performs a loop generating SAS code. It
proceeds sequentially through one of 5 kinds of data objects:
SAS dataset, value list, number range, dataset contents and
directory contents. Data object values are assigned to macro
variables specified in macro_var_list upon each loop iteration.
Example:
%for(hospid hospname, in=[report_hosps], do=%nrstr(
title "&hospid &hospname Patient Safety Indicators";
proc print data=psi_iqi(where=(hospid="&hospid")); run;
))
The example above loops over the dataset "report_hosps", which
has dataset variables "hospid" and "hospname". For each dataset
observation, macro variables &hospid and &hospname are assigned
values from the identically named dataset variables and the loop
code is generated, which in the example prints a report.
Parameters:
macro_var_list = space-separated list of macro variable names
to be assigned values upon each loop iteration.
in = Data object to access. Object type is distinguished
by choice of brackets (or no backets for a range):
(a b c) - a space-separated value list whose
values are sequentially accessed.
[xyz] - a SAS dataset whose observations are
sequentially accessed.
{xyz} - a SAS dataset whose variable descriptions
(proc contents) are sequentially accessed.
<c:\abc> - a directory path whose file descriptions
are sequentially accessed.
1:100 - a number range whose values are
sequentially accessed.
do = The SAS code to be generated for each iteration of
the %for macro. If macro variable substitution is
to be done in the loop code (the typical case) enclose
the code in a %nrstr() macro call to defer macro
substitution to loop generation time.
For dataset [ ] iterations:
The dataset name can be qualified with a libname and where clause.
For each observation in the dataset, all macro variables in the
macro_var_list are assigned values of identically named dataset
variables and the "do=" SAS code is generated.
For dataset contents { } iterations:
The dataset name can be qualified with a libname.
For each variable in the dataset, the macro variables in the
macro_var_list are assigned values to describe the dataset
variable and the "do=" SAS code is generated. Valid names in
macro_var_list are "name", "type", "length", "format" and "label".
name - is set to the variable name
type - is set to 1 for numeric variables and 2 for
character variables.
format - is set to the variable format
length - is set to the variable length
label - is set to the variable label
For directory < > iterations:
For each file in the directory, the macro variables in the
macro_var_list are assigned values to describe a directory
file and the "do=" SAS code is generated. Valid names in
macro_var_list are "filepath", "filename", "shortname",
"extension", and "isdir".
filepath - full pathname of file
filename - filename with extension
shortname - filename without extension
extension - file extension
isdir - 1 if file is directory, else 0
For value list ( ) iterations:
Space is the default value separator. Enclose other separator
characters in nested parentheses, e.g., in=((|)Joe Jones|Al Smith)
The variables named in macro_var_list are assigned successive values
from the value list. When all variables are assigned, the "do=" SAS
code is generated. The process repeats until the end of value list is
reached. If the end of value list is reached before all variables in
macro_var_list are assigned values, then the iteration is terminated
without generating the "do=" code on the partially assigned variables
(the number of values in the value list should be a multiple of the
number of names in macro_var_list).
For range : iterations:
A range with 2 numbers uses a single colon and has an implied
increment of 1. For a range with 3 numbers (e.g., in=1:11:2),
the final number is the increment. The first variable in the
macro_var_list is assigned values as it would be by a
%do-%to-%by statement.
NOTE: Macro variable values obtained from datasets remain unquoted
when they contain only letters, digits, whitespace, '_' and '.',
otherwise they are quoted. Values obtained from value list are
always unquoted.
Author: Jim Anderson, UCSF, james.anderson@ucsf.edu
"Please keep, use and pass on the %for macro
with this authorship note. -Thanks "
Please send improvements, fixes or comments to Jim Anderson.
*/
%local _for_itid _for_ct _for_do _for_i _for_val1 _for_var1 _n_
_for_dir _for_var_num _for_in_first _for_in_last _for_in_length
_for_in_sep _for_in_values _for_in_more _for_extra;
%let _for_do=&do;
%if %eval(%index(&do,%nrstr(%if))+%index(&do,%nrstr(%do))) %then
%do; %* conditional macro code - need to embed in macro;
%global _for_gen;
%if &_for_gen=%str( ) %then %let _for_gen=0;
%else %let _for_gen=%eval(&_for_gen+1);
%unquote(%nrstr(%macro) _for_loop_&_for_gen(); &do %nrstr(%mend;))
%let _for_do=%nrstr(%_for_loop_)&_for_gen();
%end;
%let _for_ct=0;
%let _for_in_first=%qsubstr(&in,1,1);
%let _for_in_length=%length(&in);
%let _for_in_last=%qsubstr(&in,&_for_in_length,1);
%if &_for_in_first=%qsubstr((),1,1) %then
%do; %* loop over value list;
%if &_for_in_last ne %qsubstr((),2,1) %then
%do;
%put ERROR: "for" macro "in=(" missing terminating ")";
%return;
%end;
%if ¯o_var_list=%str( ) %then
%do; %*empty variable list - perhaps (s)he just wants &_n_;
%let macro_var_list=_for_extra;
%end;
%if %qsubstr(&in,2,1) ne &_for_in_first %then
%do; %* implicit space separator -- empty entries disallowed;
%if &_for_in_length<3 %then %return;
%let _for_in_values=%substr(&in,2,%eval(&_for_in_length-2));
%local _for_value_index;
%let _for_value_index=1;
%do %while(1);
%let _for_ct=%eval(&_for_ct+1);
%let _n_=&_for_ct;
%let _for_i=1;
%let _for_var1=%scan(¯o_var_list,1,%str( ));
%do %while(%str(&_for_var1) ne %str( ));
%let _for_val1=%scan(&_for_in_values,&_for_value_index,%str( ));
%let _for_value_index=%eval(&_for_value_index+1);
%if %length(&_for_val1)=0 %then
%do; %* end of values before end of variables, terminate iteration;
%return;
%end;
%let &_for_var1=&_for_val1;
%let _for_i=%eval(&_for_i+1);
%let _for_var1=%scan(¯o_var_list,&_for_i,%str( ));
%end;
%unquote(&_for_do)
%end;
%return;
%end;
%else
%do; %* explicit separator -- empty entries allowed;
%if &_for_in_length<6 %then %return; %* empty list;
%let _for_in_sep=%qsubstr(&in,3,1);
%if %qsubstr(&in,4,1) ne &_for_in_last %then
%do;
%put ERROR: "for" macro "in=" explicit separator missing right parenthesis;
%return;
%end;
%let _for_in_values=%qleft(%qtrim(%qsubstr(&in,5,%eval(&_for_in_length-5))));
%let _for_in_more=1;
%do %while(1);
%let _for_ct=%eval(&_for_ct+1);
%let _n_=&_for_ct;
%let _for_i=1;
%let _for_var1=%scan(¯o_var_list,1,%str( ));
%do %while(%str(&_for_var1) ne %str( ));
%if &_for_in_more=0 %then %return; %* end of value list;
%if &_for_in_sep=%qsubstr(&_for_in_values,1,1) %then %let &_for_var1=;
%else %let &_for_var1=%scan(&_for_in_values,1,&_for_in_sep);
%let _for_i=%eval(&_for_i+1);
%let _for_var1=%scan(¯o_var_list,&_for_i,%str( ));
%let _for_in_more=%index(&_for_in_values,&_for_in_sep);
%if %length(&_for_in_values)=&_for_in_more %then %let _for_in_values=%str( );
%else %let _for_in_values=%qsubstr(&_for_in_values,%eval(&_for_in_more+1));
%end;
%unquote(&_for_do)
%end;
%return;
%end;
%end;
%else %if &_for_in_first=%str([) %then
%do; %* loop over dataset;
%local _for_in_dataset;
%if &_for_in_last ne %str(]) %then
%do;
%put ERROR: "for" macro "in=[" missing terminating "]";
%return;
%end;
%if &_for_in_length<3 %then %return;
%let _for_in_dataset=%substr(&in,2,%eval(&_for_in_length-2));
%let _for_itid=%sysfunc(open(&_for_in_dataset));
%if &_for_itid=0 %then
%do;
%put ERROR: for macro cant open dataset &_for_in_dataset;
%return;
%end;
%do %while(%sysfunc(fetch(&_for_itid,NOSET))>=0);
%let _for_ct=%eval(&_for_ct+1);
%let _n_=&_for_ct;
%let _for_i=1;
%let _for_var1=%scan(¯o_var_list,1,%str( ));
%do %while(%str(&_for_var1) ne %str( ));
%let _for_var_num=%sysfunc(varnum(&_for_itid,&_for_var1));
%if &_for_var_num=0 %then
%do;
%put ERROR: "&_for_var1" is not a dataset variable;
%return;
%end;
%if %sysfunc(vartype(&_for_itid,&_for_var_num))=C %then
%do; %* character variable;
%let _for_val1=%qsysfunc(getvarc(&_for_itid,&_for_var_num));
%if %sysfunc(prxmatch("[^\w\s.]+",&_for_val1)) %then
%let &_for_var1=%qtrim(&_for_val1);
%else
%let &_for_var1=%trim(&_for_val1);
%end;
%else
%do; %* numeric variable;
%let &_for_var1=%sysfunc(getvarn(&_for_itid,&_for_var_num));
%end;
%let _for_i=%eval(&_for_i+1);
%let _for_var1=%scan(¯o_var_list,&_for_i,%str( ));
%end;
%unquote(&_for_do)
%end;
%let _for_i=%sysfunc(close(&_for_itid));
%return;
%end;
%else %if &_for_in_first=%str({) %then
%do; %* loop over proc contents;
%local _for_in_dataset;
%if &_for_in_last ne %str(}) %then
%do;
%put ERROR: "for" macro "in={" missing terminating "}";
%return;
%end;
%if &_for_in_length<3 %then %return;
%let _for_in_dataset=%substr(&in,2,%eval(&_for_in_length-2));
%let _for_itid=%sysfunc(open(&_for_in_dataset));
%if &_for_itid=0 %then
%do;
%put ERROR: for macro cant open dataset &_for_in_dataset;
%return;
%end;
%let _for_ct = %sysfunc(attrn(&_for_itid,NVARS));
%do _for_i=1 %to &_for_ct;
%let _n_=&_for_i;
%let _for_var_num=1;
%let _for_var1=%upcase(%scan(¯o_var_list,1,%str( )));
%do %while(%str(&_for_var1) ne %str( ));
%if &_for_var1=NAME %then
%do;
%let name=%sysfunc(varname(&_for_itid,&_for_i));
%end;
%else %if &_for_var1=FORMAT %then
%do;
%let format=%sysfunc(varfmt(&_for_itid,&_for_i));
%end;
%else %if &_for_var1=TYPE %then
%do;
%if %sysfunc(vartype(&_for_itid,&_for_i))=C %then
%let type=2;
%else
%let type=1;
%end;
%else %if &_for_var1=LENGTH %then
%do;
%let length=%sysfunc(varlen(&_for_itid,&_for_i));
%end;
%else %if &_for_var1=LABEL %then
%do;
%let _for_val1=%qsysfunc(varlabel(&_for_itid,&_for_i));
%if %sysfunc(prxmatch("[^\w\s.]+",&_for_val1)) %then
%let label=%qtrim(&_for_val1);
%else
%let label=%trim(&_for_val1);
%end;
%else
%do;
%put ERROR: "&_for_var1" is not NAME, TYPE, FORMAT, LENGTH or LABEL;
%return;
%end;
%let _for_var_num=%eval(&_for_var_num+1);
%let _for_var1=%upcase(%scan(¯o_var_list,&_for_var_num,%str( )));
%end;
%unquote(&_for_do)
%end;
%let _for_i=%sysfunc(close(&_for_itid));
%return;
%end;
%else %if &_for_in_first=%str(<) %then
%do; %* loop over directory contents;
%if &_for_in_last ne %str(>) %then
%do;
%put ERROR: "for" macro "in=<" missing terminating ">";
%return;
%end;
%let _for_val1=;
%if &_for_in_length<3 %then %return;
%let _for_dir=%substr(&in,2,%eval(&_for_in_length-2));
%let _for_itid=%sysfunc(filename(_for_val1,&_for_dir));
%let _for_itid=%sysfunc(dopen(&_for_val1));
%if &_for_itid=0 %then
%do;
%put ERROR: cant open directory path=&_for_dir;
%return;
%end;
%let _for_ct = %sysfunc(dnum(&_for_itid));
%do _for_i=1 %to &_for_ct;
%let _n_=&_for_i;
%let _for_var_num=1;
%let _for_var1=%upcase(%scan(¯o_var_list,1,%str( )));
%do %while(%str(&_for_var1) ne %str( ));
%let _for_extra=%sysfunc(dread(&_for_itid,&_for_i));
%if &_for_var1=FILENAME %then
%do;
%let filename=&_for_extra;
%end;
%else %if &_for_var1=EXTENSION %then
%do;
%if %index(&_for_extra,%str(.)) ne 0 %then
%do;
%let extension=.%scan(&_for_extra,-1,%str(.));
%end;
%else
%do;
%let extension=;
%end;
%end;
%else %if &_for_var1=FILEPATH %then
%do;
%let filepath=&_for_dir\&_for_extra; %*windows specific;
%end;
%else %if &_for_var1=SHORTNAME %then
%do;
%if %index(&_for_extra,%str(.)) ne 0 %then
%do;
%let _for_val1=%eval(%length(&_for_extra)-
%length(%scan(&_for_extra,-1,%str(.)))-1);
%let shortname=%substr(&_for_extra,1,&_for_val1);
%end;
%else
%do;
%let shortname=&_for_extra;
%end;
%end;
%else %if &_for_var1=ISDIR %then
%do; %*below windows specific;
%let _for_var1=_forfile;
%let _for_val1=%sysfunc(filename(_for_var1,&_for_dir\&_for_extra));
%let _for_val1=%sysfunc(dopen(&_for_var1));
%let isdir=%eval(&_for_val1 ne 0);
%if &isdir %then
%do;
%let _for_val1=%sysfunc(dclose(&_for_val1));
%end;
%end;
%else
%do;
%put ERROR: "&_for_var1" is not FILENAME, EXTENSION, FILEPATH, SHORTNAME or ISDIR;
%return;
%end;
%let _for_var_num=%eval(&_for_var_num+1);
%let _for_var1=%upcase(%scan(¯o_var_list,&_for_var_num,%str( )));
%end;
%unquote(&_for_do)
%end;
%let _for_i=%sysfunc(dclose(&_for_itid));
%let _for_i=%sysfunc(filename(_for_val1,));
%return;
%end;
%else %if %index(&in,%str(:)) %then
%do; %* loop from:to:by;
%local _for_in_from _for_in_to _for_in_by;
%let _for_in_from=%scan(&in,1,%str(:));
%let _for_in_to=%scan(&in,2,%str(:));
%if &_for_in_to=%str( ) %then
%do;
%put ERROR: for macro missing value after : in range;
%return;
%end;
%let _for_in_by=%scan(&in,3,%str(:));
%if &_for_in_by=%str( ) %then %let _for_in_by=1;
%let _for_var1=%scan(¯o_var_list,1,%str( ));
%let _for_ct=1;
%do _for_i=&_for_in_from %to &_for_in_to %by &_for_in_by;
%let _n_=&_for_ct;
%if %str(&_for_var1) ne %str( ) %then %let &_for_var1=&_for_i;
%let _for_ct=%eval(&_for_ct+1);
%unquote(&_for_do)
%end;
%return;
%end;
%put ERROR: for macro unrecognized in= argument value "&in";
%mend for;