Published
Edited
Nov 15, 2020
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tooltipsvg = tooltip.append("svg").attr("id","tooltipContent")
Insert cell
barSVG = tooltipsvg.insert("svg","#zero-line")
Insert cell
headerSVG = tooltipsvg.append("g")
.attr("transform",`translate(${tooltipWidth/2},23)`)
.append("text")
Insert cell
{
tooltipsvg
.append("line").attr("id", "zero-line")
.attr("x1",xScale(0))
.attr("x2",xScale(0))
.attr("y1",margin.top)
.attr("y2",tooltipHeight-margin.bottom)
.attr("stroke","#B6C0C2")
.attr("stroke-width",1)
}
Insert cell
xScale = d3.scaleLinear()
.domain([d3.min(flatRawData_top5, d=>d.sg), d3.max(flatRawData,d => d.sg)])
.range([margin.left, tooltipWidth - margin.right])
Insert cell
xScale(0)
Insert cell
tooltipHeader = {
headerSVG.append("tspan")
.attr("id","player-name")
.text("player name")
.style("font-size",15)
.attr("text-anchor","middle")
.attr("fill","grey")
.attr("x",0);

headerSVG.append("tspan")
.attr("text-anchor","middle")
.text("Absolute Strokes Gained by shot type")
.style("font-size",12)
.attr("dy","1.5em")
.attr("fill","#B6C0C2")
.attr("x",0);
headerSVG.append("tspan")
.text("▊")
.attr("fill","#e6e6e6")
.attr("opacity",0.7)
.attr("x",0)
.attr("dy","1.2em")
.style("font-size",12)
.attr("fill","#e6e6e6")
.attr("opacity",0.75)
.attr("text-anchor","middle")

headerSVG.append("tspan")
.text(" = highest value in dataset")
.style("font-size",11)
.attr("fill","#B6C0C2")
}
Insert cell
barTooltip = function (player, season) {

let PlayerData = flatRawData.filter(d => d.player == player & d.year == season)
var colScale = d3.scaleOrdinal()
.domain(PlayerData.map(d => d.stat))
.range(["#EBD0B3","#c1d0cb","#AED8E6","#AED8E6"])
//For best in dataset context, only consider players who have played >=7 tourns in a season
let statRangeData = flatRawData.filter(d => d.tourn_count >= 7)
//Find the min/max SG's in the dataset across the categories
let statRange = d3Array.rollup(statRangeData, v => [d3.min(v, d => d.sg),d3.max(v, d => d.sg)], d => d.stat)
let statRangeFlat = unroll(statRange,['stat','sg'],"sg")
// let xScale = d3.scaleLinear()
// .domain([d3.min(flatRawData_top5, d=>d.sg), d3.max(flatRawData,d => d.sg)])
// .range([margin.left, tooltipWidth - margin.right])
//Dynamic tooltip header
headerSVG
.select("#player-name")
.text(`${player} | ${season}`)
//Plot best in dataset context bars
barSVG
.selectAll("#rangeBars")
.data(statRangeFlat)
.join("rect").attr("id","rangeBars")
.attr("x", xScale(0))
.attr("y", (d, i) => yScale(i))
.attr("width", d => xScale(d.sg[1]) - xScale(0))
.attr("height", yScale.bandwidth())
.attr("fill", "#e6e6e6")
.attr("opacity",0.6)
//Plot selected SG bars
barSVG
.selectAll("#bars")
.data(PlayerData)
.join("rect").attr("id","bars")
.attr("x", d => d.sg > 0 ? xScale(0) : xScale(d.sg))
.attr("y", (d, i) => yScale(i))
.attr("width", d => d.sg > 0 ? xScale(d.sg) - xScale(0) : xScale(0) - xScale(d.sg))
.attr("height", yScale.bandwidth())
.attr("fill", d => colScale(d.stat))
.attr("opacity",0.8)
barSVG
//.append("g")
.selectAll("#sg-labels")
.data(PlayerData)
.join("text").attr("id","sg-labels")
.attr("x", d => xScale(d.sg))
.attr("y", (d, i) => yScale(i) + yScale.bandwidth()/2)
.text(d=> Math.round(d.sg*10)/10)
.attr("font-size",15)
.attr("text-anchor", d => d.sg >= 0 ? "start" : "end")
.attr("dy", "0.3em")
.attr("dx",d => d.sg >= 0 ? 4 : -4)
.attr("font-weight","bold")
.attr("fill",d=> d3.hsl(colScale(d.stat)).darker(0.1))

return tooltipsvg.node();
}
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(yScale).tickFormat(i => flatRawData[i].stat).tickSizeOuter(0))
Insert cell
margin = ({top: 67, right: 30, bottom: 15, left: 30})
Insert cell
tooltipHeight = 140
Insert cell
tooltipWidth =230
Insert cell
yScale = d3.scaleBand()
.domain(d3.range(3))
.range([margin.top, tooltipHeight - margin.bottom])
.padding(0.1)
Insert cell
Insert cell
flatData = flattenData(top5_SG)
Insert cell
flatRawData = flattenData(sgRaw)
Insert cell
flatRawData_top5 = flattenData(top5_SG_raw);
Insert cell
height = 640
Insert cell
sg = await FileAttachment("sg-utf8@1.csv").csv()
Insert cell
sg2 = await FileAttachment("sg-utf8@1.csv").csv()
Insert cell
fieldSG = await FileAttachment("Masters 20 sg@5.csv").csv()
Insert cell
fieldSG2 = await FileAttachment("Masters 20 sg@5.csv").csv()
Insert cell
sgWithField = [...sg,...fieldSG]
Insert cell
sgWithField.filter(d=>d.player == "Bryson DeChambeau")
Insert cell
sgWithField2 = [...sg2,...fieldSG2]
Insert cell
sgWithField.filter(d => d.year == "2020/21")
Insert cell
sgNorm = {
const data = sgWithField.filter(d => +d.tourn_count >= 5)

data.forEach(function(d){
d.OTT = Math.max(+d.OTT,0);
d.APP = Math.max(+d.APP,0);
d.ARG = +d.ARG;
d.PUTT = +d.PUTT;
d.SHORT = Math.max(d.ARG + d.PUTT,0);
d.totalSG = d.OTT + d.APP + d.SHORT;
d.OTT_Pct = d.OTT/d.totalSG;
d.APP_Pct = d.APP/d.totalSG;
d.SHORT_Pct = d.SHORT/d.totalSG;
})
return data;
}
Insert cell
sgRaw = {
let data = sgWithField2.filter(d => +d.tourn_count >= 5)
data.forEach(function(d){
d.OTT = +d.OTT;
d.APP = +d.APP;
d.SHORT = +d.ARG + +d.PUTT;
})
return data;
}
Insert cell
sgNorm.filter(d => d.year == "2020/21")
Insert cell
top5 = await FileAttachment("top5@3.csv").csv()
Insert cell
mastersTop5 = top5.filter(d => ['Masters Tournament'].includes(d.tourn_names) & d.rank <=3)
Insert cell
//Only return data for top5 Masters finishers
top5_SG = {

let data = []

sgNorm.forEach(function(d){
for (let i=0; i<mastersTop5.length; i++){
if (mastersTop5[i].player == d.player & mastersTop5[i].year == d.year) {
d.rank = mastersTop5[i].rank;
data.push(d)
}
}
});
return data
}
Insert cell
top5_SG_raw = {
let data = [];
//Only return data for top5 Masters finishers
sgRaw.forEach(function(d){
for (let i=0; i<mastersTop5.length; i++){
if (mastersTop5[i].player == d.player & mastersTop5[i].year == d.year) {
d.rank = mastersTop5[i].rank;
data.push(d)
}
}
});
return data
}

Insert cell
fieldNorm = sgNorm.filter(d => d.year == "2020/21")
Insert cell
top5_field_norm = [...top5_SG,...fieldNorm]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3_svg_annotation = require("d3-svg-annotation")
Insert cell
d3Array = require("d3-array@2")
Insert cell
import {checkbox} from "@jashkenas/inputs"
Insert cell
_ = require('lodash')
Insert cell
import {form} from "@mbostock/form-input"
Insert cell
d3 = require('d3@5')
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