%let name=wealth_and_health; /* Set your current-working-directory (to read/write files), if you need to ... %let rc=%sysfunc(dlgcdir('c:\someplace\public_html')); */ filename odsout '.'; /*************************************************************** Creating an animation similar to: http://www.gapminder.org/ Using data from: http://www.gapminder.org/data/ ****************************************************************/ /* First, run wealth_and_health_container.sas to create html container. Then run wealth_and_health_map.sas to create the world map. Then run this program (wealth_and_health.sas) to create gif animation. */ /* libname robsdata '\\sashq\root\u\realliso\public_html\democd27\'; */ libname robsdata '../democd27'; /* sort by year so you can use the by statement */ /* sort by region so colors are assigned consistently (data order) */ /* (sgplot bubble automatically draws bubbles so smallest ones are in front) */ proc sort data=robsdata.gapminder_data (where=(region^='unknown' and year>=1950)) /* (where=(region^='unknown' and year>=1950 and year<=1995)) (where=(region^='unknown' and year=1995)) */ out=bubble_data; by year region; run; data bubble_data; set bubble_data; /* I don't want to extend my x-axis larger than $100000, but some of the values might extend past that, so I create a special 'truncated' value to plot (and then tweak the axis values to show '$100,000+'). */ if income_pp>100000 then income_pp_modified=100000; else income_pp_modified=income_pp; /* Label some of the major countries */ length labeled_countries $50; if country in ( 'China' 'India' 'USA' 'Japan' 'South Africa' 'Nigeria' 'Russia' 'Congo, Dem. Rep.' 'Ethiopia' 'Bangladesh' 'Vietnam' 'Kuwait' 'Qatar' 'Pakistan' 'Canada' 'Afghanistan' 'Brazil' 'Mexico' 'Indonesia' 'Cambodia' 'Saudi Arabia' 'Macao, China' 'Gabon' 'Iran' 'Angola' 'Rwanda' 'Chile' 'Liberia') then labeled_countries=trim(left(country)); if labeled_countries='Congo, Dem. Rep.' then labeled_countries='Congo'; if labeled_countries='Macao, China' then labeled_countries='Macao'; /* Since we can't annotate "by year", fake it with a 'text' statement */ x_center_year=3000; y_center_year=55; run; /* This map created by wealth_and_health_map.sas */ data anno_map; length function $10 anchor $20 drawspace $20; function='image'; drawspace='datapercent'; anchor='bottomright'; x1=99.8; y1=0.3; widthunit='percent'; width=40.5; heightunit='percent'; height=26.3; layer='front'; image='wealth_and_health_map.png'; run; /* Let's annotate the footnote, so it will take less space. */ data anno_footnote; length function $10 anchor $20 drawspace $20; function='text'; drawspace='GraphPercent'; x1=0.8; y1=2.5; label="Data source: gapminder.com"; textcolor='gray77'; textsize=10; width=100; anchor='left'; run; data anno_all; set anno_map anno_footnote; run; /* Create a user-defined-format, so you can show $100,000 as $100,000+ on the axis. */ proc format; picture my_dollar low - 99999 = '00,000' (prefix='$') 100000 = '100,000+' (prefix='$') 100001 - high = '000,000,000' (prefix='$') ; run; /* Note that this program only outputs a gif animation file. I have a separate program (wealth_and_health_container.sas) that writes out the html file (wealth_and_health.htm) that displays the gif animation. */ options papersize=('8 in', '6 in') printerpath=gif animation=start animduration=.4 animloop=yes noanimoverlay; ods printer file="&name..gif"; ods graphics / width=8in height=6in imagefmt=gif; options nodate nonumber nobyline; /* Use this, so you won't get each graph also written out as SGPlot*.gif */ ods listing select none; %let ctext=cx526b81; title1 h=18pt c=&ctext "Wealth & Health of Nations"; proc sgplot data=bubble_data noautolegend uniform=all sganno=anno_all; by year; format life_expectancy comma5.0; /* format income_pp_modified dollar8.0; */ format income_pp_modified my_dollar.; label life_expectancy='Life Expectancy (years)'; label income_pp_modified='Income per person (GDP/capita, PPP$ inflation-adjusted)'; styleattrs backcolor=cxcedce3; text x=x_center_year y=y_center_year text=year / textattrs=(size=160pt weight=bold color=cxd3e0e6); bubble x=income_pp_modified y=life_expectancy size=population / /* Using a 3d shading effect might visually imply the dots are 3d, which would affect the visual perception of their size (you'd have to size based on volume, rather than 2d area). A shaded color also does not match the legend (map) colors exactly. */ /*dataskin=gloss*/ group=region bradiusmin=3pt bradiusmax=25pt datalabel=labeled_countries datalabelpos=top /* datalabelpos=center */ datalabelattrs=(color=gray33 size=8pt) ; /* order of colors = America, East Asia & Pacific, Europe & Central Asia, Middle East & North Africa, South Asia, Sub-Saharan Africa */ styleattrs datacolors=(cxe5ff2f cxff2f2f orange cx00ff00 cx2fbfe5 cxD15FEE); yaxis values=(15 to 90 by 5) valuesdisplay=( ' ' '20' '25' '30' '35' '40' '45' '50' '55' '60' '65' '70' '75' '80' '85' ' ' ) offsetmin=0 offsetmax=0 grid labelattrs=(color=&ctext weight=bold) valueattrs=(color=&ctext size=10pt) ; xaxis min=100 max=100000 /* Since this technique doesn't work, I'm using a user-defined-format instead */ /* valuesdisplay=('$100' '$1,000' '$10,000' '$100,000+') */ offsetmin=0 offsetmax=0 type=log logstyle=logexpand logbase=10 minor minorcount=8 grid minorgrid /* turn subpixel=off, or some minor grid lines might 'disappear' */ labelattrs=(color=&ctext weight=bold) valueattrs=(color=&ctext size=10pt) ; run; options printerpath=gif animation=stop; ods printer close; quit; ODS HTML CLOSE; ODS LISTING;