Skip to content

Instantly share code, notes, and snippets.

@ColinEberhardt
Last active February 3, 2023 22:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ColinEberhardt/32b0782562f83566fa82d2a4f2a2543f to your computer and use it in GitHub Desktop.
Save ColinEberhardt/32b0782562f83566fa82d2a4f2a2543f to your computer and use it in GitHub Desktop.
Weekly mileage vs. Marathon training plans
license: mit

This charts shows the average weekly training mileage for 1,000 London Marathon 2016 finishers as obtained from strava athletes data, versus a number of training plans from Runners World. The data is rendered using a combination of d3 and d3fc components.

<!DOCTYPE html>
<!-- include polyfills for custom event, Symbol and Custom Elements -->
<script src="//unpkg.com/babel-polyfill@6.26.0/dist/polyfill.js"></script>
<script src="//unpkg.com/custom-event-polyfill@0.3.0/custom-event-polyfill.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/document-register-element/1.8.0/document-register-element.js"></script>
<!-- use babel so that we can use arrow functions and other goodness in this block! -->
<script src="//unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="//unpkg.com/d3@5.5.0"></script>
<script src="//unpkg.com/d3fc@14.0.41"></script>
<style>
body {
font-family: sans-serif;
font-size: 1.2em;
}
.tick {
font-size: 1.2em;
}
#chart {
margin-top: 25px;
}
#chart>div {
width: 50%;
display: inline-block;
}
.cartesian-chart {
position: relative;
width: 50%;
}
.chart-label {
position: absolute;
top: -15%;
left: 10%;
}
#chart>div:nth-child(1) .x-axis,
#chart>div:nth-child(1) .x-label,
#chart>div:nth-child(2) .x-axis,
#chart>div:nth-child(2) .x-label {
display: none;
}
#chart>div:nth-child(1) .y-axis,
#chart>div:nth-child(1) .y-axis-label,
#chart>div:nth-child(3) .y-axis,
#chart>div:nth-child(3) .y-axis-label {
display: none;
}
.gridline-y {
display: none;
}
.gridline-x {
opacity: 0.5;
}
.area {
opacity: 0.1;
fill: blue;
}
.multi:nth-child(3) .line {
stroke-dasharray: 5, 5
}
</style>
<div id='chart'></div>
<script src="mileage-versus-plan.js" type='text/babel'></script>
40 37.62692308 26 25.93230769 19 19.57583893 13 18.4953271
48 39.20096154 32 27.18592593 25 20.95104895 20 18.87425743
51 39.65904762 35 27.79185185 29 20.95 22 19.13457944
43 39.37903226 31 28.44930556 27 21.766875 23 18.64678899
52 44.6 39 30.93197279 30 24.1224359 24 22.82636364
56 43.81967213 43 32.0589404 33 23.92051282 27 21.66
60 46.40081967 47 31.71466667 37 25.96325301 32 24.64561404
41 41.33125 36 30.13866667 31 25.1 31 23.28
62 45.03362069 48 29.22450331 41 23.31886792 36 21.38684211
44 44.5125 39 31.14802632 38 24.97267081 34 23.32605042
60 47.42627119 46 32.56438356 43 26.5128655 38 25.36347826
60 39.55481481 48 27.50451613 43 23.83885714 39 21.14690265
58 44.47984496 45 31.50764331 42 25.78313253 39 21.85309735
57 37.05078125 42 24.24509804 39 19.19710983 35 17.71578947
41 23.56693548 31 16.46405229 30 12.39515152 24 11.24770642
17 7.459090909 13 6.995731707 9 3.511827957 8.7 3.601526718
function shapeData(data, index, title) {
var series = d3.zip(
data.map(d => d[index]),
data.map(d => d[index + 1])
);
series.title = title;
return series;
}
d3.text('mileage-versus-plan.csv').then(text => {
var data = d3.csvParseRows(text, d => d.map(Number));
var chartData = [
shapeData(data, 0, 'sub 3:00 marathon'),
shapeData(data, 2, 'sub 3:30 marathon'),
shapeData(data, 4, 'sub 4:00 marathon'),
shapeData(data, 6, 'sub 4:30 marathon')
];
var upperLine = fc.seriesSvgLine()
.crossValue((d, i) => i + 1)
.mainValue(d => d[0]);
var lowerLine = fc.seriesSvgLine()
.crossValue((d, i) => i + 1)
.mainValue(d => d[1]);
var area = fc.seriesSvgArea()
.crossValue((d, i) => i + 1)
.mainValue(d => d[0])
.baseValue(d => d[1]);
var gridlines = fc.annotationSvgGridline()
.yTicks(5);
var multi = fc.seriesSvgMulti()
.series([gridlines, area, upperLine, lowerLine]);
var chart = fc.chartCartesian(
d3.scaleLinear(),
d3.scaleLinear()
)
.xDomain([0.5, data.length + 0.5])
.yDomain([0, 65])
.xLabel('Week')
.yTicks(5)
.chartLabel(d => d.title)
.svgPlotArea(multi);
d3.select('#chart')
.selectAll('div')
.data(chartData)
.enter()
.append('div')
.attr('style', 'height: 200px')
.call(chart);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment