Public
Edited
Jun 21, 2024
Insert cell
Insert cell
Insert cell
function precomputeGaussianWeights() {
const weights = [];
for (let i = 1; i <= 300; i++) {
weights[i] = [];
for (let j = 1; j <= 300; j++) {
const distance = Math.abs(i - j);
const avgPickNo = (i + j) / 2;
const stdDev = Math.pow(avgPickNo + 2, 1 / 2.5) * 1;
weights[i][j] = gaussianWeight(distance, stdDev);
}
}
return weights;
}
Insert cell
expectedVal = expectedValueByPositionAndPick(picks)
Insert cell
function expectedValueByPositionAndPick(picks) {
const gaussianWeights = precomputeGaussianWeights();

const weightedValues = {};

for (const pick of picks) {
const { pos, pick: pickNumber, w_av } = pick;

if (!weightedValues[pos]) {
weightedValues[pos] = {};
}

for (
let comparisonPickNumber = 1;
comparisonPickNumber <= 224;
comparisonPickNumber++
) {
const weight = gaussianWeights[pickNumber][comparisonPickNumber];

if (!weightedValues[pos][comparisonPickNumber]) {
weightedValues[pos][comparisonPickNumber] = {
valueSum: 0,
weightSum: 0
};
}

weightedValues[pos][comparisonPickNumber].valueSum += w_av * weight;
weightedValues[pos][comparisonPickNumber].weightSum += weight;
}
}

const expectedValues = {};

for (const pos in weightedValues) {
expectedValues[pos] = {};

for (const pickNumber in weightedValues[pos]) {
const { valueSum, weightSum } = weightedValues[pos][parseInt(pickNumber)];

expectedValues[pos][parseInt(pickNumber)] = valueSum / weightSum;
}
}

return expectedValues;
}

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
positions = [...new Set(picks.map((d) => d.pos))]

Insert cell
ev_pos_pick = FileAttachment("ev_pos_pick.json").json()
Insert cell
byPosPick = {
const arr = [];
Object.keys(ev_pos_pick).forEach((pos) => {
const posPickValues = ev_pos_pick[pos];
Object.keys(posPickValues).forEach((k) => {
arr.push({
...posPickValues[k],
pick: +k,
pos,
round: Math.ceil(+k / 32)
});
});
});
return arr;
}
Insert cell
Plot.plot({
facet: {
data: byPosPick,
y: "pos"
// x: "round"
},
marks: [
Plot.frame(),
Plot.dot(byPosPick, { x: "pick", y: "value", fill: "value" })
]
})
Insert cell
rawPicks = FileAttachment("picks@1.json").json()
Insert cell
function valuePick(pick) {
const ev = expectedValue[pick.pick];
const diff = pick.est - ev;
return Math.max(0, Math.min(11, Math.round(diff / 14) + 5));
}
Insert cell
valuePick({ est: 30, pick: 40 })
Insert cell
function overallGrade(pick) {
return Math.max(0, Math.min(11, Math.round(pick.est / 12)));
}
Insert cell
Insert cell
Insert cell
groupedByTeam = groupByTeamSeason(picks).filter((d) => d.season < 2022)
Insert cell
Plot.plot({
title: "NFL Draft Value by Round",
subtitle: "Estimated Value of NFL Players by Round",
width: 800,
height: 700,
color: {
type: "ordinal",
scheme: "Set2"
},
y: { label: null, tickSize: 0, label: null, axis: null },
x: { grid: true, tickSize: 0, label: "Estimated Value" },
facet: {
data: groupedByTeam,
y: "team",
label: null
},
marginLeft: 80,
marginBottom: 60,
marks: [
//jitter for each observation
Plot.dot(groupedByTeam, {
x: (d) => d.diff,
y: (d) => +Math.random() * 0,
r: 5,
// fill: "round",
fill: "team",
dy: 5,
stroke: "white",
strokeWidth: 0.5,
//customize tooltip on hover with channels and tip format
channels: {
Team: "team",
Season: "season",
Value: "sumEst",
Capital: "sumEv",
Gain: "diff"
},
tip: { format: { fill: false, x: false, y: false, fy: false } }
})
]
})
Insert cell
posOptions = ["QB", "RB", "WR", "TE", "OL", "DT", "DE", "LB", "S", "CB"]
Insert cell
teamOptions = Array.from(new Set(picks.map((d) => d.team))).sort()
Insert cell
filteredPicks = picks.filter(
(d) =>
(selectedPos.length === 0 || selectedPos.includes(d.pos)) &&
(selectedTeam.length === 0 || selectedTeam.includes(d.team)) &&
(selectedYear.length === 0 || selectedYear.includes(d.season)) &&
(selectedPick.length === 0 || selectedPick.includes(d.pick))
)
Insert cell
yearOptions = d3.range(2006, 2024)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function markPointInterval(data, { y, x, fill, text, fillText }) {
const marks = [
//range
Plot.ruleY(
data,
Plot.groupY(
{ x1: "min", x2: "max" },
{ y: y, x: x, stroke: fill, strokeWidth: 2 }
)
),
//quartiles IQR
Plot.ruleY(
data,
Plot.groupY(
{ x1: (D) => d3.quantile(D, 0.25), x2: (D) => d3.quantile(D, 0.75) },
{ y: y, x: x, stroke: fill, strokeWidth: 4.5 }
)
),
//median dot
Plot.dot(
data,
Plot.groupY({ x: "median" }, { y: y, x: x, fill: fill, r: 14 })
),
//median text
Plot.text(
data,
Plot.groupY(
{ x: "median", text: (d) => d3.median(d).toFixed(1) },
{
y: y,
x: x,
fill: fillText,
text: text,
fontSize: 10,
fontWeight: 700
}
)
)
];

return marks;
}
Insert cell
Insert cell
Insert cell
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