%let name=us_goat_inventory; filename odsout '.'; /* Working on a SAS/Graph imitation/improvement of: http://www.washingtonpost.com/blogs/wonkblog/wp/2015/01/12/map-literally-every-goat-in-the-united-states/ Got data from here... http://quickstats.nass.usda.gov/ Census Animals & Products Livestock Goats Goats Inventory Total County (select no state) Click 'Spreadsheet' (top/right), and it saves a csv file. C093BA7B-C657-33F7-8252-57FE86FAD1E4.csv */ PROC IMPORT OUT=goat_data DATAFILE="C093BA7B-C657-33F7-8252-57FE86FAD1E4.csv" DBMS=CSV REPLACE; GETNAMES=YES; DATAROW=2; RUN; /* Looks like it's only 2012 data, but make 100% sure */ data goat_data; set goat_data (where=(year='2012')); run; data goat_data; set goat_data (rename=(state=state_name county=county_name)); state=.; state=state_ansi; county=.; county=county_ansi; goats=.; goats=input(value, comma8.0); run; proc sort data=goat_data out=goat_data; by goats; run; proc format; value ranges 1 = 'non-disclosed' 2 = '<=250' 3 = '<=500' 4 = '<=1,000' 5 = '<=5,000' 6 = '>5,000' ; run; data goat_data; set goat_data; format bucket ranges.; if value='(D)' then bucket=1; else if goats<=250 then bucket=2; else if goats<=500 then bucket=3; else if goats<=1000 then bucket=4; else if goats<=5000 then bucket=5; else if goats>5000 then bucket=6; run; /* add html hover-text */ data goat_data; set goat_data; length my_html $300; if goats^=. then my_html='title='||quote(trim(left(propcase(county_name)))||', '||trim(left(fipstate(state)))||': '|| trim(left(put(goats,comma8.0)))); else if value='(D)' then my_html='title='||quote(trim(left(propcase(county_name)))||', '||trim(left(fipstate(state)))||': '|| 'Non-disclosure'); run; libname robsmaps '../democd97'; data my_map; set robsmaps.uscounty (where=(statecode not in ('AK' 'HI'))); run; /* annotate state outlines on the map */ proc gremove data=my_map out=anno_outline; id state county; by state; run; data anno_outline; set anno_outline; by state segment notsorted; xsys='2'; ysys='2'; hsys='3'; when='a'; length function $8; color='gray44'; style='empty'; if first.state or first.segment then function='poly'; else function='polycont'; run; goptions device=png; goptions xpixels=900 ypixels=600; goptions border; ODS LISTING CLOSE; ODS HTML path=odsout body="&name..htm" (title="US Goat Inventory") style=htmlblue; goptions gunit=pct htitle=5.0 ftitle="albany amt/bold" htext=2.75 ftext="albany amt"; pattern1 v=s c=graydd; pattern2 v=s c=cxffffd4; pattern3 v=s c=cxfed98e; pattern4 v=s c=cxfe9929; pattern5 v=s c=cxd95f0e; pattern6 v=s c=cx993404; legend1 label=none position=(bottom left) mode=share across=1 order=descending value=(justify=left) shape=bar(.15in,.15in) offset=(4,2); title1 link='http://quickstats.nass.usda.gov/' ls=1.5 "2012 U.S. Goat Population, by county"; proc gmap data=goat_data map=my_map all; format goats comma8.0; note move=(26.5,89.3) c=gray link='http://quickstats.nass.usda.gov/' 'Data source: USDA National Algricultural Statistics Service'; id state county; choro bucket / discrete midpoints = 1 2 3 4 5 6 legend=legend1 coutline=cxc8966e anno=anno_outline html=my_html des='' name="&name"; run; /* Hmm ... for the state totals, maybe rather than using the county data, which omits some non-disclosure values, it would be better to re-generate a new csv file/report of the state totals - that way, I bet it wouldn't omit any. */ /* proc sql; create table goat_summary as select unique fipnamel(state) as State, sum(goats) as Goats from goat_data group by state order by goats descending; quit; run; title1 "2012 U.S. Goat Population summary, by state"; title2 "(omitting non-disclosure values)"; proc print data=goat_summary noobs; format Goats comma12.0; sum goats; run; */ quit; ODS HTML CLOSE; ODS LISTING;