In this notebook, I'll go through how to create GIFs in Python. We'll be using the Shot Charts for Stephen Curry of the Golden State Warriors as our sequence of images.
Credits¶
- Special thanks goes to go Savvast Jortjoglou for making an ipynb notebook that shows you how to make nba shot charts in python, and for making a python module that shows you how to access the data and plot it. In this notebook, we'll be using this module to draw our courts.
# load the packages we'll be using
%matplotlib inline
import matplotlib.pyplot as plt
import requests
import pandas as pd
import seaborn as sns
import sys
Use the below url to access the stats.nba.com API to get the shot details of Stephen Curry for the 2014-2015 season. Steph Curry's player_id is 201939. To get a comphrehensive list of player id's, you can use the goldsberry package. This is the we use to access the API for shot coordinates and details.
player_id = 201939
shot_url = 'http://stats.nba.com/stats/shotchartdetail?CFID=33&CFPAR'\
'AMS=2014-15&ContextFilter=&ContextMeasure=FGA&DateFrom=&D'\
'ateTo=&GameID=&GameSegment=&LastNGames=0&LeagueID=00&Loca'\
'tion=&MeasureType=Base&Month=0&OpponentTeamID=0&Outcome=&'\
'PaceAdjust=N&PerMode=PerGame&Period=0&PlayerID=%s&Plu'\
'sMinus=N&Position=&Rank=N&RookieYear=&Season=2014-15&Seas'\
'onSegment=&SeasonType=Regular+Season&TeamID=0&VsConferenc'\
'e=&VsDivision=&mode=Advanced&showDetails=0&showShots=1&sh'\
'owZones=0' % player_id
Request the data from the stats.nba.com API.
response = requests.get(shot_url)
headers = response.json()['resultSets'][0]['headers']
rows = response.json()['resultSets'][0]['rowSet']
Load the data into a pandas dataframe and show the first couple of lines of data.
shots = pd.DataFrame(rows, columns=headers)
shots.head()
Load the nbaShotCharts module so that we can plot our shot charts.
import sys
sys.path.append('../../../nbaShotCharts/')
from nbaShotCharts import draw_court
For the shot chart get, a picture of Steph Curry. I got mines from here.
player_pic = plt.imread("../../../steph_curry/201939.png")
plt.imshow(player_pic)
plt.show()
Define a function to plot shot charts with the player's image. We want a function because we'll be creating multiple charts that we'll create in our GIF.
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
def shot_chart_plot(df):
plt.figure(figsize=(12,11))
cmap=plt.cm.Purples
shot_chart = sns.kdeplot(df.LOC_X, df.LOC_Y, shade=True, ax=draw_court(), cmap=cmap, legend=True)
shot_chart.set_xlim(-250,250)
shot_chart.set_ylim(422.5, -47.5)
shot_chart.set_xlabel('')
shot_chart.set_ylabel('')
shot_chart.tick_params(labelbottom='off', labelleft='off')
# make a title
shot_chart.set_title('Steph Curry Field Goal Attempts \n2014-15 Reg. Season', fontsize=18)
# read the image
curry_pic = plt.imread("../../../steph_curry/201939.png")
# load a scaled down version of the image
img1 = OffsetImage(curry_pic, zoom=0.45)
# place the image on the plot. I played around with the xy coordinates till I got something that looked half decent
img1.set_offset((0, 300))
# add the image
shot_chart.add_artist(img1)
# add in a scale
scale = plt.imread("../../../steph_curry/scale.png")
img2 = OffsetImage(scale, zoom=.5, interpolation="nearest")
img2.set_offset((-5, 100))
shot_chart.add_artist(img2)
shot_chart_plot(shots)
With our shot chart ready, we can start to make the multiple images for our GIF. We're going to make multiple images by time elapsed. First, we want to create a new MINUTES variable that uses both PERIOD AND MINUTES_REMAINING. This way our new variable MINUTES will be from 1-48.
shots['MINUTES'] = shots['PERIOD'] * (12 - shots['MINUTES_REMAINING'])
shots['MINUTES'].value_counts()
Notice that the minutes are pretty spread out in terms of uniformity, with the majority of Curry's shots coming in minutes 12 and 14, or the end of the 1st and quarters, respectively. For that reason, we'll split our minutes variable according to a value range, grouping every 6 minutes together.
shots['MINUTES_G'] = pd.cut(shots['MINUTES'],
bins=[1,6,12,18,24,30,36,42,48])
# drop if empty
shots = shots.dropna(how='any')
shots['MINUTES_G'].value_counts()
Now that we have the minutes grouped together, we can start to make shot chart images by those minute groupings.
# build a list of unique group values
minutes = sorted(set(shots.MINUTES_G.values))
chart_list = []
# make a shot chart for each minutes grouping
i = 0
for min in minutes:
df = shots[shots['MINUTES_G'] == min]
plot = shot_chart_plot(df)
# convert back to 1-48 min
#minute = (int(min) + 1) * 6
plt.text(-45, 460, "%s Min" % min, fontsize=25)
plt.savefig("../../../steph_curry/curry_%s" % i)
chart_list.append("curry_%s.png" % i)
i += 1
Awesome! We now have our shot chart files. The next step is to combine these files together to make our GIF.
chart_list
Alrighty, let's make our gifs. We'll be using the images2gif package and the the PIL (Python Imaging Library) to do this.
from images2gif import writeGif
from PIL import Image
import os
images = [Image.open("../../../steph_curry/" + fn) for fn in chart_list]
writeGif("../../../steph_curry/shots.gif", images, duration=8)
Great, now we now have our GIF!
import IPython.display as IPdisplay
IPdisplay.Image(url="../../../steph_curry/shots.gif")