Public
Edited
Apr 13, 2023
Insert cell
Insert cell
rawdata = FileAttachment("training@3.csv").csv({typed: true})
Insert cell
data = rawdata.map(rawrow => {
// don't modify the raw data
const row = {...rawrow};
if (row["dist"]) {
row["dist"] = parseFloat(row["dist"].split(' ')[0])
}
if(row["pace"]) {
let [mins, secs] = Array.from(row["pace"].matchAll(/\d+/g)).map(parseFloat)
row["pace"] = 60*mins + secs
}
let [month, day] = Array.from(row["date"].matchAll(/\d+/g)).map(parseFloat)
const year = month==12?2022:2023
row["date"] = new Date(year, month-1, day)

return row;
})
Insert cell
Plot.plot({
inset: 8,
grid: true,
color: {
legend: true,
},
marks: [
Plot.dot(data, {x: "dist", y: "vo2"})
]
})
Insert cell
Plot.plot({
inset: 8,
grid: true,
color: {
legend: true
},
marks: [
Plot.dot(data, { x: "date", y: "vo2", stroke: "dist" }),
Plot.line(
data,
Plot.regression({
x: "date",
y: "vo2",
// "poly" type should give a polynomial curve, but gives a line for reasons I don't understand
// type: "poly"
// order: 3,
type: "loess",
bandwidth: 0.5
})
)
]
})
Insert cell
{
let p = Plot.plot({
inset: 8,
grid: true,
color: {
legend: true,
domain: ["short run", "medium", "long run"],
scheme: "reds"
},
marks: [
Plot.axisY({ label: "↑ vo2 (estimated fitness)", className: "yaxis" }),
Plot.dot(data, {
x: "date",
y: "vo2",
stroke: (d) =>
d.dist >= 15 ? "long run" : d.dist > 10 ? "medium" : "short run",
fill: (d) =>
d.dist >= 15 ? "long run" : d.dist > 10 ? "medium" : "short run"
}),
// A solid line for the more tightly-bound regression
Plot.line(
data,
Plot.regression({
x: "date",
y: "vo2",
// "poly" type should give a polynomial curve, but gives a line for reasons I don't understand
// type: "poly",
// order: 3,
type: "loess",
bandwidth: 0.2,
stroke: "red"
})
),
// A dashed line for the more long-term regression
Plot.line(
data,
Plot.regression({
x: "date",
y: "vo2",
strokeDasharray: [1.5, 4],
strokeWidth: 1.5,
type: "loess",
bandwidth: 0.8,
stroke: "red"
})
)
]
});

d3.select(p).selectAll("svg > g").style("font-size", "12px");

return p;
}
Insert cell
// https://observablehq.com/@fil/plot-regression
reg = require("d3-regression@1")
Insert cell
function addRegression(Plot) {
Plot.regression = function ({ x, y, type, bandwidth, order, ...options }) {
type = String(type).toLowerCase();
const regressor =
type === "quad"
? reg.regressionQuad()
: type === "poly"
? reg.regressionPoly()
: type === "pow"
? reg.regressionPow()
: type === "exp"
? reg.regressionExp()
: type === "log"
? reg.regressionLog()
: type === "loess"
? reg.regressionLoess()
: reg.regressionLinear();
if (bandwidth && regressor.bandwidth) regressor.bandwidth(bandwidth);
if (order && regressor.order) regressor.order(order);

const z = options.z || options.stroke; // maybeZ
return Plot.transform(options, function (data, facets) {
const X = Plot.valueof(data, x);
const Y = Plot.valueof(data, y);
const Z = Plot.valueof(data, z);
console.log(X, Y, X.length, Y.length)
regressor.x((i) => X[i]).y((i) => Y[i]);

const regFacets = [];
const points = [];
for (const facet of facets) {
const regFacet = [];
for (const I of Z ? d3.group(facet, (i) => Z[i]).values() : [facet]) {
const reg = regressor(I);
for (const d of reg) {
const j = points.push(d) - 1;
if (z) d[z] = Z[I[0]];
regFacet.push(j);
}
}
regFacets.push(regFacet);
}
return { data: points, facets: regFacets };
});
};
return Plot;
}
Insert cell
Plot = addRegression(await require("@observablehq/plot"))
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more