forked from friendly/SAS-macros
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcombine.sas
218 lines (183 loc) · 6.61 KB
/
combine.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
/*--------------------------------------------------------------*
* Name: combine.sas *
* Title: Combine the values of two or more variables *
Doc: http://www.datavis.ca/sasmac/combine.html
*--------------------------------------------------------------*
* Author: Michael Friendly <[email protected]> *
* Created: 4 Mar 1999 9:59 *
* Revised: 09 Feb 2006 08:21:57 *
* Version: 1.1-1 *
* Added IGNMISS= to ignore missing numeric values *
* Fixed buglet with length and sep *
* Added RUN; to complete data step *
* *
*--------------------------------------------------------------*/
/*=
=Description:
The COMBINE macro combines two or more variables (character or
numeric) into a single one. This is useful for situations where you
need two or more CLASS variables, but some procedure or macro
stupidly only handles one. Also handy for plots of the form
plot y * x = Group Sex
where there are two or more curve variables.
=Usage:
The COMBINE macro is called with keyword parameters. The VAR=
parameter is required.
The arguments may be listed within parentheses in any order, separated
by commas. For example:
%combine(var=group gender, result=gp_sex);
==Parameters:
* DATA= The name of the input data set [Default: DATA=_LAST_]
* VAR= List of two or more variables to be combined. *required*
* RESULT= The name of the result variable. [Default: RESULT=_ID_]
* WHERE= Otional WHERE clause to subset the observations written to the
OUT= data set.
* SEP= Separator character(s), inserted between adjacent values
[Default: SEP=:]
* ABBREV= If specified, each character variable in VAR= is truncated to this length
in RESULT. To specify different truncation lengths, use a list of numbers,
whose order corresponds to the VAR= variables, e.g., ABBREV=2 2 4.
* LENGTH= If specified, the RESULT= variable is truncated to this total length,
regardless of the ABBREV= setting.
* USEFMT= If postive, numeric variables which have formats stored in the data set
have their formatted values combined. [Default: USEFMT=0]
* IGNMISS= Ignore missing values?
* OUT= The name of the output data set. The default (OUT=&data) means that
the input data set is replaced. [Default: OUT=&data]
=Bugs:
The internally calculated length for the result variable is incorrect for numeric
variables.
Should provide a way to use a formatted value of a character variable.
=*/
%macro combine(
data=_last_, /* name of input dataset */
var=, /* list of variables to be combined */
where=, /* WHERE clause to subset the data */
result=_id_, /* result variable */
sep=:, /* separator character */
abbrev=, /* abbreviated length of each char variable */
length=, /* max total length of RESULT */
usefmt=0, /* use formats for numeric variables? */
ignmiss=0, /* ignore missing values? */
out=&data /* name of output dataset */
);
%let var=%upcase(&var);
%*let var=%vexpand(&var); *-- Uncomment to make this work for VAR=X1-X5;
%let nv=%words(&var,root=_v_); *-- Get number of VAR= variables, create _v_1 ... ;
%if %upcase(&data)=_LAST_ %then %let data=&syslast;
/*
%if &nv < 2 %then %do;
data &out;
set &data;
%if &usefmt %then %do;
_fmt_=vformat(&var);
put _fmt_=;
if vtype(&var)='N' then
&result=left(trim(put(&var, _fmt_));
else &result = &var;
%end;
%else %do;
&result = &var;
%end;
run;
%put WARNING: Only &nv VAR= variable was specified. &data has been copied to &out;
%goto done;
%end;
%*put nv=&nv;
*/
options nonotes;
proc contents data=&data noprint
out=_vars_(keep=name type format length);
run;
data _null_;
set _vars_ end=eof;
%do i=1 %to &nv;
if upcase(name) = upcase("&&_v_&i") then do;
call symput('_t_'||"&i", put(type,1.0));
if format ^= ' ' and index(format,'.')=0 then format=trim(format)||'.';
call symput('_f_'||"&i", format);
* put name= format=;
len + length+%length(&sep);
end;
%end;
if eof then do;
call symput('_len_', left(put(len, 8.0)));
end;
run;
%if %length(&length)>0 %then %do;
%if %verify(&length, %str(0123456789))=0
%then %let _len_ = &length;
%end;
%if %length(&abbrev)>0 %then %do;
%if %verify(&abbrev, %str(0123456789 ))>0
%then %do;
%put WARNING: Non-numeric ABBREV= &abbrev has been ignored.;
%let abbrev=;
%end;
%end;
%put COMBINE: Length of &result = &_len_;
options notes;
data &out;
set &data;
%if %length(&where) %then %do;
where &where;
%end;
length &result $&_len_;;
&result = '';
%let s=;
%do i=1 %to &nv;
%if &i>1 & %length(&sep)>0 %then %do;
&result = trim(&result) || trim("&sep");
%end;
%if &&_t_&i = 1 %then %do; /* numeric */
length _tmp_ $&_len_;;
drop _tmp_ ;
%if &ignmiss>0 %then %do;
if not missing(&&_v_&i) then do;
%end;
%if &usefmt>0 and %length(&&_f_&i)>0 %then %do;
_tmp_ = left(put(&&_v_&i, &&_f_&i));
%end;
%else %do;
_tmp_ = left(put(&&_v_&i, best8.));
%end;
&result = trim(&result) || _tmp_;
%if &ignmiss>0 %then %do;
end;
%end;
%end; /* numeric */
%else %do; /* character */
%if %length(&abbrev)>0 %then %do;
%let ab = %scan(&abbrev,&i);
%if &ab= %then %let ab= %scan(&abbrev,1);
&result = trim(&result) ||
substr(&&_v_&i,1,min(&ab,length(&&_v_&i)));
%end;
%else %do;
&result = trim(&result) || &&_v_&i;
%end;
%end; /* character */
%next:
%end;
run;
%done:
%mend;
%macro words(string,root=);
%*--------------------------------------------------;
%* Return number of words in string. If root ^' ', ;
%* then create global variables starting with root. ;
%*--------------------------------------------------;
%local count word;
%let count=1;
%let word = %scan(&string,&count,%str( ));
%do %while(&word^= );
%*put WORDS: word=&word;
%if &root^= %then %do;
%global &root&count;
%let &root&count=&word;
%end;
%let count = %eval(&count+1);
%let word = %scan(&string,&count,%str( ));
%end;
%eval(&count-1)
%mend words;