-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathnumbername.sas
211 lines (174 loc) · 8.54 KB
/
numbername.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
%macro NumberName(in,out,var,newvar,style=SHORT) / des='Convert numbers to names';
/********************************************************************************
BEGIN MACRO HEADER
********************************************************************************
Name: NumberName
Author: Chris Swenson
Created: 2010-11-12
Purpose: Convert numbers (e.g., 1000) to names (e.g., One thousand)
Arguments: in - input data set
out - outpout data set
var - variable containing numbers
newvar - new variable
style= - LONG or SHORT style
Note: The macro does not generate names for decimal places.
Revisions
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Date Author Comments
¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯
YYYY-MM-DD III Please use this format and insert new entries above
********************************************************************************
END MACRO HEADER
********************************************************************************/
%macro NumberNameFmt(lib) / des='Create format for NumberName macro';
%if "&LIB"="" %then %do;
%put %str(E)RROR: No library specified.;
%return;
%end;
data NumberName;
format Start End Label $25.;
retain Fmtname '$Numbername' Type 'C';
/* Ones */
start='1'; label='one'; end=start; HLO=''; output;
start='2'; label='two'; end=start; HLO=''; output;
start='3'; label='three'; end=start; HLO=''; output;
start='4'; label='four'; end=start; HLO=''; output;
start='5'; label='five'; end=start; HLO=''; output;
start='6'; label='six'; end=start; HLO=''; output;
start='7'; label='seven'; end=start; HLO=''; output;
start='8'; label='eight'; end=start; HLO=''; output;
start='9'; label='nine'; end=start; HLO=''; output;
/* Teens */
start='10'; label='ten'; end=start; HLO=''; output;
start='11'; label='eleven'; end=start; HLO=''; output;
start='12'; label='twelve'; end=start; HLO=''; output;
start='13'; label='thirteen'; end=start; HLO=''; output;
start='14'; label='fourteen'; end=start; HLO=''; output;
start='15'; label='fifteen'; end=start; HLO=''; output;
start='16'; label='sixteen'; end=start; HLO=''; output;
start='17'; label='seventeen'; end=start; HLO=''; output;
start='18'; label='eighteen'; end=start; HLO=''; output;
start='19'; label='nineteen'; end=start; HLO=''; output;
/* 20+ */
start='20'; label='twenty'; end=start; HLO=''; output;
start='30'; label='thirty'; end=start; HLO=''; output;
start='40'; label='forty'; end=start; HLO=''; output;
start='50'; label='fifty'; end=start; HLO=''; output;
start='60'; label='sixty'; end=start; HLO=''; output;
start='70'; label='seventy'; end=start; HLO=''; output;
start='80'; label='eighty'; end=start; HLO=''; output;
start='90'; label='ninety'; end=start; HLO=''; output;
/* Other */
start=''; label=''; end=start; HLO='O'; output;
run;
proc format library=&LIB cntlin=NumberName;
run;
proc catalog c=&LIB..formats;
modify NumberName.formatc(description="Number names used with NumberName macro");
run; quit;
%mend NumberNameFmt;
%let style=%upcase(&STYLE);
/* Check arguments */
%if "&in"="" %then %do;
%put %str(E)RROR: No argument specified for IN.;
%return;
%end;
%if %sysfunc(exist(&in))=0 %then %do;
%put %str(E)RROR: Specified input data set does not exist.;
%return;
%end;
%if "&out"="" %then %do;
%put %str(E)RROR: No argument specified for OUT.;
%return;
%end;
%if "&var"="" %then %do;
%put %str(E)RROR: No argument specified for VAR.;
%return;
%end;
%if "&NEWVAR"="" %then %do;
%put %str(E)RROR: No new variable name (NEWVAR) specified.;
%return;
%end;
%if %index(*LONG*SHORT*,*&STYLE*)=0 %then %do;
%put %str(E)RROR: %str(I)nvalid STYLE argument. Please use SHORT or LONG.;
%return;
%end;
%local varlen fmt;
/* Check length of number in characters */
proc sql noprint;
select max(length(compress(put(&var, 25.))))
into :varlen
from &in
;
quit;
%if &varlen>21 %then %do;
%put %str(E)RROR: Specified variable exceeds the current limit of macro (21).;
%return;
%end;
/* Check on the availability of the NumberName format */
%let fmt=0;
proc sql noprint;
select count(fmtname)
into :fmt
from dictionary.formats
where fmtname='$NUMBERNAME'
;
quit;
%let fmt=&fmt;
/* Run the macro to create the format if it is not available */
%if &FMT=0 %then %do;
%put NOTE: The $NUMBERNAME format was not available. The macro will execute the NumberNameFmt macro.;
%put NOTE: To save the $NUMBERNAME format in a permanent library for faster processing,;
%put NOTE: run the NumberNameFmt macro specifying a permanent library and modify the FMTSEARCH option.;
%NumberNameFmt(work);
%end;
/* Convert the number to the name */
data &out(drop=_temp_ _digit_ _scale_ i i_d);
set ∈
format _temp_ $25. &newvar $250. _digit_ $1. _scale_ $50.;
/* Add leading zeros to the input number for scanning */
_temp_=left(put(&var, z21.));
/* Loop through each digit, one at a time */
do i=1 to 21;
/* Scan for the Ith digit */
_digit_=substr(compress(_temp_), i, 1);
/* Set the scale */
/* The dots will not be included and are only used as placeholders */
%if &STYLE=SHORT %then %do;
_scale_=scan(". . quintillion, . . quadrillion, . . trillion, . . billion, . . million, . . thousand, . . .", i, ' ');
%end;
%else %if &STYLE=LONG %then %do;
_scale_=scan(". . trillion, . . thousand_billion, . . billion, . . thousand_million, . . million, . . thousand, . . .", i, ' ');
%end;
/* Divide the iteration by 3 to determine hundreds/tens/ones place */
/* Since the number has been padded with zeros in groups of 3, if the scan
iteration is divisible by 3, it is in the Ones place. If .67 remains,
it is in the tens place. If .33 remains, it is in the hundreds place. */
i_d=scan(put(i/3, 20.2), -1, '.');
/* Hundreds place */
/* Only populate if not zero */
if i_d='33' then do;
if put(_digit_, $NumberName.) ne ''
then &newvar=compbl(&newvar || ' ' || put(_digit_, $NumberName.) || ' hundred ');
end;
/* Tens place */
/* For those starting with 1, add the ones place. Add a 0 for the others. */
else if i_d='67' then do;
if _digit_='1' then &newvar=compbl(&newvar || ' ' || put(substr(compress(_temp_), i, 2), $NumberName.));
else &newvar=compbl(&newvar || ' ' || put(compress(_digit_ || '0'), $NumberName.));
end;
/* Ones place */
/* Only process if the tens place was not 1, and add the scale at the end */
else if i_d='00' then do;
if substr(compress(_temp_), i-1, 1) ne '1'
then &newvar=compbl(&newvar || ' ' || put(_digit_, $NumberName.));
if &newvar ne '' and _scale_ ne '.' then &newvar=compbl(&newvar || _scale_);
else if &var=0 then &newvar='Zero';
end;
end;
/* Final cleanup: Set characters to left, remove _ from the long style, and
set the first letter to upper-case */
&newvar=tranwrd(left(&newvar), '_', ' ');
&newvar=upcase(substr(&newvar, 1, 1)) || substr(&newvar, 2, length(&newvar)-1);
run;
%mend NumberName;