Skip to main content

Overview

The new_tracking.py script collects player tracking statistics from NBA.com’s tracking endpoints. It gathers data across 8 different tracking categories including drives, catch-and-shoot, passing, possessions/touches, and location-specific touches.

Data Sources

  • NBA.com Tracking API: leaguedashptstats endpoint
  • Tracking Categories:
    • Drives
    • Catch & Shoot
    • Passing
    • Possessions (touches)
    • Elbow Touches
    • Post Touches
    • Paint Touches
    • Pull-Up Shots

Core Function

get_tracking()

Collects tracking data for all categories across specified years.
years
list[int]
required
List of years to collect (e.g., [2024, 2025])
ps
boolean
default:"False"
Playoffs mode - if True, fetches playoff data
Returns: Dictionary mapping category names to lists of DataFrames (one per year)
# From new_tracking.py:10
def get_tracking(years, ps=False):
    stype = "Playoffs" if ps else "Regular%20Season"
    
    shots = ["Drives", "CatchShoot", "Passing", "Possessions", 
             "ElbowTouch", "PostTouch", "PaintTouch", "PullUpShot"]
    
    # Dictionary to store dataframes for each shot category
    category_frames = {shot: [] for shot in shots}
    
    for year in years:
        season = str(year) + '-' + str(year + 1 - 2000)
        
        for shot in shots:
            url = f"https://stats.nba.com/stats/leaguedashptstats?PtMeasureType={shot}&Season={season}&SeasonType={stype}"
            
            response = requests.get(url, headers=headers)
            if response.status_code == 200:
                json = response.json()
                data = json["resultSets"][0]["rowSet"]
                columns = json["resultSets"][0]["headers"]
                df = pd.DataFrame.from_records(data, columns=columns)
                df["Season"] = season
                df['year'] = year + 1
                category_frames[shot].append(df)

Tracking Categories

Drives

DRIVES
integer
Number of drives to the basket
DRIVE_FGM
integer
Field goals made on drives
DRIVE_FGA
integer
Field goal attempts on drives
DRIVE_FG_PCT
float
Field goal percentage on drives
DRIVE_FT_PCT
float
Free throw percentage on drive attempts
DRIVE_PTS
integer
Points scored on drives
DRIVE_PASSES
integer
Passes made during drives
DRIVE_AST
integer
Assists generated from drives
DRIVE_TOV
integer
Turnovers on drives
DRIVE_PF
integer
Personal fouls drawn on drives

Catch & Shoot

CATCH_SHOOT_FGM
integer
Catch-and-shoot field goals made
CATCH_SHOOT_FGA
integer
Catch-and-shoot field goal attempts
CATCH_SHOOT_FG_PCT
float
Catch-and-shoot field goal percentage
CATCH_SHOOT_PTS
integer
Points from catch-and-shoot situations
CATCH_SHOOT_FG3M
integer
Catch-and-shoot three-pointers made
CATCH_SHOOT_FG3A
integer
Catch-and-shoot three-point attempts
CATCH_SHOOT_FG3_PCT
float
Catch-and-shoot three-point percentage

Passing

PASSES_MADE
integer
Total passes made
PASSES_RECEIVED
integer
Total passes received
AST
integer
Assists
SECONDARY_AST
integer
Secondary assists (pass to the assister)
POTENTIAL_AST
integer
Potential assists (pass led to shot attempt)
AST_PTS_CREATED
integer
Points created from assists
AST_ADJ
float
Adjusted assists (accounts for shot difficulty)

Possessions (Touches)

TOUCHES
integer
Number of times player touched the ball
FRONT_CT_TOUCHES
integer
Touches in front court
TIME_OF_POSS
float
Time of possession (seconds)
AVG_SEC_PER_TOUCH
float
Average seconds per touch
AVG_DRIB_PER_TOUCH
float
Average dribbles per touch
PTS_PER_TOUCH
float
Points per touch
ELBOW_TOUCHES
integer
Touches at the elbow
POST_TOUCHES
integer
Touches in the post
PAINT_TOUCHES
integer
Touches in the paint

Pull-Up Shots

PULL_UP_FGM
integer
Pull-up field goals made
PULL_UP_FGA
integer
Pull-up field goal attempts
PULL_UP_FG_PCT
float
Pull-up field goal percentage
PULL_UP_PTS
integer
Points from pull-up shots
PULL_UP_FG3M
integer
Pull-up three-pointers made
PULL_UP_FG3A
integer
Pull-up three-point attempts

Output Files

Each tracking category has its own file:
drives.csv
CSV
Drive statisticsPath: tracking/drives.csv or tracking_ps/drives.csv
catchshoot.csv
CSV
Catch-and-shoot statisticsPath: tracking/catchshoot.csv
passing.csv
CSV
Passing and assist statisticsPath: tracking/passing.csv
touches.csv
CSV
Possession and touch statisticsPath: tracking/touches.csv
elbow.csv
CSV
Elbow touch statisticsPath: tracking/elbow.csv
post.csv
CSV
Post touch statisticsPath: tracking/post.csv
paint.csv
CSV
Paint touch statisticsPath: tracking/paint.csv
pullup.csv
CSV
Pull-up shot statisticsPath: tracking/pullup.csv

File Management

# From new_tracking.py:66-96
category_maps = {
    "Drives": 'drives.csv',
    "CatchShoot": 'catchshoot.csv',
    "Passing": 'passing.csv',
    "Possessions": 'touches.csv',
    "ElbowTouch": 'elbow.csv',
    "PostTouch": 'post.csv',
    "PaintTouch": 'paint.csv',
    "PullUpShot": 'pullup.csv'
}

if ps == False:
    for cat in category_frames.keys():
        file = 'tracking/' + category_maps[cat]
        old_df = pd.read_csv(file)
        old_df = old_df[old_df.year != current_year + 1]
        new_df = pd.concat(category_frames[cat])
        df = pd.concat([old_df, new_df])
        df.to_csv(file, index=False)
else:
    for cat in category_frames_ps.keys():
        file = 'tracking_ps/' + category_maps[cat]
        df = pd.concat(category_frames_ps[cat])
        df.to_csv(file, index=False)

Usage Example

# Configuration
ps = True  # Playoff mode
current_year = 2024
years = [i for i in range(2013, 2025)]

# Collect tracking data
if ps == False:
    category_frames = get_tracking(years, ps=ps)
else:
    category_frames_ps = get_tracking(years, ps=True)

# Save to files
for cat in category_frames_ps.keys():
    file = 'tracking_ps/' + category_maps[cat]
    df = pd.concat(category_frames_ps[cat])
    df.to_csv(file, index=False)
Output:
Processing 2024 season...
Fetching Drives data...
Fetching CatchShoot data...
Fetching Passing data...
Fetching Possessions data...
Fetching ElbowTouch data...
Fetching PostTouch data...
Fetching PaintTouch data...
Fetching PullUpShot data...
Saved to tracking_ps/drives.csv

Historical Data Management

The script updates existing files by:
  1. Reading old data
  2. Filtering out the current year (to avoid duplicates)
  3. Concatenating new data
  4. Saving updated file
old_df = pd.read_csv(file)
old_df = old_df[old_df.year != current_year + 1]  # Remove current year
new_df = pd.concat(category_frames[cat])  # New data
df = pd.concat([old_df, new_df])  # Combine
df.to_csv(file, index=False)

Analysis Use Cases

  • Offensive role identification: Drives vs catch-and-shoot vs pull-up shot distribution
  • Playmaking evaluation: Passing metrics, secondary assists, potential assists
  • Ball dominance: Touches, time of possession, dribbles per touch
  • Location tendencies: Elbow, post, and paint touches
  • Shot creation: Pull-up vs catch-and-shoot percentages

Build docs developers (and LLMs) love