Public
Edited
Nov 14, 2023
Insert cell
Insert cell
chart = Plot.plot({
height: 800,
width: 1200,
marginLeft: 65,
facet:{
data: shots, x: "team_name", y: "event_type", label: ""
},
//axis: null,
x: {domain: [-250, 250], axis: null},
y: {domain: [-50, 450], axis: null},
marks: [
//Plot.rect(shots, Plot.bin({fill: "count"}, {x: "loc_x", y: "loc_y", filter: d => +d.shot_made_flag, inset: 0, interval: 10})),
//Plot.gridX({interval: 10, strokeOpacity: 0.05}),
//Plot.gridY({interval: 10, strokeOpacity: 0.05}),
Plot.dot(shots, {
x: "loc_x",
y: "loc_y",
r: 2,
stroke: "event_type",
strokeOpacity: 1,
strokeWidth: 0.25,
symbol: 'plus'
}),
Plot.density(shots, {
x: "loc_x",
y: "loc_y",
bandwidth: 30,
thresholds: 20,
stroke: "event_type",
strokeOpacity: 0.4,
fill: "event_type",
fillOpacity: 0.1,
mixBlendMode: "multiply",
weight: 1
}),
markings()
]
})
Insert cell
Insert cell
Plot.plot({
x:{
label: "Shots Made"
},
marginLeft: 50,
marks: [
Plot.barX(shots_df, Plot.groupY({x: "count"}, {y: "opponent",
sort: {y: "x", reverse: true, limit: 10},
filter: d => +d.shot_made_flag,
fill: "#552583"})),
Plot.ruleX([0])
]
})
Insert cell
Plot.plot({
x:{
type: "point"
},
marks:[
Plot.line(distance_summary, {x: "season", y: "average_attempt_distance"})
]
})
Insert cell
distance_summary = shots_df
.groupby("season")
.rollup({
average_attempt_distance: d => op.mean(d.shot_distance)
})
Insert cell
shots = FileAttachment("lebron_shots.csv").csv()
Insert cell
shots_df = aq.from(shots)
.derive({
team_abv: aq.escape(d => teamNameToAbbreviation(d.team_name))
})
.derive({
opponent: aq.escape( d => getOpponent(d.team_abv, d.htm, d.vtm))
})
Insert cell
Insert cell
function getOpponent(abv, htm, vtm){
if(abv == htm) {
return vtm }
else return htm
}
Insert cell
function teamNameToAbbreviation(team){
if(team == "Los Angeles Lakers"){
return "LAL"
} else if(team == "Cleveland Cavaliers"){
return "CLE"
}
else return "MIA";
}
Insert cell
Insert cell
function markings({
stroke = "currentColor",
strokeWidth = 1,
strokeOpacity = 1
} = {}) {
// Ref. https://observablehq.com/@nor/nba-2018-19-shooting-effeciency
const angle = Math.atan(90 / 220);
const arc = d3.arc();
const lines = [
[-250, 420, 250, 420], // half
[-250, 450, -250, -50], // left
[250, 450, 250, -50], // right
[250, -50, -250, -50], // bottom
[-220, -50, -220, 90], // corner 3
[220, -50, 220, 90], // corner 3
[-80, -50, -80, 140], // paint
[80, -50, 80, 140], // paint
[-60, -50, -60, 140], // paint
[60, -50, 60, 140], // paint
[-80, 140, 80, 140], // free throw line
[-30, -10, 30, -10], // backboard
[0, -10, 0, -7.5], // rim
[-40, -10, -40, 0], // ra
[40, -10, 40, 0] // ra
];
const circles = [
[0, 0, 7.5], // rim
[0, 140, 60], // key
[0, 420, 20], // center court inner
[0, 420, 60] // center court outer
];
const arcs = [
[0, 0, 40, -Math.PI * 0.5, Math.PI * 0.5], // ra
[0, 0, 237.5, -Math.PI * 0.5 - angle, Math.PI * 0.5 + angle] // 3pt
];
return (index, {x, y}) => {
return htl.svg`<g fill=none stroke=${stroke} stroke-width=${strokeWidth} stroke-opacity=${strokeOpacity}>
${lines.map(([x1, y1, x2, y2]) => htl.svg`<line x1=${x(x1)} x2=${x(x2)} y1=${y(y1)} y2=${y(y2)}>`)}
${circles.map(([cx, cy, r]) => htl.svg`<ellipse cx=${x(cx)} cy=${y(cy)} rx=${Math.abs(x(r) - x(0))} ry=${Math.abs(y(r) - y(0))}>`)}
${arcs.map(([cx, cy, r, a1, a2]) => htl.svg`<path d="M${x(cx + r * Math.cos(a1 - Math.PI / 2))},${y(cy + r * Math.sin(a1 - Math.PI / 2))}A${Math.abs(x(r) - x(0))} ${Math.abs(y(r) - y(0))} 0 0 ${Math.sign(x(r) - x(0)) * Math.sign(y(r) - y(0)) > 0 ? 0 : 1} ${x(cx + r * Math.cos(a2 - Math.PI / 2))},${y(cy + r * Math.sin(a2 - Math.PI / 2))}">`)}
</g>`;
};
}
Insert cell
shots
Type Table, then Shift-Enter. Ctrl-space for more options.

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