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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more