March 23, 2014

Graphing NBA conference rankings over time.

data

Bored by the relative lack of Eastern conference drama at this point in the season, I thought it would be a fun experiment to graph the relative rankings of NBA teams over the past few seasons.

Getting the data was surprisingly easy: major sports data is notoriously hard to retrieve, but there exists a (likely illegal) API that had exactly what I wanted, mapping dates to relative rankings across the conferences. Scraping the code with Python relatively painless as well.

import requests
import datetime
import logging
import time

# Takes a datetime in format of YYYYMMDD
base_url = "https://erikberg.com/nba/standings/{}.json"

outfile = open('nba2011.txt', 'w')

season_start = datetime.date(2011, 12, 25)
weeks = [season_start]
while weeks[-1] < datetime.date(2012, 4, 23):
    weeks.append(weeks[-1] + datetime.timedelta(weeks=1))
weeks = [week.isoformat().replace('-', '') for week in weeks]

team_ids = []
rankings = {"east": [], "west": []}

for week in weeks:
    query_url = base_url.format(week)
    data = requests.get(query_url).json().get('standing')
    east_data = {rank.get('team_id'): str(rank.get('rank')) for rank in data if rank.get('conference') == 'EAST'}
    west_data = {rank.get('team_id'): str(rank.get('rank')) for rank in data if rank.get('conference') == 'WEST'}
    rankings['east'].append(east_data)
    rankings['west'].append(west_data)
    if not team_ids:
        team_ids = {"east": east_data.keys(), "west": west_data.keys()}

    # To avoid hitting the rate limit.
    time.sleep(10)

for conference in ["east", "west"]:
    outfile.write("date" + "\t" + "\t".join(team_ids.get(conference)) + "\n")
    for ind in range(len(rankings.get(conference))):
            outfile.write(weeks[ind] + "\t" + "\t".join([rankings.get(conference)[ind].get(team) for team in team_ids.get(conference)]) + "\n")

I graphed this all up in D3 (view source if you're particularly intrigued), and while I was too lazy to get the actual lines to match with team colors I think the results are particularly cool. Check out the Western Conference:

A few things are evident:

  • The San Antonio Spurs are very very good at basketball
  • Most of the poorly-ranked teams seem to do a good job immediately plummeting to their level of performance -- with the exception of the Suns, who hit something like a sinusoidal curve as they approach theirs
  • We're only two years removed from a 23-43 Warriors team
  • No really, the Spurs are very good

And compare it to the East:

Man, the East has actually been exciting -- on paper, at least -- as long as you remove the constant victory/defeat of the Pacers, Heat, and Bucks. It's pretty impressive to see the Nets' steady rise into a playoff contender, and the fight for the five through eight seeds is particularly spirited 1.

And, of course, this doesn't get into the larger issue of disparity between the East and West. If we graph the win-loss differential over the course of the season, we see how the two teams stack up against each other (Western teams in blue, Eastern teams in orange):

all_teams = team_ids.get("west") + team_ids.get("east")
rankings["all"] = []
for ind in range(len(rankings.get("west"))):
    merged_dict = dict(rankings.get("west")[ind].items() + rankings.get("east")[ind].items())
    rankings["all"].append(merged_dict)
outfile.write("date" + "\t" + "\t".join(all_teams) + "\n")
for ind in range(len(rankings.get("all"))):
        outfile.write(weeks[ind] + "\t" + "\t".join([rankings.get("all")[ind].get(team) for team in all_teams]) + "\n")

So the worst teams in the East are really, really bad. Two years ago that distinction went to the Bobcats; last year it was shared by the Bobcats and the Magic; this year, its the Bucks and the Sixers. 2


  1. This is slightly buoyed by the fact that you don't necessarily need to have a winning record to be in the playoffs in the East. Whammy. 

  2. The latter of which Bill Simmons would argue would not even get past the Sweet 16

You should .

Comments