%let name=ironman_raleigh_2017; filename odsout '.'; /* Using data from: http://www.ironman.com/triathlon/events/americas/ironman-70.3/raleigh/results.aspx?ps=20#axzz4j8rD3kK6 */ filename file1 'ironman_raleigh_2017.txt'; data iron_data; infile file1 lrecl=200 pad firstobs=1 dlm='09'x; /* tab-delimited */ length name $100 country $3; informat swim_time bike_time run_time total_finish_time time8.0; format swim_time bike_time run_time total_finish_time time8.0; input name country div_rank gender_rank overall_rank swim_time bike_time run_time total_finish_time points; format sum_time total_transition_time time8.0; length first_name last_name $50; first_name=scan(name,2,','); last_name=scan(name,1,','); sum_time=swim_time+bike_time+run_time; total_transition_time=total_finish_time-sum_time; if total_transition_time>0 then suspect='*'; if total_finish_time^=. then output; run; goptions device=png; goptions noborder; ODS LISTING CLOSE; ODS HTML path=odsout body="&name..htm" (title="Ironman 70.3 Raleigh - 2017") style=htmlblue; goptions gunit=pct ftitle="albany amt" ftext="albany amt" htitle=18pt htext=11pt; axis1 label=(j=c f='albany amt/bold' 'Finish Time' f='albany amt' j=c 'hours') order=('00:00:00't to '10:00:00't by '01:00:00't) value=(t=1 '') minor=none offset=(0,0); axis2 label=(f='albany amt/bold' 'Overall Rank') value=(t=1 '1') minor=none offset=(0,0); symbol1 value=circle height=2.0 c=A0000ff33 interpol=none; title1 ls=1.5 "Ironman 70.3 Triathlon Finishers, Raleigh, NC (2017)"; goptions xpixels=1000 ypixels=600; proc gplot data=iron_data; format total_finish_time time5.0; plot total_finish_time*overall_rank / vaxis=axis1 haxis=axis2 noframe autohref chref=graydd autovref cvref=graydd des='' name="&name._plot"; run; data rank_grid; do possible_ranks = 1 to 1467 by 1; output; end; run; proc sql noprint; create table iron_data as select unique rank_grid.possible_ranks, iron_data.* from rank_grid left join iron_data on rank_grid.possible_ranks=iron_data.overall_rank; quit; run; data iron_data; set iron_data; if overall_rank=. then overall_rank=possible_ranks; run; data bar_data; set iron_data; /* only put mouseover text on the last bar segment, to keep size of html file down */ length my_html $300; my_html= 'title='||quote( trim(left(name))||'0d'x|| 'overall rank: '||trim(left(overall_rank))||'0d'x|| '--------------'||'0d'x|| trim(left(put(swim_time,time8.0)))||' swim'||'0d'x|| trim(left(put(bike_time,time8.0)))||' bike'||'0d'x|| trim(left(put(run_time,time8.0)))||' run '||'0d'x|| trim(left(put(total_transition_time,time8.0)))||' transitions '||'0d'x|| '--------------'||'0d'x|| trim(left(put(total_finish_time,time8.0)))||' finish')|| ' href='||quote('http://www.google.com/search?&q=triathlon+'|| "'"||trim(left(first_name))||' '||trim(left(last_name))||"'"); /* this is just so the blank/gap bars will show up */ if swim_time=. then swim_time=0; if bike_time=. then bike_time=0; if run_time=. then run_time=0; if total_transition_time=. then total_transition_time=0; time=swim_time; bar_order=1; activity='Swim'; output; time=bike_time; bar_order=2; activity='Bike'; output; time=run_time; bar_order=3; activity='Run'; output; time=total_transition_time; bar_order=4; activity='Transitions'; output; run; /* The default axes get a bit 'stretched' when the size and proportions of the graph are extreme (like this one). Therefore, let's annotate the axes, to get them just the way we want them... */ data anno_maxis; length function $8 text $100; xsys='1'; ysys='2'; hsys='3'; when='a'; do y=1, 1467, 10 to 1460 by 10; function='label'; position='<'; size=.; x=0; text=trim(left(y))||'a0a0'x; output; end; do y=1, 1467, 10 to 1460 by 10; function='move'; x=0; output; function='draw'; color='grayaa'; position=''; size=.001; x=-.7; output; end; run; data anno_raxis; length function $8 text $100; xsys='2'; ysys='1'; hsys='3'; when='a'; do x = '01:00:00't to '10:00:00't by '01:00:00't; function='label'; size=.; text=trim(left(put(x,time5.0))); y=100; position='2'; output; y=0; position='8'; output; end; run; data anno_text; length function $8 style $35 text $100; hsys='d'; when='a'; function='label'; xsys='3'; ysys='3'; x=50; y=99.75; size=18; text="Ironman 70.3 Triathlon Finishers, Raleigh, NC (2017)"; output; style='albany amt/bold'; size=.; x=50; y=99.3; text="Time (hours)"; output; x=4.0; y=98.58; text="Overall"; output; y=y-.2; text="Rank"; output; run; /* Ignore errors, if you get any... */ data ignore_error; function='seterror'; size=100000; output; run; data anno_all; set ignore_error anno_maxis anno_raxis anno_text; run; legend1 label=none position=(top right inside) across=1 mode=share offset=(-34.5,.1) value=(j=left 'swim' 'bike' 'run' 'transitions') shape=bar(.15in,.15in); axis1 label=(c=white) /* if I use a custom order (to show the 'gaps'), then the annotated maxis values don't show up in the right place (fixed in 9.4m5 in S0913562 & S0969460). In the meantime, I'll insert actual values into the data instead. order=(1 to 1467 by 1) */ value=none offset=(.08in,.08in); axis2 label=none major=(height=.05in) major=none minor=none order=('00:00:00't to '10:00:00't by '01:00:00't) value=none style=0 offset=(0,.3in); pattern1 v=s c=cx60AFFE; pattern2 v=s c=cx8CDD81; pattern3 v=s c=cxFFA07A; pattern4 v=s c=grayee; title; footnote; goptions xpixels=800 ypixels=9000; ods html anchor='bar'; proc gchart data=bar_data anno=anno_all; format time time8.; hbar overall_rank / discrete type=sum sumvar=time nostats subgroup=bar_order maxis=axis1 raxis=axis2 noframe autoref clipref lref=34 cref=gray55 space=0 coutline=gray77 legend=legend1 html=my_html des='' name="&name._bar"; run; /* proc print data=iron_data (obs=10) noobs; var name swim_time bike_time run_time total_finish_time sum_time total_transition_time; run; */ quit; ODS HTML CLOSE; ODS LISTING;