Unlisted
Edited
Dec 19, 2023
Insert cell
Insert cell
truncated_strava@2.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
styles = htl.svg`<style>
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;700&display=swap');
text {
font-family: "Space Grotesk" !important;
font-size: 12px !important;
font-weight: 400 !important;
color: #636363 !important;
}
span {
font-family: "Space Grotesk" !important;
font-weight:700 !important;
color: #636363;
}
h3 {
font-family: "Space Grotesk" !important;
font-size: 14px !important;
font-weight: 400 !important;
}
h2{
font-family: "Space Grotesk" !important;
font-size: 16px !important;
font-weight: 700 !important;
}
.cum {
color: #24cde6;
}
.dist {
color: #1f4788;
}
.dist-week {
color: #7b63ff;
}
.pace {
color: #ce00a0;
}
.hr {
color: lime;
}
figure {
margin-bottom: 20px;
}
label {
font-family: "Space Grotesk" !important;
}
`
Insert cell
xAxis = Plot.axisX({ticks: "month", tickSize: 16, tickPadding: -11, tickFormat: " %b", textAnchor: "start", stroke:"#636363", strokeWidth:1, strokeOpacity:0.2})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// time of day?
// does time of day have an affect
// color the dot based on if morning, afternnon, night
// note i did not have a plan for any run other than distance
Plot.plot({
title: "Pace over time against time of day",
subtitle: "Does time of day have an impact on running performance?",
style: {
fontFamily: "system-ui",
fontSize: "15px",
overflow: "visible"
},
color: {
type: "categorical",
domain: ["morning", "afternoon", "night", "late_night"],
range: ["yellow", "orange", "blue", "black"],
legend:true
},
marks: [
Plot.dot(strava, {x: "x_date", y: "x_min/mi", fill: (data) => {

const colors = {
morning: 'yellow',
afternoon: 'orange',
night: 'blue',
late_night: 'black',
};

const timeRanges = {
morning: { start: '06:00:00', end: '12:00:00' },
afternoon: { start: '12:00:00', end: '18:00:00' },
night: { start: '18:00:00', end: '00:00:00' },
late_night: { start: '00:00:00', end: '06:00:00' },
};


const startDate = new Date(`1970-01-01T${data.start_date.split(" ")[1]}`);
const color = Object.keys(timeRanges).find((timeRange) => {
const { start, end } = timeRanges[timeRange];
const startTime = new Date(`1970-01-01T${start}`);
const endTime = timeRange == "night" ? new Date(`1970-01-02T${end}`) : new Date(`1970-01-01T${end}`);
return startDate >= startTime && startDate < endTime;
});
return colors[color];

}}),
d3.groups(addTimeOfDayProp(strava), (d) => d.timeOfDay).map(([s]) =>
Plot.density(addTimeOfDayProp(strava), {
x: "x_date",
y: "x_min/mi",
weight: (d) => d.timeOfDay === s ? 1 : -1,
fill: () => {
const colors = {
morning: 'yellow',
afternoon: 'orange',
night: 'blue',
late_night: 'black',
}
return colors[s]},
fillOpacity: 0.2,
thresholds: [0.01]
})
),
Plot.lineY(strava, Plot.windowY(8, {x: "x_date", y: "x_min/mi", stroke: "red", curve:'basis'})),
Plot.linearRegressionY(strava, {x: "x_date", y: "x_min/mi"})
]
})

Insert cell
// time of day?
// does time of day have an affect
// color the dot based on if morning, afternnon, night
// note i did not have a plan for any run other than distance

// i should just do the thresholds by seconds instead of using complicated date stuff
Plot.plot({
title: "Pace over time against time of day",
subtitle: "Does time of day have an impact on running performance?",
color: {
type: "categorical",
domain: ["morning", "afternoon", "night", "late_night"],
range: ["yellow", "orange", "blue", "black"],
legend:true
},
marks: [
Plot.dot(addTimeOfDayInMSProp(strava), {x: "startTime", y: "x_min/mi", fill: (data) => {
const colors = {
morning: 'yellow',
afternoon: 'orange',
night: 'blue',
late_night: 'black',
};

const timeRanges = {
morning: { start: '09:00:00', end: '15:00:00' },
afternoon: { start: '15:00:00', end: '21:00:00' },
night: { start: '21:00:00', end: '03:00:00' },
late_night: { start: '03:00:00', end: '09:00:00' },
};



const startDate = new Date(`1970-01-01T${data.start_date.split(" ")[1]}`);
const color = Object.keys(timeRanges).find((timeRange) => {
const { start, end } = timeRanges[timeRange];
const startTime = new Date(`1970-01-01T${start}`);
const endTime = timeRange == "night" ? new Date(`1970-01-02T${end}`) : new Date(`1970-01-01T${end}`);
return startDate >= startTime && startDate < endTime;
});

return colors[color];
}}),
]
})

Insert cell
fivekpaceChart=Plot.plot({
title:html`<h2 class="pace">pace</h2>`,
subtitle: html`<h3>average pace per run, minutes per mile for runs less than 4 miles and greater than 3 miles</h3>`,
style: {
overflow:"visible"
},
y:{
domain:[7,18],
label:"min/mi"
// grid:true,
// ticks: 5,
// type:"log",
},
x: {
label: "date"
},
marks: [
Plot.dot(strava, {filter:(e) => e.x_mi<4 && e.x_mi>3,
x: "x_date", y: "x_min/mi", fill: paceColor, fillOpacity:0.2, stroke: paceColor, strokeWidth:(e)=>isMarathon(e)?1.5:0.5}),
Plot.lineY(strava, Plot.windowY(8, {filter:(e) => e.x_mi<4 && e.x_mi>3,x: "x_date", y: "x_min/mi", stroke: "red", curve:'basis', strokeWidth:0.7, stroke: "gray"})),
Plot.linearRegressionY(strava, {filter:(e) => e.x_mi<4&& e.x_mi>3,x: "x_date", y: "x_min/mi", fill: "#ce00a0", stroke: "#ce00a0", strokeWidth:2.5}),
Plot.axisY({ tickSize: 0, interval:0.5, tickFormat:(e) => {return decimalToMinutes(e)}, stroke:"#636363", strokeWidth:1, strokeOpacity:0.2, grid:true, filter:(e)=>{
// filter out tick marks that are granular above 11 min
return (e>10 || e<8) && Math.floor(e) != e ? false : true;
}}),
Plot.gridY({interval: 0.5, filter:(e)=>{
// filter out tick marks that are granular above 11 min
return (e>10 || e<8) && Math.floor(e) != e ? false : true;
}}), xAxis, () => styles,
]
})
Insert cell
paceColorCategorized =(e) => {
if(e.id== "10323401108"){
return "red"
}
if ( e.x_mi >= 3 && e.x_mi <4 && show5k) {
return "black"
}
// if (e.x_mi<4) {
// return "black"
// }
return "#ce00a0"
if (e.x_mi<3) {
return "blue"
}
}
Insert cell
keep5k = (e) => e.x_mi >= 3 && e.x_mi <4 && show5k
Insert cell
viewof show5k = Inputs.toggle({label: "show 5k improvement"})
Insert cell
categorizedPaceChart=Plot.plot({
title:html`<h2 class="pace">pace</h2>`,
subtitle: html`<h3>average pace per run, minutes per mile</h3>`,
style: {
overflow:"visible"
},
color: {
type: "categorical",
domain: ["3mi - 4mi"],
range: ["black"],
opacity:0.25,
stroke:0,
legend:show5k
},
y:{
domain:[7,18],
label:"min/mi"
// grid:true,
// ticks: 5,
// type:"log",
},
x: {
label: "date"
},
marks: [
Plot.dot(strava, {x: "x_date", y: "x_min/mi", fill: paceColorCategorized, fillOpacity:0.2, stroke: paceColorCategorized, strokeWidth:(e)=>isMarathon(e)?1.5:0.5}),
Plot.lineY(strava, Plot.windowY(8, {x: "x_date", y: "x_min/mi", stroke: "red", curve:'basis', strokeWidth:0.7, stroke: "gray"})),
Plot.linearRegressionY(strava, {x: "x_date", y: "x_min/mi", fill: "#ce00a0", stroke: "#ce00a0", strokeWidth:2.5}),
Plot.linearRegressionY(strava, {filter: keep5k, x: "x_date", y: "x_min/mi", fill: paceColorCategorized, stroke: paceColorCategorized, strokeWidth:2.5}),
Plot.axisY({ tickSize: 0, interval:0.5, tickFormat:(e) => {return decimalToMinutes(e)}, stroke:"#636363", strokeWidth:1, strokeOpacity:0.2, grid:true, filter:(e)=>{
// filter out tick marks that are granular above 11 min
return (e>10 || e<8) && Math.floor(e) != e ? false : true;
}}),
Plot.gridY({interval: 0.5, filter:(e)=>{
// filter out tick marks that are granular above 11 min
return (e>10 || e<8) && Math.floor(e) != e ? false : true;
}}), xAxis, () => styles, Plot.text(strava, { filter: isMarathon,x: "x_date", y: "x_min/mi", dx : -46,dy:20, text: (d) => `half-marathon`, fill:"red", rotate:-25 }),
]
})
Insert cell
hrOrderedChart=Plot.plot({
title:html`<h2 class="hr">heart rate</h2>`,
subtitle: html`<h3>average heartrate per run, bpm</h3>`,
y:{
domain:[140,180],
label:"bpm",
grid:true,
// ticks: 5,
tickSize:0,
// type:"log",
},
color: {
type: "categorical",
domain: ["zone 2", "zone 3", "zone 4",],
range: ["#46eee1", "#bdfe00", "#ff820a"],
opacity:0.25,
stroke:0,
legend:true
},
x: {
label: "date",
domain:[strava[0].x_date, strava[strava.length-1].x_date]
},
marks: [
// Plot.legend({color: {type: "linear", legend:true}}),
// Plot.legend( {color: {
// type: "categorical",
// domain: ["zone 2", "zone 3", "zone 4",],
// range: ["#46eee1", "#bdfe00", "#ff820a"],

// stroke:0,
// legend:true
// }}),
Plot.dot(strava, {x: "x_date", y: "average_heartrate", z:null,fill: (e) =>isMarathon(e)?"red":"lime", fillOpacity:0.2, stroke: (e)=> isMarathon(e)?"red":"lime", strokeWidth:(e)=>isMarathon(e)?1.5:0.5}),
Plot.lineY(strava, Plot.windowY(9, {x: "x_date", y: "average_heartrate", curve:'basis', strokeWidth:0.7, stroke: "gray"})),
Plot.linearRegressionY(strava, {x: "x_date", y: "average_heartrate", fill: "lime", stroke: "lime", strokeWidth:2.5}),
()=>styles,
Plot.text(strava, { filter: isMarathon,x: "x_date", y: "average_heartrate", dx : -46,dy:-20, text: (d) => `half-marathon`, fill:"red", rotate:25 }),
Plot.rect([strava[0]], {
x1: strava[0].x_date, // or ([x1]) => x1
y1: 140, // or ([, y1]) => y1
x2: strava[strava.length-1].x_date, // or ([,, x2]) => x2
y2: 152, // or ([,,, y2]) => y2
fill:"#46eee1",
fillOpacity:0.05,
}), Plot.rect([strava[0]], {
x1: strava[0].x_date, // or ([x1]) => x1
y1: 153, // or ([, y1]) => y1
x2: strava[strava.length-1].x_date, // or ([,, x2]) => x2
y2: 165, // or ([,,, y2]) => y2
fill:"#bdfe00",
fillOpacity:0.05,
}), Plot.rect([strava[0]], {
x1: strava[0].x_date, // or ([x1]) => x1
y1: 166, // or ([, y1]) => y1
x2: strava[strava.length-1].x_date, // or ([,, x2]) => x2
y2: 179, // or ([,,, y2]) => y2
fill:"#ff820a",
fillOpacity:0.05,
}), ,xAxis, () => styles,
// Plot.rectX(strava,{y1: 140, y2: 156, x: strava[strava.length-1].x_date, fill:"green"}),

// Plot.areaY(strava, {x:"x_date", y:"average_heartrate"})
// Plot.areaX(strava,{y:{domain:[140,150]}, x1:new Date("2023-09-01"), x2: new Date("2023-09-01"), fill:"blue", fillOpacity:1})

// Plot.areaX(strava, {x: "100%", y: "average_heartrate", fillOpacity: 0.3}),
]
})
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