1 | #NM_RateAA10K_GE35.def |
---|
2 | #Calculates directly age-adjusted rates per 10,000 for |
---|
3 | # records that have been filtered by IBIS-Q (data=tmp) |
---|
4 | #Uses the IBIS-Q data_frame for cross1 and cross2 |
---|
5 | #Uses an "agepop_frame" so all agegrp18 (agepop elsewhere) |
---|
6 | # groups are always included/represented (weights will sum to 1.0) |
---|
7 | #NM_ in filename = applies cell suppression rule |
---|
8 | #Outputs redflag var based on RSE |
---|
9 | # |
---|
10 | f type special |
---|
11 | ######################################### |
---|
12 | --------BoNdArY-------- |
---|
13 | 1 script |
---|
14 | OPTIONS MPRINT MLOGIC SYMBOLGEN NONUMBER NODATE linesize=175 PAGESIZE=4000; |
---|
15 | OPTION SPOOL; |
---|
16 | |
---|
17 | %LET MULTIPLIER=10000; |
---|
18 | %LET STDVAR=agepop; |
---|
19 | %LET NCAT=11; |
---|
20 | ************************** 1. TMP ******************************; |
---|
21 | * The dataset 'tmp' is the numerator dataset that has been read ; |
---|
22 | * in already by ibis-q. Any filters have already been applied. ; |
---|
23 | * The proc summary counts deaths by agepop, cross1, and cross2. ; |
---|
24 | * The variable "x" must be in the dataset, it is set equal to 1.; |
---|
25 | ****************************************************************; |
---|
26 | proc summary data=tmp; |
---|
27 | var x; |
---|
28 | class &STDVAR. %cross1% |
---|
29 | ?cross2? %cross2% |
---|
30 | ; |
---|
31 | output out=tmp sum=count; |
---|
32 | proc sort data=tmp; |
---|
33 | by %cross1% %cross2% &STDVAR. ; |
---|
34 | run; |
---|
35 | proc print data=tmp noobs; |
---|
36 | title1 '---------------------------------'; |
---|
37 | title2 '1. TMP - numerator dataset'; |
---|
38 | run; |
---|
39 | |
---|
40 | ********************** 2. %data_frame% *************************; |
---|
41 | * %data_frame% is a dataset created by IBIS-Q. It consists of ; |
---|
42 | * %cross1% and %cross2% (if the user specified %cross2%) and a ; |
---|
43 | * variable named "count" that has been set to "0". The results ; |
---|
44 | * of the proc summary must be merged with the %data_frame% ; |
---|
45 | * dataset. ; |
---|
46 | ****************************************************************; |
---|
47 | data agepop_frame; |
---|
48 | retain count 0; |
---|
49 | do |
---|
50 | &STDVAR. = 1 to &NCAT. by 1 ; |
---|
51 | output; |
---|
52 | end; |
---|
53 | run; |
---|
54 | *************************************************************************************; |
---|
55 | * If user selects specific dimension values, e.g., years, the total dimension value ; |
---|
56 | * will not be included in the data frame. This creates a dimension value for the ; |
---|
57 | * total row. But if user does not select specific values (e.g., selects a year, ; |
---|
58 | * then crosses by sex), there will be a total row for the cross1 and cross2 values. ; |
---|
59 | * So in a later step, the new_frame dataset is sorted with a "nodupkey" option to ; |
---|
60 | * remove the additional dimension total values. ; |
---|
61 | *************************************************************************************; |
---|
62 | data crosstotalframe; |
---|
63 | retain count 0; |
---|
64 | do |
---|
65 | %cross1%=.; |
---|
66 | ?cross2? %cross2%=.; |
---|
67 | output; |
---|
68 | end; |
---|
69 | run; |
---|
70 | data df_%cross1%%cross2%; |
---|
71 | set df_%cross1%%cross2% crosstotalframe; |
---|
72 | run; |
---|
73 | proc print data=df_%cross1%%cross2% noobs; title2 'cross1cross2 frame with total row'; run; |
---|
74 | proc print data=agepop_frame noobs; title2 'agepop frame'; run; |
---|
75 | proc sql; |
---|
76 | create table new_frame as |
---|
77 | select df_%cross1%%cross2%.*, agepop_frame.* |
---|
78 | from df_%cross1%%cross2%, agepop_frame ; |
---|
79 | quit; |
---|
80 | proc sort data=new_frame nodupkey; |
---|
81 | by %cross1% |
---|
82 | ?cross2? %cross2% |
---|
83 | &STDVAR. ; |
---|
84 | run; |
---|
85 | proc print data=new_frame noobs; title2 'new_frame, sorted with nodupkey'; run; |
---|
86 | proc sort data=tmp; by %cross1% %cross2% &STDVAR. ; |
---|
87 | run; |
---|
88 | |
---|
89 | proc sort data=tmp out=sorted; |
---|
90 | by %cross1% %cross2% &STDVAR; run; |
---|
91 | data newtmp; |
---|
92 | merge new_frame sorted; * tmp, must list frame dataset first, then sorted tmp; |
---|
93 | by %cross1% %cross2% &STDVAR.; |
---|
94 | drop _TYPE_ _FREQ_ ; |
---|
95 | run; |
---|
96 | |
---|
97 | proc print data=newtmp noobs; |
---|
98 | title2 '2. NEWTMP, after frame merged with tmp'; |
---|
99 | run; |
---|
100 | data tmp; |
---|
101 | set newtmp; |
---|
102 | run; |
---|
103 | |
---|
104 | ************************* 3. POP *******************************; |
---|
105 | * Sum the pop counts by age group, cross1 and cross2. ; |
---|
106 | * Read proc summary output, recode the agepop total records, ; |
---|
107 | * cross1 and cross2 totals to -1. ; |
---|
108 | ****************************************************************; |
---|
109 | data poptmp; format &STDVAR %popcross1% %popcross2% 8.; set poptmp; run; |
---|
110 | proc summary data=poptmp; |
---|
111 | var popcount; |
---|
112 | class &STDVAR %popcross1% %popcross2%; |
---|
113 | output out=pop (drop=_TYPE_ _FREQ_) |
---|
114 | sum=popcount; |
---|
115 | run; |
---|
116 | data pop; |
---|
117 | set pop; |
---|
118 | proc print data=pop noobs; |
---|
119 | title2 '3. POP - denominator dataset'; |
---|
120 | run; |
---|
121 | |
---|
122 | *********************** 4. NUMBERS *****************************; |
---|
123 | * Join tmp, pop. ; |
---|
124 | * -1 values are left of out NUMBERS because stdpop doesn't have ; |
---|
125 | * any -1 values. ; |
---|
126 | ****************************************************************; |
---|
127 | proc sql; |
---|
128 | create table numbers as |
---|
129 | select tmp.*, pop.* |
---|
130 | from tmp left join pop |
---|
131 | on tmp.&STDVAR.=pop.&STDVAR. |
---|
132 | ?popcross1? and tmp.%cross1%=pop.%popcross1% |
---|
133 | ?popcross2? and tmp.%cross2%=pop.%popcross2% |
---|
134 | ; |
---|
135 | quit; |
---|
136 | data numbers; |
---|
137 | set numbers; |
---|
138 | if &STDVAR. ^=.; *get rid of total rows to stop doubling from pop proc summary; |
---|
139 | if %cross1%=. then %cross1%=-1; |
---|
140 | ?cross2? if %cross2%=. then %cross2%=-1; |
---|
141 | proc print data=numbers noobs; |
---|
142 | title2 '4. NUMBERS'; |
---|
143 | title3 ' '; |
---|
144 | run; |
---|
145 | |
---|
146 | ************************ 5. STDWGT *****************************; |
---|
147 | * Select the appropriate records from the stdwgts datafile. ; |
---|
148 | * All weights are for U.S. 2000 standard population (per NCHS ; |
---|
149 | * Statnote20). 'stdvar' values are as follows: ; |
---|
150 | * agepop - the standard 11 age group weights ; |
---|
151 | * agepopGE35 - agepop wgts for age>=35 ; |
---|
152 | * AADist10 - Statnote20 dist #10, for BRFSS ; |
---|
153 | * AADist10GE40 - AADist10 for ages >=40 ; |
---|
154 | * AADist10GE50 - AADist10 for ages >=50 ; |
---|
155 | * AgeGrp18 - 5-year age groups, for cancer data ; |
---|
156 | * AgeGrp18LT50 - AgeGrp18 for age <50 ; |
---|
157 | * AgeGrp18GE50 - AgeGrp18 for age >=50 ; |
---|
158 | * AgeGrp18LT15 - AgeGrp18 for age <15 ; |
---|
159 | * AgeGrp18LT20 - AgeGrp18 for age <20 ; |
---|
160 | ****************************************************************; |
---|
161 | data stdwgt; |
---|
162 | informat stdwgt 7.6; |
---|
163 | set pop.stdwgts; |
---|
164 | *pop libname is set in .cfg file with the saspop statement; |
---|
165 | if (stdvar='agepopGE35'); *stdvar name is case-sensitive; |
---|
166 | keep &STDVAR. stdwgt; |
---|
167 | run; |
---|
168 | proc print data=stdwgt noobs; |
---|
169 | title2 '5. STDWGT- std. pop. weights for direct age-adjustment'; |
---|
170 | run; |
---|
171 | ******************** 6. NUMBERS2 *******************************; |
---|
172 | * Adds the standard population weights to the numerator and ; |
---|
173 | * denominator table. This is done as a separate sql, left join ; |
---|
174 | * to preserve all the -1 values. ; |
---|
175 | ****************************************************************; |
---|
176 | proc sql; |
---|
177 | create table numbers2 as |
---|
178 | select numbers.*, stdwgt.stdwgt |
---|
179 | from numbers left join stdwgt |
---|
180 | on numbers.&STDVAR.=stdwgt.&STDVAR. |
---|
181 | quit; |
---|
182 | proc print data=numbers2 noobs; |
---|
183 | title2 '6. NUMBERS2- Standard POP wgts merged onto dataset'; |
---|
184 | run; |
---|
185 | |
---|
186 | *********************** 7. AGESPECIFIC *************************; |
---|
187 | * Calculate age-specific rates & cross-products ; |
---|
188 | ****************************************************************; |
---|
189 | data agespecific; |
---|
190 | set numbers2; |
---|
191 | r=(count/popcount); *age-specific proportion; |
---|
192 | ratewgt=r*stdwgt; *weighted age-specific proportion; |
---|
193 | * Montana method... rate_var=count*(stdwgt/popcount)**2; * New formula from doh.wa.gov; |
---|
194 | rate_var=(stdwgt**2)*((r*(1-r))/popcount); |
---|
195 | |
---|
196 | proc print data=agespecific noobs; |
---|
197 | title2 '7. AGESPECIFIC - combines numer and denom data, includes R (age-specific proportion),'; |
---|
198 | title3 'RATEWGT (weighted age-specific rate), and RATE_VAR (rate variance) vars'; |
---|
199 | run; |
---|
200 | |
---|
201 | ************************ 8. AARATE ******************************; |
---|
202 | * Sum the age-specific rates into aa rates, and merge wgtmax. ; |
---|
203 | *****************************************************************; |
---|
204 | proc summary data=agespecific nway; |
---|
205 | var ratewgt rate_var count popcount; |
---|
206 | class %cross1% %cross2%; |
---|
207 | output out=aarate (drop=_TYPE_ _FREQ_ ) |
---|
208 | sum(ratewgt rate_var count popcount)=aarate aarate_var count popcount; |
---|
209 | run; |
---|
210 | data aarate; |
---|
211 | set aarate; |
---|
212 | drop _TYPE_ _FREQ_ ; |
---|
213 | run; |
---|
214 | proc sort data=aarate; by %cross1% %cross2%; run; |
---|
215 | proc print data=aarate noobs; |
---|
216 | title2 '8. AARATE - summed across weighted &STDVAR. cross-products to get AA rate and'; |
---|
217 | title3 'rate variance within each cross-by var'; |
---|
218 | run; |
---|
219 | |
---|
220 | ******************* 9. TMP *************************************; |
---|
221 | * Almost final IBIS output dataset. ; |
---|
222 | * Make sure no summary rows are included. All -1 totals will be ; |
---|
223 | * coded back to '.' after this step. ; |
---|
224 | ****************************************************************; |
---|
225 | data rate; |
---|
226 | set aarate; |
---|
227 | if %cross1%=-1 then %cross1%=.; *Recode the statewide totals.; |
---|
228 | ?cross2? if %cross2%=-1 then %cross2%=.; |
---|
229 | |
---|
230 | if (count>0) then do; |
---|
231 | rateper=(aarate*&MULTIPLIER.); |
---|
232 | rate_se=sqrt(aarate_var); |
---|
233 | stderr=(rate_se*&MULTIPLIER.); |
---|
234 | end; |
---|
235 | if (count<=0) then do; |
---|
236 | count=0; |
---|
237 | |
---|
238 | rateper=0; |
---|
239 | stderr=sqrt((3/popcount)*(1-(3/popcount))/popcount)*&MULTIPLIER.; |
---|
240 | end; |
---|
241 | t1=(rateper-(1.96*stderr)); |
---|
242 | if (t1<0) then t1=0; |
---|
243 | LL=put(t1, 8.2); |
---|
244 | UL=put((rateper+(1.96*stderr)), 8.2); |
---|
245 | LL=compress(LL); |
---|
246 | UL=compress(UL); |
---|
247 | n=count; |
---|
248 | |
---|
249 | *********************** 10. Red Flag ****************************; |
---|
250 | * redflag is the statistical stability indicator based on the ; |
---|
251 | * relative standard error (RSE, or coefficient of variation). ; |
---|
252 | * Redflag values created here are converted to images or special ; |
---|
253 | * characters in IBIS-View application XSLTfiles, for instance: ; |
---|
254 | * (xslt\html\query\module\result\ResultPage.xslt, ...Values.xslt ; |
---|
255 | *****************************************************************; |
---|
256 | if count>0 then do; |
---|
257 | rse=(stderr/rateper); |
---|
258 | redflag=put(' ', $12.); |
---|
259 | if rse>.3 then redflag=put('Unstable', $12.); |
---|
260 | if rse>.5 then redflag=put('Very Unstable', $12.); |
---|
261 | if stderr=. then redflag=put('Unstable', $12.); |
---|
262 | end; |
---|
263 | if count<=0 then redflag=put('Unstable', $12.); *no variance, n=0, rse=div by zero; |
---|
264 | run; |
---|
265 | |
---|
266 | ************* 11. New Mexico Small Numbers Rule *******************; |
---|
267 | * Suppress cells if the numerator in (1 2 3) AND the denominator ; |
---|
268 | * is less than 20. For Counts, must run the crude rate code to ; |
---|
269 | * capture the denominator, but only output the N. ZW's program ; |
---|
270 | * uses ".A" to identify cells for suppression. I have co-opted ; |
---|
271 | * his method so I can use the NM logic for cell suppression instead; |
---|
272 | * of the standard IBIS logic. And I need to use ZW's program ; |
---|
273 | * because it will suppress the table marginals that can be used to ; |
---|
274 | * calculate the suppressed cells. If this code is used, the .def ; |
---|
275 | * file should have the "NM_" prefix. Needs suppressed_variabes ; |
---|
276 | * code at the end of this file to work. ; |
---|
277 | *******************************************************************; |
---|
278 | data tmp; |
---|
279 | set rate; |
---|
280 | if ((0<n<4) and (popcount<5000)) then do; |
---|
281 | n = .A; |
---|
282 | rateper = .A; |
---|
283 | LL = put('**', 8.0); |
---|
284 | UL = put('**', 8.0); |
---|
285 | redflag = put('Suppressed', $12.); |
---|
286 | end; |
---|
287 | proc print data=tmp noobs; title2 '11. TMP - final dataset to pass to IBIS View app'; |
---|
288 | run; |
---|
289 | |
---|
290 | --------BoNdArY-------- |
---|
291 | f out_variable |
---|
292 | # definition for output file |
---|
293 | f xml_out_map_file XMLRate10KNDLCLUCL.map |
---|
294 | --------BoNdArY-------- |
---|
295 | f out_detail lbl_not_used__see_xml_out_map_file |
---|
296 | rateper 15.1 |
---|
297 | n 15.0 |
---|
298 | popcount 15.0 |
---|
299 | LL 15.3 |
---|
300 | UL 15.3 |
---|
301 | redflag 15.0 |
---|
302 | --------BoNdArY-------- |
---|
303 | ***************** 12. suppressed variables *************************; |
---|
304 | * ZW's CGI program must be told how many variables it will need to ; |
---|
305 | * suppress and which ones they are. NOTE: If the SAS code, above, is; |
---|
306 | * commented out, these lines can be left in the .def file without ; |
---|
307 | * causing any problems. They will only be used if the SAS code, ; |
---|
308 | * above is active, OR if the small_num and small_pop parameters ; |
---|
309 | * are active in the .CFG file, and with non-zero values. ; |
---|
310 | ********************************************************************; |
---|
311 | --------BoNdArY-------- |
---|
312 | 1 suppressed_variables 2 |
---|
313 | n |
---|
314 | rateper |
---|
315 | --------BoNdArY-------- |
---|