-
Notifications
You must be signed in to change notification settings - Fork 96
/
ccmap.sas
365 lines (295 loc) · 11.4 KB
/
ccmap.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
/*--------------------------------------------------------------*
* Name: ccmap.sas *
* Title: Produce a conditioned choropleth map *
Doc: http://www.datavis.ca/sasmac/ccmap.html
*--------------------------------------------------------------*
* Author: Michael Friendly <[email protected]> *
* Created: 2 Dec 2005 10:23:56 *
* Revised: 13 Dec 2005 16:36:43 *
* Version: 1.0 *
* *
*--------------------------------------------------------------*/
/*=
=Description:
The CCMAP macro produces a set of choropleth maps for one response
variable where the geographic units are partitioned into groups
by two conditioning variables. Each region appears in all maps,
but is only shaded (in relation the the RESPONSE= variable) in those
views defined by the ranges of the conditioning (GIVEN=) variable.
This makes it possible to see how the geographic distribution of the
response changes with the conditioning variables.
See: Dan Carr's CCmaps page: http://www.galaxy.gmu.edu/~dcarr/CCmaps/
for information on CCmaps.
=Usage:
The CCMAP macro is defined with keyword parameters. The RESPONSE=
(or VAR=) variable is required, as are two GIVEN= variables. The ID=
variable must name the geographic region in the MAP= and DATA= data
sets.
The arguments may be listed within parentheses in any order, separated
by commas. For example:
%ccmap();
For useful results, you should also specify the MIDPOINTS= values of the
RESPONSE= variable and define a corresponding set of PATTERN statements.
==Parameters:
* DATA= The name of the input data set
* MAP= Name of the input map data set [Default: MAP=MYMAPS.USVISMAP]
* RESPONSE= Response variable shown on maps
* VAR= Synonym for RESPONSE=
* ID= The name of the map ID variable indicating geographic regions in the
MAP= and DATA= data sets.
* WEIGHT= Region weight variable (e.g. population), used for calculating weighted
means/medians for the levels of the conditioning variables.
* GIVEN= The names of 2 Conditioning variables, X, Y
* SLICES= Number of slices for X,Y [Default: SLICES=2 2]
* OVERLAP= Allowed overlap between slices of the GIVEN= variables, in percent. Ignored if
the given variables are character. [Default: OVERLAP=0.10]
* LEVELS= Number of levels for the VAR= variable in the maps [Default: LEVELS=7]
* MIDPOINTS= Midpoints for the VAR= variable. You should use this to
specify a common set of midpoints for the response variable
in the maps, so they all use the same midpoints. You can
append PCT to a list of numeric midpoint values to have the
macro calculate the corresponding percentiles of the VAR=
variable. Specifying MIDPOINTS= overrides the LEVELS=
parameter.
* COUTLINE= Outline color for the map regions. Specify COUTLINE=, (null) to suppress
the region outlines. [Default: COUTLINE=GRAYCC]
* LEGEND= NONE, or name of a LEGEND statement, e.g., LEGEND=LEGEND1. If
you do not specify LEGEND=, you will get a default PROC GMAP
legend in each map, probably not what you want.
* GMAPOPTIONS= Other GMAP options
* ANNO= Name of an additional input annotate dataset. If supplied, this is
used in all of the maps.
* HSTRIP= Height (in %) for marginal label strip used to identify the
values of the GIVEN= variables. [Default: HSTRIP=6]
* STRIPBG= Strip background color [Default: STRIPBG=YELLOW]
* STRIPFG= Strip foreground color [Default: STRIPFG=BLACK]
* GTEMP= Temporary graphics catalog used to generate the individual
maps. [Default: GTEMP=GTEMP]
* KILL= Delete gtemp when done? [Default: KILL=Y]
* GOUT= The name of the graphics catalog [Default: GOUT=GSEG]
* NAME= The name of the graph in the graphic catalog [Default: NAME=CCMAP]
==Dependencies:
CCMAP requires the following macros:
gdispla.sas Device-independent DISPLAY/NODISPLAY control
panels.sas Display a set of plots in rectangular panels
slice.sas Divide a variable into slices
=Examples:
%include macros(ccmap);
%include data(guerry);
*-- Define a palette of 7 colors from Red to Blue thru Yellow;
%brewerpal(n=7, palette=RdYlBu, out=palette);
%genpat(data=palette, n=nobs, colors=color);
legend1
mode=share
position=(bottom inside left)
across=4
label=(position=middle j=c 'Pop/Crime' j=c '(00s)')
shape=bar(3,1);
%ccmap(data=guerry, map=mymaps.gfrance(where=(density<5)),
var=Crime_pers, given=Literacy Wealth,
id=dept, weight=Pop1831,
midpoints = 20 30 40 50 60 70 80 PCT,
legend=legend1,
slices=2 2, overlap=0.10);
% =*/
%macro ccmap(
data=_last_, /* name of input data set */
map=mymaps.usvismap, /* name of the input map data set */
response=, /* response variable shown on maps */
var=, /* response variable (synonym) */
id=, /* map region id variable */
weight=, /* region weight variable (e.g. pop) */
given=, /* 2 Conditioning variables, X, Y */
slices=2 2, /* Number of slices for X,Y */
overlap=0.10, /* Overlap between slices */
levels=7, /* Number of levels for the VAR= variable */
midpoints=, /* Midpoints for the VAR= variable */
coutline=graycc, /* Outline color for the map regions */
legend=, /* NONE, or name of a LEGEND statement */
gmapOptions=, /* Other GMAP options */
anno=, /* Input annotate dataset */
hstrip=6, /* Height (%) for marginal label strip */
stripbg=yellow, /* Strip background color */
stripfg=black, /* Strip foreground color */
gtemp=gtemp, /* temporary graphics catalog */
kill=Y, /* delete gtemp when done? */
gout=GSEG, /* graphic catalog for plot matrix */
name=ccmap
);
%if %length(&response)=0 %then
%if %length(&var) %then %let response=&var;
%else %do;
%put ERROR: You must supply a RESPONSE= (or VAR=) response variable;
%goto done;
%end;
%let given1 = %scan(&given,1,%str( ));
%let given2 = %scan(&given,2,%str( ));
%let slices1 = %scan(&slices,1,%str( ));
%let slices2 = %scan(&slices,2,%str( ));
%if %length(slices2)=0 %then %let slices2=&slices1;
%let legend = %upcase(&legend);
%if %length(&given2)=0 %then %do;
%put ERROR: You must supply two GIVEN= variables;
%goto done;
%end;
%if %length(&id)=0 %then %do;
%put ERROR: You must supply a map ID= variable variable;
%goto done;
%end;
/*
*-- doesnt work if data set options are specified
%if %sysfunc(exist(&map))=0 %then %do;
%put ERROR: The MAP= &map data set cannot be found;
%goto done;
%end;
*/
%slice(data=&data, var=&given1, slices=&slices1, overlap=&overlap,
out=out1, outs=_slice1_, slicevar=slice1);
%slice(data=&data, var=&given2, slices=&slices2, overlap=&overlap,
out=out2, outs=_slice2_, slicevar=slice2);
proc sort data=out1;
by &id;
proc sort data=out2;
by &id;
data _combine_;
merge out1(drop=lowerx upperx)
out2(drop=lowerx upperx);
by &id;
*-- Find (weighted) means of response for each slice;
proc means noprint nway data=_combine_;
var &response;
class slice1 slice2;
%if %length(&weight) %then %do;
weight &weight;
%end;
output out=_stats_(drop=_type_ _freq_) mean=mean median=median n=n;
proc print data=_stats_;
id slice1 slice2;
%if %length(&midpoints) %then %do;
*options mprint;
%let midpoints = %upcase(&midpoints);
%if %index(&midpoints, PCT) %then %do;
%let pctiles = %substr(&midpoints, 1,%eval(%index(&midpoints, PCT)-1);
*-- Find the midpoints as percentiles of the respopnse variable;
proc univariate data=&data noprint;
var &response;
output out=_pctiles_
pctlpts=&pctiles
pctlpre=p;
proc transpose data=_pctiles_ out=_pctiles_;
var p:;
proc sql noprint;
select col1
into :midpoints separated by ' '
from _pctiles_;
select count(col1)
into :levels
from _pctiles_;
quit;
%put CCMAP: Percentile midpoints(&pctiles) = &midpoints;
%put CCMAP: Using &levels levels for &response;
%end;
*options nomprint;
%end;
%gdispla(OFF);
%let panel=0;
%do j=1 %to &slices2;
data _strip2_;
set _slice2_ end=last;
where (slice2=&j);
drop lowerx upperx lopct uppct;
length text $12 function $8;
retain xsys '3' ysys '3' style 'solid' when 'b';
color="&stripbg"; line=0;
x=lopct; y=100; function='MOVE '; output;
x=uppct; y=100-&hstrip; function='BAR '; output;
/*
color='grayb0'; line=1;
y =100-&hstrip;
x = 0; function='MOVE '; output;
x =100; function='DRAW '; output;
*/
style=''; color="&stripfg";
x=mean(of lopct uppct);
y=100-(&hstrip/2); hsys='1'; size=&hstrip;
function='label';
text = "&given2";
output;
size=&hstrip/2;
x = lopct; position='6'; text=put(lowerx,3.0); output;
x = uppct; position='4'; text=put(upperx,3.0); output;
%do i=1 %to &slices1;
%let panel=%eval(&panel+1);
data _strip1_;
set _slice1_ end=last;
where (slice1=&i);
drop lowerx upperx lopct uppct shrink;
length text $12 function $8;
retain xsys '3' ysys '3' style 'solid' when 'b';
color="&stripbg"; line=0;
shrink = (100-&hstrip)/100;
y=lopct*shrink; x=0; function='MOVE '; output;
y=uppct*shrink; x=&hstrip; function='BAR '; output;
style=''; color="&stripfg"; angle=90;
y=shrink*mean(of lopct uppct);
x=(&hstrip/2); hsys='1'; size=&hstrip-1;
function='label';
text = "&given1";
output;
size=&hstrip/2;
y = lopct*shrink; position='6'; text=put(lowerx,3.0); output;
y = uppct*shrink; position='4'; text=put(upperx,3.0); output;
data _inset_;
set _stats_;
where (slice1=&i) and (slice2=&j);
retain xsys ysys '1' size 2;
x = 98; y = 94;
function = 'label ';
text = 'Mdn: ' ||put(median, best6.);
position='A'; output;
text = 'n: ' ||put(n, best6.);
position='D'; output;
data _strip_;
set _strip1_ _strip2_ _inset_ &anno;
*-- Leave some space at top and left for strip labels;
title h=%eval(&hstrip+2) pct;
title h=&hstrip pct a=90;
goptions border;
proc gmap
all
map=&map
data=_combine_
gout=>emp;
where (slice1=&i and slice2=&j);
id &id;
choro &response /
levels=&levels
%if %length(&midpoints) %then %do;
midpoints=&midpoints
%end;
%if %length(&coutline) %then %do;
coutline=&coutline
%end;
anno=_strip_
%if &legend=NONE %then nolegend;
%else %if %length(&legend)>0 %then %str(legend=&legend);
name = "&name.&i.&j"
&gmapOptions
;
run; quit;
%end;
%end;
%gdispla(ON);
%panels(rows=&slices2, cols=&slices1, order=down byrows, gin=>emp );
%if %substr(%upcase(&kill),1,1)=Y %then %do;
proc catalog kill catalog=>emp et=grseg;
run; quit;
%end;
/*------------------------------------*
| Clean up datasets no longer needed |
*------------------------------------*/
proc datasets nofs nolist library=work memtype=(data);
delete _strip1_ _strip2_ _strip_ combine_ _stats_;
run; quit;
%DONE:
%mend;