Public
Edited
Jul 11, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
dotplot = Plot.dot(s2019_t.fold(t_a).objects(), { x: "value", y: "key" }).plot()
Insert cell
Insert cell
{
const t = s2019_t
.fold(t_a)
.orderby(aq.desc("key"))
.groupby("Number")
.derive({ cum_time: aq.rolling((d) => op.sum(d.value)) });
const thresholds = 50;
return Plot.plot({
marginLeft: 50,
grid: true,
width: 1000,
height: 300,
x: {
// axis: "top"
// label: "Percent (%) →",
ticks: 14,
tickRotate: 30,
transform: (d) => d3.timeSecond.offset(Date.UTC(2022, 6, 9, 7), d)
},
y: {
// domain: ages,
label: "Leg"
},
marks: [
Plot.ruleX([0, 3600 * 15 - 600]),
Plot.tickX(
t.objects(),
// Plot.normalizeX({ x: "cum_time", y: "key" }) //z: "state",basis: "sum",
{ x: "cum_time", y: 0, stroke: "key" }
),
Plot.areaY(
t.objects(),
Plot.binX(
{ y2: "count" },
{
fillOpacity: 0.4,
x: { thresholds: thresholds, value: "cum_time" },
fill: "key",
// mixBlendMode: "multiply",
curve: "natural"
}
)
),
Plot.lineY(
t.objects(),
Plot.binX(
{ y: "count" },
{
// fillOpacity: 0.4,
x: { thresholds: thresholds, value: "cum_time" },
stroke: "key",
// mixBlendMode: "multiply",
curve: "natural"
}
)
)
]
});
}
Insert cell
t = s2019_t
.fold(t_a)
.orderby(aq.desc("key"))
.groupby("Number")
.derive({ cum_time: aq.rolling((d) => op.sum(d.value)) })
Insert cell
Insert cell
{
const d = d3.timeParse("%I:%M%p")("7:00am"); //, 2022 7:00am

// return s;
return d3.timeSecond.offset(d, 108);
}
Insert cell
// compare cumulative Reverso times with actual cumulative times (remembering the ??)
Insert cell
Insert cell
s2019_t
.fold(t_a)
.groupby("key")
.rollup({
mean: (d) => op.mean(d.value),
min: (d) => op.min(d.value),
q25: (d) => op.quantile(d.value, 0.25),
q50: (d) => op.quantile(d.value, 0.5),
q75: (d) => op.quantile(d.value, 0.75),
q97: (d) => op.quantile(d.value, 0.97),
max: (d) => op.max(d.value)
})
.orderby(aq.desc("key"))
.derive({
min_cumul: aq.rolling((d) => d.sum(d.min)),
mean_cumul: aq.rolling((d) => d.sum(d.mean)),
q25_cumul: aq.rolling((d) => d.sum(d.q25)),
q50_cumul: aq.rolling((d) => d.sum(d.q50)),
q75_cumul: aq.rolling((d) => d.sum(d.q75)),
q97_cumul: aq.rolling((d) => d.sum(d.q97)),
max_cumul: aq.rolling((d) => d.sum(d.max))
})
.derive({
key: (d) => op.slice(d.key, 2),

mean_: aq.escape((d) => jpt(d.mean)),
q25_: aq.escape((d) => jpt(d.q25)),
q50_: aq.escape((d) => jpt(d.q50)),
q75_: aq.escape((d) => jpt(d.q75)),
q97_: aq.escape((d) => jpt(d.q97)),
mean_cumul_: aq.escape((d) => jpt(d.mean_cumul)),
q25_cumul_: aq.escape((d) => jpt(d.q25_cumul)),
q50_cumul_: aq.escape((d) => jpt(d.q50_cumul)),
q75_cumul_: aq.escape((d) => jpt(d.q75_cumul)),
q97_cumul_: aq.escape((d) => jpt(d.q97_cumul))
})
.select([
"key",
"mean_",
"q25_",
"q50_",
"q75_",
"q97_",
"mean_cumul_",
"q25_cumul_",
"q50_cumul_",
"q75_cumul_",
"q97_cumul_"
])
.rename({ key: "leg" })
.view()
Insert cell
Insert cell
Insert cell
legs_t
.fold(aq.all)
.groupby("key")
.rollup({
mean: (d) => op.mean(d.value),
q25: (d) => op.quantile(d.value, 0.25),
q50: (d) => op.quantile(d.value, 0.5),
q75: (d) => op.quantile(d.value, 0.75)
})
.orderby(aq.desc("key"))
.derive({
mean_cumul: aq.rolling((d) => d.sum(d.mean)),
q25_cumul: aq.rolling((d) => d.sum(d.q25)),
q50_cumul: aq.rolling((d) => d.sum(d.q50)),
q75_cumul: aq.rolling((d) => d.sum(d.q75))
})
.view()
Insert cell
data_t
.fold(aq.all)
.groupby("key")
.rollup({
mean: (d) => op.mean(d.value),
q25: (d) => op.quantile(d.value, 0.25),
q50: (d) => op.quantile(d.value, 0.5),
q75: (d) => op.quantile(d.value, 0.75)
})
// .orderby(aq.desc("key"))
// .derive({
// q25_cumul: aq.rolling((d) => d.sum(d.q25)),
// q50_cumul: aq.rolling((d) => d.sum(d.q50)),
// q75_cumul: aq.rolling((d) => d.sum(d.q75))
// })
.view()
Insert cell
parseHMS = d3.timeParse("%H:%M:%S.%L")
Insert cell
parseHMS("1:00:00.3")
Insert cell
jpt(jtp("1:12:13:01.9"))
Insert cell
t_a = {
let t_a = [1, 2, 3, 4, 5, 6, 7].map((d) => "TA" + d);
return t_a;
}
Insert cell
viewof legs_t = aq.from(legs_oa.map(decumulative)).view()
Insert cell
legs_t.numRows()
Insert cell
legs_oa = data_t.objects()
Insert cell
// viewof legs0_t = data_t.view()
Insert cell
Insert cell
// jtp test
jtp("1:12:13:01.9")
Insert cell
viewof data_t = aq.from(d2_a).select(t_a.concat("Number")).view()
Insert cell
t_a.map((w) => w + " Enter")
Insert cell
aq
.from(s2023_oa)
// .map(o )
.select(t_a.map((w) => w + " Enter"))
.view()
Insert cell
data_t.numRows()
Insert cell
// string to seconds
function st2sec(a_) {
// const a_ = data_a[0];
const ks = Object.keys(a_);
let aa = {};
const aa_ = Object.assign(aa, a_);
for (let k of ks) {
if (k.startsWith("TA") || k === "Finish") {
aa[k === "Finish" ? "TA7" : k] = jtp(a_[k]);
}
}
return aa;
}
Insert cell
// change strings in array to seconds (couldn't figure out how to do this in arquero because string methods..)
d2_a = data_a.map(st2sec)
Insert cell
// only take team times: women, men and mixed
// Relay,,,,,,,,,,300m,(rank),CP1,(rank),TA1,(rank),CP2,(rank),TA2,(rank),CP3a,(rank),CP3b,(rank),CP3c,(rank),TA3,(rank),CP4a,(rank),CP4b,(rank),TA4,(rank),TA4 Depart,(rank),CP5a,(rank),CP5b,(rank),TA5,(rank),TA5 Depart,(rank),CP6a,(rank),CP6b,(rank),CP6c,(rank),TA6,(rank),TA6 Depart,(rank),CP7a,(rank),Finish,(rank)\r

// ,1,14:47:28.1,,Orignial Joes,,763,Edmonton,Alberta,CA,1:24.0,3,57:27.5,1,1:27:01.2,2,2:25:53.6,1,3:10:31.4,1,3:52:57.2,1,4:44:39.9,1,5:20:24.7,1,6:10:37.3,1,6:47:00.7,1,7:41:17.1,1,8:25:55.2,1,8:26:11.4,1,9:05:22.5,1,10:09:59.9,1,10:46:18.7,1,10:46:35.8,1,11:26:48.4,1,,,,,13:57:10.7,1,13:57:22.3,1,14:24:06.2,1,14:47:28.1,1\r

data_a = {
let a = s.split("\n").slice(158, 158 + 75);
a[0] =
"Relay,Rank_1,TotalTime,A1,TeamName,A3,Number,City,Province,Country,300m,(rank),CP1,(rank),TA1,(rank),CP2,(rank),TA2,(rank),CP3a,(rank),CP3b,(rank),CP3c,(rank),TA3,(rank),CP4a,(rank),CP4b,(rank),TA4,(rank),TA4 Depart,(rank),CP5a,(rank),CP5b,(rank),TA5,(rank),TA5 Depart,(rank),CP6a,(rank),CP6b,(rank),CP6c,(rank),TA6,(rank),TA6 Depart,(rank),CP7a,(rank),Finish,(rank)";

return d3.csvParse(a.join("\n"));
}
Insert cell
Insert cell
viewof s2019_t = aq
.from(s2019_oa.map(st2sec))
.select(t_a.concat("Number"))
.derive({
TA7: (d) => d.TA7 - (d.TA1 + d.TA2 + d.TA3 + d.TA4 + d.TA5 + d.TA6)
})
.filter((d) => d.TA7 > 0)
.view()
Insert cell
viewof s2018_t = aq
.from(s2018_oa.map(st2sec))
.select(t_a.concat("Number"))
.derive({
TA7: (d) => d.TA7 - (d.TA1 + d.TA2 + d.TA3 + d.TA4 + d.TA5 + d.TA6)
})
.filter((d) => d.TA7 > 0)
.view()
Insert cell
viewof s2023_t = aq
.from(s2023_oa.map(st2sec))
.select(t_a.concat("Number"))
.derive({
TA7: (d) => d.TA7 - (d.TA1 + d.TA2 + d.TA3 + d.TA4 + d.TA5 + d.TA6)
})
.filter((d) => d.TA7 > 0)
.view()
Insert cell
s2019_oa = FileAttachment("lap_times_S7_2019.csv").csv()
Insert cell
s = FileAttachment("Overall Results - 2021 Sinister 7?@1.csv").text()
Insert cell
// seconds to string
jpt = function (secs) {
const h = Math.floor(secs / 3600);
const m = Math.floor((secs % 3600) / 60);
const s = Math.floor((secs % 3600) % 60);
// return h + ":" + (m < 10 ? "0" + m : m) + ":" + (s < 10 ? "0" + s : s);
return h + ":" + (m < 10 ? "0" + m : m);
}
Insert cell
// parse time string, returns seconds
jtp = function (str) {
// const str = "01:04:50.6";
// if (typeof str !== "string") return NaN;
// if (typeof str === 'string' || str instanceof String) {
const matches = str.match(/(\d+:)*(\d+):([\d\.]+)/);
return matches
? 3600 * (matches[1] ? matches[1].slice(0, -1) : 0) +
60 * +matches[2] +
+matches[3]
: NaN;
// }
}
Insert cell
import { aq, op } from "@uwdata/arquero"
Insert cell
allTimesList2018Sinister7 = FileAttachment("All Times List - 2018 Sinister 7.csv")
Insert cell
s2018_oa = {
let s2018_a = s2018_s.split("\n");
const header =
",rank,time,status,firstname,lastname,Bib #,Chip Numbers,R1 Name,R2 Name,R3 Name,R4 Name,R5 Name,R6 Name,R7 Name,TA1,(rank),TA2,(rank),TA3,(rank),TA4,(rank),TA5,(rank),TA6,(rank),Finish,(rank),Finish,(rank)";
s2018_a[1] = header;
const s2018h_s = s2018_a.slice(1).join("\n");
// return s2018h_s;
return d3.csvParse(s2018h_s).map(function (o) {
let oo = {};
Object.assign(oo, o);
oo["Number"] = o["Bib #"];
return oo;
});
}
Insert cell
s2018_s = FileAttachment("Team Category Results - 2018 Sinister 7.csv").text()
Insert cell
s2023_s = FileAttachment("Team S7 2023.csv").text()
Insert cell
s2023_oa = {
let s2023_a = s2023_s.split("\n");
const h23 =
"N0,N1,N2,N3,N4,N5,Bib #,N7,N8,CP1A,(rankCP1a),TA1 Enter,(rankTA1e),TA1 Exit,(rankTA1x),CP2,(rankCP2),TA2 Enter,(rankTA2e),TA2 Exit,(rankTA2x),CP3a,(rankCP3a),CP3b,(rankCP3b),CP3c,(rankCP3c),TA3 Enter,(rankTA3e),TA3 Exit,(rankTA3x),CP4a,(rankCP4a),CP4b,(rankCP4b),TA4 Enter,(rankTA4e),TA4 Exit,(rankTA4x),CP5a,(rankCP5a),CP5b,(rankCP5b),TA5 Enter,(rankTA5e),TA5 Exit,(rankTA5x),CP6a,(rankCP6a),CP6b,(rankCP6b),CP6c,(rankCP6c),TA6 Enter,(rankTA6e),TA6 Exit,(rankTA6x),CP7a,(rankCP7a),Finish,(rankFinish),Finish1,(rankFinish1)";
// const header_old =
// ",rank,time,status,firstname,lastname,Bib #,Chip Numbers,R1 Name,R2 Name,R3 Name,R4 Name,R5 Name,R6 Name,R7 Name,TA1,(rank),TA2,(rank),TA3,(rank),TA4,(rank),TA5,(rank),TA6,(rank),Finish,(rank),Finish,(rank)";
// const header =
// ",rank,time,status,firstname,lastname,Bib #,Chip Numbers,R1 Name,R2 Name,R3 Name,R4 Name,R5 Name,R6 Name,R7 Name,TA1,(rank),TA2,(rank),TA3,(rank),TA4,(rank),TA5,(rank),TA6,(rank),Finish,(rank),Finish,(rank)";
// s2018_a[1] = header;
// const s2018h_s = s2018_a.slice(1).join("\n");
// return s2018h_s;
s2023_a[0] = h23;
const s2023_sh = s2023_a.join("\n");
return d3.csvParse(s2023_sh).map(function (o) {
let oo = {};
// Object.assign(oo, o);
oo["Number"] = o["Bib #"];
oo["TA1"] = o["TA1 Enter"];
oo["TA2"] = o["TA2 Enter"];
oo["TA3"] = o["TA3 Enter"];
oo["TA4"] = o["TA4 Enter"];
oo["TA5"] = o["TA5 Enter"];
oo["TA6"] = o["TA6 Enter"];
oo["TA7"] = o["Finish"];

// oo["TA7 Enter"] = o["Finish"]
return oo;
});
}
Insert cell
// need to map TA1 Enter to TA1
//
s23_t = aq.from(s2023_oa.map(st2sec))
Insert cell
s23_t.view()
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