In this post I'm going to show how to make scatter plots with team logos

In [10]:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import NBAapi as nba
from scipy import misc
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
import matplotlib.gridspec as gridspec
from PIL import Image
import StringIO
from cairosvg import svg2png

%matplotlib inline

The NBA team logos can be found on the NBA website. For example: http://stats.nba.com/media/img/teams/logos/season/2016-17/GSW_logo.svg To get all the logos we need to specify the season and the team abbreviation. To make it easier I added team info under nba.team.team_list(). The infromation is the full team name, abbreviation,team color, team id and conference. We can therefore merge any team stats with the team info to get the team abbreviation.

Note: if the sesaon is from 2002 or earlier, CHA needs to be changed to CHH

Plot logos:

Let's plot all of the NBA team logos from the 2016-17 season. I'm loading the logos from the nba website. You can play with the season to see how it works but note that before 2002 you'll have to change CHA to CHH in the team's abbreviation.

In [11]:
season = '2016-17'
teams = nba.team.team_list() # get team info
team_stats = nba.team.stats(season=season) # get team stats to see which teams played in the 2016-17 season
teams_wABB = team_stats.merge(teams,on='TEAM_NAME',how='left') # merge the DataFrames

names = teams_wABB['ABBREVIATION'].values # get list of teams abbreviations

# create the url parts that do not change
base_url = r'http://stats.nba.com/media/img/teams/logos/season/'
season = season
url_end = r'_logo.svg'

# use gridspec to create the subplots
plt.figure(figsize=(30,30))
gs1 = gridspec.GridSpec(6, 5)
gs1.update(wspace=0.01, hspace=0.01) # set the spacing between axes. 

for i,name in enumerate(names):
    ax1 = plt.subplot(gs1[i])
    I = svg2png(url=base_url + season + '//' +name + url_end) # convert svg to png strin
    Im = Image.open(StringIO.StringIO(I)) # convert png string to an image format
    ax1.imshow(Im) # plot image
    plt.gca().axis('off')
    

Now let's load some more interestign data!

Advanced team stats:

In [12]:
team_stats1 = nba.team.stats(season='2017-18',measuretype='Advanced') # get this year stats
team_stats2 = nba.team.stats(season='2016-17',measuretype='Advanced') # last year stats

# merge the DataFrames and add _2017 suffixes to the 2017 data
team_stats = team_stats1.merge(team_stats2,on='TEAM_ID',how = 'left',suffixes=('', '_2017')) 

# merge with team list to get the abbreviation, color and conference data
team_stats_w_names = team_stats.merge(teams,on='TEAM_NAME',how='left')

# get relevant data
conf = team_stats_w_names['conference'].values
DEF = team_stats_w_names['DEF_RATING'].values
OFF = team_stats_w_names['OFF_RATING'].values
DEF2 = team_stats_w_names['DEF_RATING_2017'].values
OFF2 = team_stats_w_names['OFF_RATING_2017'].values
names = team_stats_w_names['ABBREVIATION'].values
colors = team_stats_w_names['color'].values

Plotting the data:

I want to make a scatter plot but with the team logos instead of the typical plot. I found this great code on stack overflow: https://stackoverflow.com/questions/22566284/matplotlib-how-to-plot-images-instead-of-points I made slight changes but the bulk of the code is the same.

I'm going to write it as a function since I'm going to use it a few times. Also, I uploaded the 2016-17 team logos to the NBAapi package into the data folder which can be accessed with nba.DATA_PATH. I'm going to upload the images from the package but if a different year is used you might want to load the images from the web like the above example.

In [13]:
def scatter_plot_with_logos(X,Y,X2,Y2,names,colors):   
    artists = []
    f = nba.DATA_PATH
    for d,o,d2,o2,name,c in zip(X,Y,X2,Y2,names,colors):
        plt.plot(d2,o2,'o',color=c,markersize=16)
        plt.plot([d,d2],[o,o2],'--',color=c)
        im = misc.imread(f+name+'.png')
        im2 = OffsetImage(im, zoom=0.15,alpha=0.8)
        ab = AnnotationBbox(im2, (d, o), xycoords='data', frameon=False)
        artists.append(ax.add_artist(ab))
In [14]:
fig, ax = plt.subplots(figsize=(17,12))

# call function
scatter_plot_with_logos(DEF,OFF,DEF2,OFF2,names,colors)

# create labels and title 
plt.xticks(size=20)
plt.yticks(size=20)
plt.ylabel(r'OFFENSIVE RATING',fontsize=20)
plt.xlabel(r'DEFENSIVE RATING',fontsize=20)
plt.ylim([93,118]);
plt.xlim([114,95]);
plt.title('HOW ARE TEAMS DOING COMPARED TO LAST YEAR',fontsize=24)
plt.text(98.0,94,'By: DoingTheDishes\nSource: NBA API\nDate: 11.8.17',color='black',fontsize=12,weight = 'bold')
plt.text(112.5,115,'Circles: 2016-17\nLogos:  2017-18',fontsize=20,style='italic')
Out[14]:
<matplotlib.text.Text at 0xc293f28>

This figure might be a little too busy for some people.

Let's plot it per conference.

East

In [15]:
# create index for eastern conference
east_conf = conf=='Eastern'
In [16]:
fig, ax = plt.subplots(figsize=(17,12))

# run the same function but add conference index to parameters
scatter_plot_with_logos(DEF[east_conf],OFF[east_conf],DEF2[east_conf],OFF2[east_conf],names[east_conf],colors[east_conf])

# create labels and title 
plt.xticks(size=20)
plt.yticks(size=20)
plt.ylabel(r'OFFENSIVE RATING',fontsize=20)
plt.xlabel(r'DEFENSIVE RATING',fontsize=20)
plt.ylim([93,118]);
plt.xlim([114,95]);
plt.title('HOW ARE TEAMS DOING COMPARED TO LAST YEAR - EAST',fontsize=24)
plt.text(98.0,94,'By: DoingTheDishes\nSource: NBA API\nDate: 11.8.17',color='black',fontsize=12,weight = 'bold')
plt.text(112.5,115,'Circles: 2016-17\nLogos:  2017-18',fontsize=20,style='italic')
Out[16]:
<matplotlib.text.Text at 0xe4aec50>

Run again but with index set to not eastern (i.e. western).

West:

In [17]:
fig, ax = plt.subplots(figsize=(17,12))

# run the same function but add conference index to parameters
scatter_plot_with_logos(DEF[~east_conf],OFF[~east_conf],DEF2[~east_conf],OFF2[~east_conf],names[~east_conf],colors[~east_conf])

# create labels and title 
plt.xticks(size=20)
plt.yticks(size=20)
plt.ylabel(r'OFFENSIVE RATING',fontsize=20)
plt.xlabel(r'DEFENSIVE RATING',fontsize=20)
plt.ylim([93,118]);
plt.xlim([114,95]);
plt.title('HOW ARE TEAMS DOING COMPARED TO LAST YEAR - WEST',fontsize=24)
plt.text(98.0,94,'By: DoingTheDishes\nSource: NBA API\nDate: 11.8.17',color='black',fontsize=12,weight = 'bold')
plt.text(112.5,115,'Circles: 2016-17\nLogos:  2017-18',fontsize=20,style='italic')
Out[17]:
<matplotlib.text.Text at 0xe392160>

Edit:

I posted this on Reddit and got some good feedback. One important comment by ClosetDoorEnthusiast is that it is hard to tell how much the net rating changed overall since the x and y axis are not to scale.

Let's plot the figure for all the team but this time with similar scaling for x and y. Notice that this will make the graph much longer and it might look a little weird but it adds the benefit of intuitively being able to compare the net change in defensive and offensive ratings.

In [18]:
fig, ax = plt.subplots(figsize=(19,25))

# call function
scatter_plot_with_logos(DEF,OFF,DEF2,OFF2,names,colors)

# create labels and title
plt.axis('equal')
plt.xticks(size=20)
plt.yticks(size=20)
plt.ylabel(r'OFFENSIVE RATING',fontsize=20)
plt.xlabel(r'DEFENSIVE RATING',fontsize=20)
plt.ylim([93,118]);
plt.xlim([114,95]);
plt.title('HOW ARE TEAMS DOING COMPARED TO LAST YEAR',fontsize=24)
plt.text(98.0,94,'By: DoingTheDishes\nSource: NBA API\nDate: 11.9.17',color='black',fontsize=12,weight = 'bold')
plt.text(112.5,115,'Circles: 2016-17\nLogos:  2017-18',fontsize=20,style='italic')
Out[18]:
<matplotlib.text.Text at 0x1016cdd8>

And now it is easier to tell, for example, that the Lakers net gain on defense is offset by a net lose on offense.

Let me know what you think!



Comments

comments powered by Disqus