Average annual attendance for all NBA team 2001-2016. Hover to see individual teams attendance.
Note the Wizards big uptick in the 2001-2003 years due to MJ. The impact of Lebron is clear when you look at attendance patterns for both the Cavaliers (2003-2010, 2014-present) and the Heat (2010-2014). The effects of the Great Recession (2007-2009) on attendance are also evident.
Data for 2016 is current as of Feb 21, 2016. Data source: ESPN.com
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
body {
font: 12px sans-serif;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
stroke-width: 1px;
}
.line {
fill: none;
stroke: #000;
stroke-width: 1px;
opacity: 0.1;
-webkit-transition: stroke-width 0.2s, opacity 0.2s;
-ms-transition: stroke-width 0.2s, opacity 0.2s;
transition: stroke-width 0.2s, opacity 0.2s;
}
.line.hovered {
stroke-width: 2px;
opacity: 1;
}
.polygon {
fill-opacity: 0;
stroke-opacity: 0;
}
.marker {
fill: none;
stroke: #ff0000;
stroke-width: 1px;
}
.team-name {
opacity: 0.05;
-webkit-transition: opacity 0.2s;
-ms-transition: opacity 0.2s;
transition: opacity 0.2s;
}
.team-name.hovered {
opacity: 1;
}
</style>
</head>
<body>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var margin = { top: 10, left: 75, bottom: 30, right: 75 },
width = 960 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var scale = {
x: d3.scale.linear().range([0, width]),
y: d3.scale.linear().range([height, 0])
};
var access = {
x: function(d) { return d.year; },
y: function(d) { return d.avg; }
};
var value = {
x: function(d) { return scale.x(access.x(d)); },
y: function(d) { return scale.y(access.y(d)); }
};
var axis = {
x: d3.svg.axis().scale(scale.x).orient("bottom")
.tickFormat(d3.format(""))
.ticks(15),
y: d3.svg.axis().scale(scale.y).orient("left")
};
var line = d3.svg.line()
.x(value.x)
.y(value.y)
.interpolate("monotone");
var voronoi = d3.geom.voronoi()
.clipExtent([[0, 0], [width, height]])
.x(value.x)
.y(value.y);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xMarker = svg.append("circle")
.attr("class", "x marker")
.attr("r", 0);
var yMarker = svg.append("circle")
.attr("class", "y marker")
.attr("r", 0);
d3.json("data.json", function(error, data){
if (error) throw error;
data.forEach(function(d) {
d.avg = +(d.avg.replace(",", ""));
d.year = +d.year;
});
var minimum = {
x: d3.min(data, access.x),
y: d3.min(data, access.y)
};
scale.x.domain(d3.extent(data, access.x));
scale.y.domain(d3.extent(data, access.y));
groupedByTeam = d3.nest()
.key(function(d) { return d.team; })
.entries(data);
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(axis.x);
svg.append("g").attr("class", "y axis")
.call(axis.y)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style("text-shadow", "1px 1px #fff")
.text("Attendance");
var lines = svg.selectAll(".line")
.data(groupedByTeam)
.enter().append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); });
var teamNames = svg.append("g").selectAll(".team-name")
.data(data.filter(function(d) { return d.year === 2016; }))
.enter().append("text")
.attr("class", "team-name")
.attr("x", value.x)
.attr("y", value.y)
.attr("dx", 3)
.attr("dy", ".32em")
.text(function(d) { return d.team; });
var polygons = svg.selectAll(".polygon")
.data(voronoi(data));
polygons.enter().append("path")
.attr("class", "polygon")
.attr("d", function(d) {
if (d !== undefined)
return "M" + d.join("L") + "Z";
})
.on("mouseenter", function(d) {
var team = d.point.team;
lines.classed("hovered", function(d) { return d.key === team; });
xMarker
.attr("r", 3)
.attr("cx", scale.x(d.point.year))
.attr("cy", scale.y(minimum.y));
yMarker
.attr("r", 3)
.attr("cx", scale.x(minimum.x))
.attr("cy", scale.y(d.point.avg));
teamNames
.classed("hovered", function(d) { return d.team === team; });
})
.on("mouseleave", function() {
lines.classed("hovered", false)
xMarker.attr("r", 0)
yMarker.attr("r", 0)
teamNames.classed("hovered", false);
});
});
</script>
</body>
</html>
var fs = require("fs");
var cheerio = require("cheerio");
var d3 = {
request: require("d3-request").request,
queue: require("d3-queue").queue
};
var queue = d3.queue();
for (var year = 2001; year <= 2016; year++) {
queue.defer(getAttendance, year);
}
queue.awaitAll(function(error, results) {
if (error) throw error;
var data = results.reduce(function(a, b) { return a.concat(b); });
fs.writeFile("data.json", JSON.stringify(data), function(error) {
if (error) throw error;
console.log("Data saved.");
})
});
function getAttendance(year, callback) {
var column_titles = [
"rank", "team",
"home_games", "home_total", "home_avg", "home_pct",
"road_games", "road_avg", "road_pct",
"games", "avg", "pct"
];
var url = "http://espn.go.com/nba/attendance/_/year/" + year;
var data = [];
d3.request(url, function(error, response) {
if (error) callback(error);
var $ = cheerio.load(response.responseText);
var container = $("#my-teams-table");
var rows = container
.find("tr")
.filter(function() {
return !$(this).attr("class").endsWith("head");
});
rows.each(function(i) {
data[i] = {};
$(this).find("td")
.each(function(j) {
var title = column_titles[j];
data[i][title] = $(this).text();
});
});
data.forEach(function(d) { d.year = year; });
callback(null, data);
});
}