I'm a bit of a boat paddling enthusiast, as you might have guessed from some of my previous paddling blog posts. The amount of exertion in race-paddling is similar to running - the longest race I've paddled in a race so far was 13 miles (half-marathon distance). But in the back of my mind, I've always been intrigued by the long paddling races ... and by long, I mean 100 miles.

Paddling 100 Miles

How long does it take to paddle 100 miles? A typical recreational kayak might go around 3mph, and would take around 33 hours. On a lake with no current or wind, I typically paddle my surfski (fast/racing kayak) around 6mph when I'm working it pretty hard ... which would take around 17 hours.

In the recent 100 mile California River Quest, April Zilg won the 100 mile race in her outrigger canoe (OC1). She paddled the 100 miles in 10 hours and 50 minutes. That's averaging 9.23 mph for the 100 miles! April is one of my paddling inspirations, and I'm especially proud of her win because she lived here in North Carolina before moving out west. I even had the privilege of paddling against her in a 13 mile race a few years ago - and yes, she beat me soundly! Here's a picture of April in that race (photo credit Brian Meyer):

Talking about paddling speeds is one thing, but of course I couldn't call myself a Graph Guy if I didn't plot some data. Therefore follow along as I create a map of the 100 mile race course, and a couple of graphs of the race results. If you don't want to wade through all the details, feel free to jump to the final graph at the end!

Mapping the Race Course

The race has a starting point, and a 'checkpoint' at each 25 miles. I estimated the latitude/longitude coordinates for each of these checkpoints, and put them in a dataset:

data checkpoints;

input lat long labeltext $ 25-50;

datalines;

40.5912103 -122.3875669 Start

40.4174706 -122.1947685 25 miles

40.1745591 -122.2285291 50 miles

39.9085391 -122.0914473 75 miles

39.7345816 -121.9580843 100 miles

;

run;

Plotting Checkpoints on Basic/Default Map

I then plotted these points as red markers on a map using the following minimal code:

proc sgmap plotdata=checkpoints noautolegend;

openstreetmap;

scatter x=long y=lat / markerattrs=(color=red size=12px symbol=circlefilled);

run;

Changing Map Proportions/Size

Hmm ... sometimes simple code doesn't produce the best map. I would like to actually see the river on the map, wouldn't you?! If we change the size and proportion of the map, it will bring out more detail. So let's try that.

ods graphics / width=500px height=1100px;

proc sgmap plotdata=checkpoints noautolegend;

openstreetmap;

scatter x=long y=lat / markerattrs=(color=red size=12px symbol=circlefilled);

run;

Changing from Streetmap to NatGeo Map

That's a bit better. Now we can see the river ... but the river still isn't the main focus of the map. Since this is a street map (from OpenStreetmaps), its main focus is streets, not rivers. Therefore let's try a different map that might have more emphasis on the natural features, rather than the roads - Esri's NatGeo_World_Map ...

proc sgmap plotdata=checkpoints noautolegend;

esrimap url="http://services.arcgisonline.com/arcgis/rest/services/NatGeo_World_Map";

scatter x=long y=lat / markerattrs=(color=red size=12px symbol=circlefilled);

run;

Labeling Checkpoints

The river is much more prominent/visible in this map (and the roads are de-emphasized). Now let's add some text labels to the red markers. I add a statement to plot the text at the lat/long locations, and position it to the right of the coordinate. The text would overlap the red marker a little, therefore I prepend some non-blanking spaces (character 'a0'x) to the left of the text.

data checkpoints; set checkpoints;

labeltext='a0a0'x||trim(left(labeltext));

run;

proc sgmap plotdata=checkpoints noautolegend;

esrimap url="http://services.arcgisonline.com/arcgis/rest/services/NatGeo_World_Map";

scatter x=long y=lat / markerattrs=(color=red size=12px symbol=circlefilled);

text x=long y=lat text=labeltext / textattrs=(color=red size=11pt weight=bold) position=right;

run;

Adding Backlight Around Text

The map is looking pretty good, but the text label for the 50 miles marker is blending in a little with the black text in the Esri basemap behind it. Therefore let's use the new backlight= option to add some light contrast color behind/around the text. And while we're using new features, let's also use the new url= option to add a link to each marker, so you can click on it to see the Google map of that area. And the tip= option to add some custom text for the mouse-over. (These new features are are available in Viya 3.5.)

data checkpoints; set checkpoints;

label labeltext='Checkpoint';

labeltext='a0a0'x||trim(left(labeltext));

length drill_down $300;

drill_down='https://www.google.com/maps/@'||trim(left(lat))||','||trim(left(long))||',16z?hl=en';

run;

proc sgmap plotdata=checkpoints noautolegend;

esrimap url="http://services.arcgisonline.com/arcgis/rest/services/NatGeo_World_Map";

scatter x=long y=lat / markerattrs=(color=red size=12px symbol=circlefilled)

url=drill_down tip=(labeltext);

text x=long y=lat text=labeltext / textattrs=(color=red size=11pt weight=bold) position=right

url=drill_down tip=(labeltext) backlight=1;

run;

Here is the final map. If you want to try the mouse-over text, and drill-down links to the Google maps, click here to see the interactive version.

Race Results - Spaghetti Plot

Spaghetti plots have gotten a bad name ... but, even so, I think they are a decent way to get a quick first-look at your data. Here's a spaghetti plot of the 2019 race results. Yes, there are too many colors. And yes, the lines and markers overlap so that you can't follow any specific racer's data through the plot. But still, it let's you see what all the data is doing, in general.

proc sgplot data=plot_data noautolegend noborder;

by race_distance_numeric notsorted;

series x=distance y=time / group=racer_num lineattrs=(pattern=solid)

markers markerattrs=(symbol=circle size=7px)

tip=(full_name class category distance time avg_mph) url=name_search;

yaxis display=(noline noticks)

labelattrs=(size=11pt weight=bold) labelpos=top

grid gridattrs=(pattern=dot color=gray88)

valueattrs=(size=11pt) offsetmin=0 offsetmax=0;

xaxis labelattrs=(size=11pt weight=bold)

values=(0 to 100 by 25) valueattrs=(size=11pt);

run;

Race Results - A Better Plot

Even though the spaghetti plot (above) is useful, it's still not a great plot. Therefore I put on my Graph Guy thinking cap, and came up with a better way to graph the data. In this plot, it's much easier to see and compare the individual racers. And the graph just looks better, also! (If you'd like to see the interactive version, click here. If you'd like to see the SAS code used to create the map and graphs, click here.)

proc sgplot data=plot_data noautolegend noborder;

by race_distance_numeric notsorted;

series y=full_name x=time / group=full_name lineattrs=(pattern=solid color=cx49E20E)

markers markerattrs=(symbol=circlefilled color=cxff4444)

tip=(full_name class category distance time avg_mph) url=name_search;

xaxis grid gridattrs=(pattern=dot color=gray88) offsetmax=0 valueattrs=(size=10pt);

yaxis display=(nolabel noline noticks) valueattrs=(size=10pt);

run;

What's Next?

How does April plan to follow up her win of the 100 mile race? Well, of course, by competing in a 1000 mile race - the Yukon 1000, coming up in July 2020! I encourage you to follow along on her website, to see how she prepares, etc. And feel free to leave her a message of encouragement in the comments section below.