WNBA Shot Chart
Every field goal attempt in the 2023 WNBA regular season. Shots are grouped into hexagonal bins, with color indicating shot potency (average score) and size indicating the total count of shots per location. The menu filters isolate shots by team or athlete.
Loading Example... ⏳
Credit: Data from Wehoop. Design inspired by Kirk Goldsberry and a UW CSE 512 project by Mackenzie Pitts and Madeline Brown.
Specification
js
import * as vg from "@uwdata/vgplot";
await vg.coordinator().exec([
vg.loadParquet("shots", "data/wnba-shots-2023.parquet", {where: "NOT starts_with(type, 'Free Throw') AND season_type = 2"}),
vg.loadParquet("court", "data/wnba-half-court.parquet")
]);
const $filter = vg.Selection.crossfilter();
const $binWidth = vg.Param.value(18);
export default vg.vconcat(
vg.hconcat(
vg.menu({from: "shots", column: "team_name", as: $filter, label: "Team"}),
vg.menu({
from: "shots",
column: "athlete_name",
filterBy: $filter,
as: $filter,
label: "Athlete"
})
),
vg.vspace(5),
vg.plot(
vg.frame({strokeOpacity: 0.5}),
vg.hexgrid({binWidth: $binWidth, strokeOpacity: 0.05}),
vg.hexbin(
vg.from("shots", {filterBy: $filter}),
{
binWidth: $binWidth,
x: "x_position",
y: "y_position",
fill: vg.avg("score_value"),
r: vg.count(),
tip: {format: {x: false, y: false}}
}
),
vg.line(
vg.from("court"),
{strokeLinecap: "butt", strokeOpacity: 0.5, x: "x", y: "y", z: "z"}
),
vg.name("shot-chart"),
vg.xAxis(null),
vg.yAxis(null),
vg.margin(5),
vg.xDomain([0, 50]),
vg.yDomain([0, 40]),
vg.colorDomain(vg.Fixed),
vg.colorScheme("YlOrRd"),
vg.colorScale("linear"),
vg.colorLabel("Avg. Shot Value"),
vg.rScale("log"),
vg.rRange([3, 9]),
vg.rLabel("Shot Count"),
vg.aspectRatio(1),
vg.width(510)
),
vg.colorLegend({for: "shot-chart"})
);
yaml
meta:
title: WNBA Shot Chart
description: >
Every field goal attempt in the 2023 WNBA regular season.
Shots are grouped into hexagonal bins, with color indicating shot potency
(average score) and size indicating the total count of shots per location.
The menu filters isolate shots by team or athlete.
credit: >
Data from [Wehoop](https://wehoop.sportsdataverse.org/). Design inspired by
[Kirk Goldsberry](https://en.wikipedia.org/wiki/Kirk_Goldsberry) and a
[UW CSE 512](https://courses.cs.washington.edu/courses/cse512/24sp/)
project by Mackenzie Pitts and Madeline Brown.
data:
shots:
file: data/wnba-shots-2023.parquet
where: NOT starts_with(type, 'Free Throw') AND season_type = 2
court:
file: data/wnba-half-court.parquet
params:
filter: { select: crossfilter }
binWidth: 18
vconcat:
- hconcat:
- input: menu
from: shots
column: team_name
as: $filter
label: Team
- input: menu
from: shots
column: athlete_name
filterBy: $filter
as: $filter
label: Athlete
- vspace: 5
- plot:
- mark: frame
strokeOpacity: 0.5
- mark: hexgrid
binWidth: $binWidth
strokeOpacity: 0.05
- mark: hexbin
data: { from: shots, filterBy: $filter }
binWidth: $binWidth
x: x_position
y: y_position
fill: { avg: score_value }
r: { count: }
tip: { format: { x: false, y: false } }
- mark: line
data: { from: court }
strokeLinecap: butt
strokeOpacity: 0.5
x: x
y: y
z: z
name: shot-chart
xAxis: null
yAxis: null
margin: 5
xDomain: [0, 50]
yDomain: [0, 40]
colorDomain: Fixed
colorScheme: YlOrRd
colorScale: linear
colorLabel: Avg. Shot Value
rScale: log
rRange: [3, 9]
rLabel: Shot Count
aspectRatio: 1
width: 510
- legend: color
for: shot-chart
json
{
"meta": {
"title": "WNBA Shot Chart",
"description": "Every field goal attempt in the 2023 WNBA regular season. Shots are grouped into hexagonal bins, with color indicating shot potency (average score) and size indicating the total count of shots per location. The menu filters isolate shots by team or athlete.\n",
"credit": "Data from [Wehoop](https://wehoop.sportsdataverse.org/). Design inspired by [Kirk Goldsberry](https://en.wikipedia.org/wiki/Kirk_Goldsberry) and a [UW CSE 512](https://courses.cs.washington.edu/courses/cse512/24sp/) project by Mackenzie Pitts and Madeline Brown.\n"
},
"data": {
"shots": {
"file": "data/wnba-shots-2023.parquet",
"where": "NOT starts_with(type, 'Free Throw') AND season_type = 2"
},
"court": {
"file": "data/wnba-half-court.parquet"
}
},
"params": {
"filter": {
"select": "crossfilter"
},
"binWidth": 18
},
"vconcat": [
{
"hconcat": [
{
"input": "menu",
"from": "shots",
"column": "team_name",
"as": "$filter",
"label": "Team"
},
{
"input": "menu",
"from": "shots",
"column": "athlete_name",
"filterBy": "$filter",
"as": "$filter",
"label": "Athlete"
}
]
},
{
"vspace": 5
},
{
"plot": [
{
"mark": "frame",
"strokeOpacity": 0.5
},
{
"mark": "hexgrid",
"binWidth": "$binWidth",
"strokeOpacity": 0.05
},
{
"mark": "hexbin",
"data": {
"from": "shots",
"filterBy": "$filter"
},
"binWidth": "$binWidth",
"x": "x_position",
"y": "y_position",
"fill": {
"avg": "score_value"
},
"r": {
"count": null
},
"tip": {
"format": {
"x": false,
"y": false
}
}
},
{
"mark": "line",
"data": {
"from": "court"
},
"strokeLinecap": "butt",
"strokeOpacity": 0.5,
"x": "x",
"y": "y",
"z": "z"
}
],
"name": "shot-chart",
"xAxis": null,
"yAxis": null,
"margin": 5,
"xDomain": [
0,
50
],
"yDomain": [
0,
40
],
"colorDomain": "Fixed",
"colorScheme": "YlOrRd",
"colorScale": "linear",
"colorLabel": "Avg. Shot Value",
"rScale": "log",
"rRange": [
3,
9
],
"rLabel": "Shot Count",
"aspectRatio": 1,
"width": 510
},
{
"legend": "color",
"for": "shot-chart"
}
]
}