コード例 #1
0
def yards_to_go(gid, pid, prechecked_gid=False, prechecked_pid=False):
    """
    Finds the distance needed (in yards) to achieve a first down

    Parameters
    ----------
    gid: an integer of a game_id
    pid: an integer of a play_id
    prechecked_gid: a boolean of whether or not the game ID has been checked
        before being passed to the function
    prechecked_pid: a boolean of whether or not the play ID has been checked
         before being passed to the function
         
    Returns
    -------
    yds_to_go: an integer number of yards needed on a play to achieve
        a first down
    """
    if not prechecked_gid:
        # Validate the game ID
        gid = check.game_id(gid)
        prechecked_gid = True

    if not prechecked_pid:
        # Validate the play ID
        pid = check.play_id(gid, pid)
        prechecked_pid = True

    # Load in the plays data
    play = load.plays_data(gid, pid, prechecked_gid, prechecked_pid)

    # Get the number of yards needed for a first down
    yds_to_go = play['yds_to_go'].iloc[0]

    return yds_to_go
コード例 #2
0
def line_of_scrimmage(gid, pid, prechecked_gid=False, prechecked_pid=False):
    """
    Finds the line of scrimmage for a specified play

    Parameters
    ----------
    gid: an integer of a game_id
    pid: an integer of a play_id
    prechecked_gid: a boolean of whether or not the game ID has been checked
        before being passed to the function
    prechecked_pid: a boolean of whether or not the play ID has been checked
         before being passed to the function
    
    Returns
    -------
    los: a float of the absolute yardline of the line of scrimmage
    """
    if not prechecked_gid:
        # Validate the game ID
        gid = check.game_id(gid)
        prechecked_gid = True

    if not prechecked_pid:
        # Validate the play ID
        pid = check.play_id(gid, pid)
        prechecked_pid = True

    # Load in the plays data
    play = load.plays_data(gid, pid, prechecked_gid, prechecked_pid)

    # Get the line of scrimmage
    los = play['absolute_yard_line'].iloc[0]

    return los
コード例 #3
0
def n_frames(gid,
             pid,
             tracking=pd.DataFrame(),
             prechecked_gid=False,
             prechecked_pid=False):
    """
    Finds the number of frames recorded for a particular play

    Parameters
    ----------
    gid: an integer of a game_id
    pid: an integer of a play_id
    tracking: a set of tracking information pertaining to a particular play.
        If none is provided, the entire tracking set will be used. This is
        the default
    prechecked_gid: a boolean of whether or not the game ID has been checked
        before being passed to the function
    prechecked_pid: a boolean of whether or not the play ID has been checked
         before being passed to the function

    Returns
    -------
    num_frames: an integer representing how many frames were recorded for the
        play
    """
    if not prechecked_gid:
        # Validate the game ID
        gid = check.game_id(gid)
        prechecked_gid = True

    if not prechecked_pid:
        # Validate the play ID
        pid = check.play_id(gid, pid)
        prechecked_pid = True

    # If no tracking information is provided, load the tracking information
    # for the week containing the desired play
    if tracking.empty:
        week = game_week(gid)
        tracking = load.tracking_data(gid,
                                      pid,
                                      week,
                                      prechecked_gid,
                                      prechecked_pid,
                                      prechecked_week=True)

    # Get the last frame of the play
    num_frames = tracking['frame_id'].max()

    return num_frames
コード例 #4
0
def play_gif(gid=0,
             pid=0,
             home='',
             away='',
             prechecked_gid=False,
             prechecked_pid=False,
             tracking=pd.DataFrame()):
    # If a game ID is provided, get the home and away team from the provided
    # game ID
    if gid != 0:
        # Start by checking the game ID if it is provided but not yet checked
        if not prechecked_gid:
            gid = check.game_id(gid)
            prechecked_gid = True

        # Get the home and away teams for the game
        home, away = find.game_teams(gid)

    # If no game ID provided, and the home team is 'NFL', set home and away
    # to NFC and AFC respectively. Otherwise, check to make sure the teams are
    # legit
    else:
        home = home.upper()
        away = away.upper()
        if home == 'NFL':
            home = 'NFC'
            away = 'AFC'
        else:
            home = check.team_code(home)
            away = check.team_code(away)
            gid = find.game_id(home, away)

    # Next, check the play ID if it has not already been checked
    if not prechecked_pid:
        pid = check.play_id(gid, pid, prechecked_gid)
        prechecked_pid = True

    # If tracking isn't supplied, load all relevant tracking data
    if tracking.empty:
        tracking = merge.tracking_and_plays(gid, pid)

    # Get the number of frames in the play
    n_frames = find.n_frames(gid=gid,
                             pid=pid,
                             tracking=tracking,
                             prechecked_gid=True,
                             prechecked_pid=True)

    # Make the temporary directory to hold static images
    file_ops.make_gif_temp_dir(gid, pid)

    # Make each frame as a static image
    for i in np.arange(1, n_frames + 1):
        print(f'Processing frame {i} of {n_frames}')
        fig, ax = play_frame(gid,
                             pid,
                             frame_no=i,
                             prechecked_gid=True,
                             prechecked_pid=True,
                             tracking=tracking,
                             prechecked_frame=True)

        if i < 10:
            fname = os.path.join('img', 'temp', f'{gid}_{pid}',
                                 f'{gid}_{pid}_000{i}.png')
        elif i < 100:
            fname = os.path.join('img', 'temp', f'{gid}_{pid}',
                                 f'{gid}_{pid}_00{i}.png')
        else:
            fname = os.path.join('img', 'temp', f'{gid}_{pid}',
                                 f'{gid}_{pid}_0{i}.png')

        plt.savefig(f'{fname}', bbox_inches='tight', pad_inches=0)

    try:
        gif_fname = tracking['down_dist_summary'].values[0] + '.gif'

    except:
        gif_fname = str(pid) + '.gif'

    # Collect the static images
    images = file_ops.collect_gif_play_frames(gid, pid)

    # Make and save the gif
    file_ops.make_gif(gid, pid, images, fname=gif_fname)

    # Delete the temporary directory that holds all static images.
    file_ops.remove_temp_static_frame_directory(gid, pid)

    return None
コード例 #5
0
def play_frame(gid=0,
               pid=0,
               home='',
               away='',
               frame_no=0,
               plot_los=True,
               plot_first_down_marker=True,
               plot_arrows=True,
               prechecked_gid=False,
               prechecked_pid=False,
               prechecked_frame=False,
               tracking=pd.DataFrame()):
    """
    Draw a frame of a given play. Teams are either supplied via the home and
    away arguments, or by looking them up from the game_id provided by the gid
    argument

    Parameters
    ----------
    gid: an int representing the game_id
    pid: an int representing the play_id
    home: a string of the home team's code. Not necessary if a game_id is
        provided
    away: a string of the away team's code. Not necessary if a game_id is
        provided
    frame_no: the number of the frame to plot
    plot_los: a boolean of whether or not to plot the line of scrimmage on the
        plot
    plot_first_down_marker: a boolean of whether or not to plot the first
        down line on the plot
    prechecked_frame: a boolean indicating whether or not it's okay to
        skip the frame validation. Defaulting to False, but should be set to
        True when using the draw_play_gif() function
    tracking: a dataframe of tracking data that can be used to speed up
        plotting

    Returns
    -------
    fig, ax: the figure and axes objects (respectively)
    """
    if gid != 0:
        # Start by checking the game ID if it is provided but not yet checked
        if not prechecked_gid:
            gid = check.game_id(gid)
            prechecked_gid = True

        # Get the home and away teams for the game
        home, away = find.game_teams(gid)

    # If no game ID provided, and the home team is 'NFL', set home and away
    # to NFC and AFC respectively. Otherwise, check to make sure the teams are
    # legit
    else:
        home = home.upper()
        away = away.upper()
        if home == 'NFL':
            home = 'NFC'
            away = 'AFC'
        else:
            home = check.team_code(home)
            away = check.team_code(away)
            gid = find.game_id(home, away)

    # Next, check the play ID if it has not already been checked
    if not prechecked_pid:
        pid = check.play_id(gid, pid, prechecked_gid)
        prechecked_pid = True

    # If tracking isn't supplied, load all relevant tracking data
    if tracking.empty:
        tracking = merge.tracking_and_plays(gid, pid)

    if not prechecked_frame:
        frame_no = check.frame_no(gid, pid, frame_no, tracking)

    # Start prepping the data for the plot. Primarily, the jersey numbers'
    # rotation angle based on team and play direction
    tracking['jersey_num_orientation'] = orient_jersey_num(
        gid, pid, prechecked_gid, prechecked_pid, tracking)

    # Split the frame's data into the home team, the away team, and the ball's
    # data (respectively)
    home_frame = tracking[(tracking['team'] == 'home')
                          & (tracking['frame_id'] == frame_no)]
    away_frame = tracking[(tracking['team'] == 'away')
                          & (tracking['frame_id'] == frame_no)]
    ball_frame = tracking[(tracking['team'] == 'football')
                          & (tracking['frame_id'] == frame_no)]

    # Get the hex color information about each team to use to make the plot
    teams_info = load.teams_data()
    home_info = teams_info[teams_info['team_code'] == home]
    away_info = teams_info[teams_info['team_code'] == away]

    home_uni_base = home_info['home_uni_base'].iloc[0]
    home_uni_highlight = home_info['home_uni_highlight'].iloc[0]
    home_uni_number = home_info['home_uni_number'].iloc[0]
    home_uni_number_highlight = home_info['home_uni_number_highlight'].iloc[0]

    away_uni_base = away_info['away_uni_base'].iloc[0]
    away_uni_highlight = away_info['away_uni_highlight'].iloc[0]
    away_uni_number = away_info['away_uni_number'].iloc[0]
    away_uni_number_highlight = away_info['away_uni_number_highlight'].iloc[0]

    # If the line of scrimmage is to be plotted, determine its position
    if plot_los:
        los = find.line_of_scrimmage(gid, pid)
        los = pd.DataFrame({
            'x': [
                los - (2 / 12), los + (2 / 12), los + (2 / 12), los - (2 / 12),
                los - (2 / 12)
            ],
            'y': [1 / 9, 1 / 9, 53 + (2 / 9), 53 + (2 / 9), 1 / 9]
        })

    # If the first down line is to be plotted, determine its position
    if plot_first_down_marker:
        first_down = find.first_down_line(gid, pid, tracking, prechecked_gid,
                                          prechecked_pid)

        first_down_line = pd.DataFrame({
            'x': [
                first_down - (2 / 12), first_down + (2 / 12),
                first_down + (2 / 12), first_down - (2 / 12),
                first_down - (2 / 12)
            ],
            'y': [1 / 9, 1 / 9, 53 + (2 / 9), 53 + (2 / 9), 1 / 9]
        })

    # Draw the field
    fig, ax = field(gid)

    # Plot the home team's players
    home_frame.plot(x='player_x',
                    y='player_y',
                    kind='scatter',
                    ax=ax,
                    color=home_uni_base,
                    s=800,
                    edgecolor=home_uni_highlight,
                    linewidth=2,
                    zorder=15)

    # Add the jersey numbers for the home team
    for i, player in home_frame.iterrows():
        ax.text(
            x=player['player_x'],
            y=player['player_y'],
            s=str(int(player['player_no'])),
            fontsize=15,
            color=home_uni_number,
            path_effects=[
                pe.withStroke(linewidth=3,
                              foreground=home_uni_number_highlight)
            ],
            fontweight='bold',
            rotation=player['jersey_num_orientation'],
            zorder=20,
            fontdict={
                'ha': 'center',
                'va': 'center'
            },
        )

        if plot_arrows:
            ax.arrow(x=player['player_x'],
                     y=player['player_y'],
                     dx=3 * math.cos(player['player_orientation']),
                     dy=3 * math.sin(player['player_orientation']),
                     length_includes_head=True,
                     width=0.3,
                     color=home_uni_highlight,
                     zorder=14)

    # Plot the away team's players
    away_frame.plot('player_x',
                    'player_y',
                    kind='scatter',
                    ax=ax,
                    color=away_uni_base,
                    s=800,
                    edgecolor=away_uni_highlight,
                    linewidth=2,
                    zorder=15)

    # Add the jersey numbers for the away team
    for i, player in away_frame.iterrows():
        ax.text(
            x=player['player_x'],
            y=player['player_y'],
            s=str(int(player['player_no'])),
            fontsize=15,
            color=away_uni_number,
            path_effects=[
                pe.withStroke(linewidth=3,
                              foreground=away_uni_number_highlight)
            ],
            fontweight='bold',
            rotation=player['jersey_num_orientation'],
            zorder=20,
            fontdict={
                'ha': 'center',
                'va': 'center'
            },
        )

        if plot_arrows:
            ax.arrow(x=player['player_x'],
                     y=player['player_y'],
                     dx=3 * math.cos(player['player_orientation']),
                     dy=3 * math.sin(player['player_orientation']),
                     length_includes_head=True,
                     width=0.3,
                     color=away_uni_highlight,
                     zorder=14)

    # Plot the ball
    ball_frame.plot('player_x',
                    'player_y',
                    kind='scatter',
                    ax=ax,
                    color='#624a2e',
                    s=100,
                    edgecolor='#000000',
                    linewidth=2,
                    zorder=15)

    ax.fill(los['x'], los['y'], '#183ec1')
    ax.fill(first_down_line['x'], first_down_line['y'], '#ffcb05')

    return fig, ax
コード例 #6
0
def orient_jersey_num(gid,
                      pid,
                      prechecked_gid=False,
                      prechecked_pid=False,
                      tracking=pd.DataFrame()):
    """
    Manipulate the tracking data to get the correct orientation for the jersey
    numbers of players involved in the play that will be plotted

    Parameters
    ----------
    gid: an integer of a game_id
    pid: an integer of a play_id
    prechecked_gid: a boolean of whether or not the game ID has been checked
        before being passed to the function
    prechecked_pid: a boolean of whether or not the play ID has been checked
         before being passed to the function
    tracking: a dataframe of tracking data that can be used to speed up
        data loading

    Returns
    -------
    tracking: a dataframe of tracking data with the proper orientation for the
        jersey numbers of the players involved
    """
    # If the game ID is not already checked, check that first
    if not prechecked_gid:
        gid = check.game_id(gid)
        prechecked_gid = True

    # Now that the game ID is checked, move to the play ID. If that is not
    # already checked, check that next. prechecked_gid is now True regardless
    # of its initially passed value since the game ID has been checked in the
    # first if statement
    if not prechecked_pid:
        pid = check.play_id(gid, pid, prechecked_gid)

    # Now that the game ID and play ID have been checked, load the tracking
    # data for the play in the provided game. This will load all tracking data
    # for the play. It will
    if tracking.empty:
        tracking = merge.tracking_and_plays(gid, pid)

    tracking.loc[tracking['team'] == 'football', 'jersey_num_orientation'] = 0

    tracking.loc[(tracking['team'] == 'home') &
                 (tracking['play_direction'] == 'right'),
                 'jersey_num_orientation'] = -90

    tracking.loc[(tracking['team'] == 'away') &
                 (tracking['play_direction'] == 'right'),
                 'jersey_num_orientation'] = 90

    tracking.loc[(tracking['team'] == 'home') &
                 (tracking['play_direction'] == 'left'),
                 'jersey_num_orientation'] = 90

    tracking.loc[(tracking['team'] == 'away') &
                 (tracking['play_direction'] == 'left'),
                 'jersey_num_orientation'] = -90

    return tracking['jersey_num_orientation']
コード例 #7
0
def tracking_data(gid = 0, pid = 0, week = 0, prechecked_gid = False,
                  prechecked_pid = False, prechecked_week = False):
    """
    Loads the tracking information provided for a specified week
    
    Parameters
    ----------
    gid: an integer of a game_id
    pid: an integer of a play_id
    week: an integer of which week's tracking data to return. A value of
        0 implies to return all weeks' tracking. The default is 0.
    prechecked_gid: a boolean of whether or not the game ID has been checked
        before being passed to the function
    prechecked_pid: a boolean of whether or not the play ID has been checked
         before being passed to the function

    Returns
    -------
    trk: a data frame containing a cleaned, renamed copy of tracking
        information for the specified week
    """
    # Check which week to load. If neither the game ID nor week number are
    # passed to the function, load all weeks (this is slow)
    if gid == 0 and week == 0:
        trk = pd.DataFrame()
        for week in range(1, 17):
            print(f'Loading week {week}...', end = '\r')
            week_file = os.path.join(fp.data_dir, f'week{week}.csv')
            this_week = pd.read_csv(week_file)
            trk = pd.concat([trk, this_week])
    else:
        # If the game ID is provided, but not checked, check the game ID first
        if gid != 0:
            if prechecked_gid == False:
                gid = check.game_id(gid)
            else:
                pass
            if week == 0:
                week = find.game_week(gid)
                prechecked_week = True
            
        if week != 0:
            # If the week is prechecked, that's great. If not, check the week
            if prechecked_week == True:
                pass
            else:
                week = check.week_number(week)
            
        # If the week number is not supplied, set the week number
        elif gid != 0 :
            week = check.week_number(week)
        
        # If the play ID is provided, but not checked, check the play ID next
        if pid != 0:
            if prechecked_pid == False:
                pid = check.play_id(gid, pid)
            else:
                pass
            
        # Now that the relevant data has all been checked, load the dataset
        # accordingly
        if gid != 0 and pid != 0:
            # If there is a game ID and play ID supplied, load only the
            # tracking information for this play in this game
            week_file = os.path.join(fp.data_dir, f'week{week}.csv')
            trk = pd.read_csv(week_file)[lambda x:
                                         (x['gameId'] == gid) &
                                         (x['playId'] == pid)]
        elif gid != 0 and pid == 0:
            # If there is a game ID but not a play ID supplied, load all
            # tracking data from all plays of this game
            week_file = os.path.join(fp.data_dir, f'week{week}.csv')
            trk = pd.read_csv(week_file)[lambda x: x['gameId'] == gid]
                
        elif gid == 0 and pid != 0:
            # If there is a play Id but not a game ID supplied, load all
            # tracking data from all plays in the week of this game with a
            # matching play ID
            week_file = os.path.join(fp.data_dir, f'week{week}.csv')
            trk = pd.read_csv(week_file)[lambda x: (x['playID'] == pid)]
        else:
            # If there's no game ID or play ID supplied, load all tracking
            # data for the week
            week_file = os.path.join(fp.data_dir, f'week{week}.csv')
            trk = pd.read_csv(week_file)
    
    # Rename columns
    trk.columns = [
        'time', 'player_x', 'player_y', 'player_speed', 'player_acceleration',
        'distance', 'player_orientation', 'player_direction', 'event_str',
        'player_id', 'player_name', 'player_no', 'player_position', 'frame_id',
        'team', 'game_id', 'play_id', 'play_direction', 'route_type'
    ]
    
    # Correct the angular variables to be plottable (needs to be in radians)
    trk['player_orientation'] = np.mod(90 - trk['player_orientation'], 360)
    trk['player_orientation'] *= math.pi / 180
    
    trk['player_direction'] = np.mod(90 - trk['player_direction'], 360)
    trk['player_direction'] *= math.pi / 180
    
    return trk
コード例 #8
0
def first_down_line(gid,
                    pid,
                    tracking=pd.DataFrame(),
                    prechecked_gid=False,
                    prechecked_pid=False):
    """
    Finds what yardline is needed to be gained to achieve a first down

    Parameters
    ----------
    gid: an integer of a game_id
    pid: an integer of a play_id
    tracking: a set of tracking information pertaining to a particular play.
        If none is provided, the entire tracking set will be used. This is
        the default
    prechecked_gid: a boolean of whether or not the game ID has been checked
        before being passed to the function
    prechecked_pid: a boolean of whether or not the play ID has been checked
         before being passed to the function

    Returns
    -------
    first_down_yardline: a float representing the absolute yardline needed
        to achieve a first down
    """
    if not prechecked_gid:
        # Validate the game ID
        gid = check.game_id(gid)
        prechecked_gid = True

    if not prechecked_pid:
        # Validate the play ID
        pid = check.play_id(gid, pid)
        prechecked_pid = True

    # Load in the schedule data
    games = load.games_data(gid, prechecked_gid)

    # Get the week of the game so that the correct tracking information can be
    # loaded
    week = games.loc[games['game_id'] == gid, 'week'].iloc[0]

    # Get the line of scrimmage and number of yards needed to achieve a first
    # down
    los = line_of_scrimmage(gid, pid)
    distance_to_first = yards_to_go(gid, pid)

    # Load in the appropriate tracking data, then subset to only be for the
    # desired play
    if tracking.empty:
        tracking = load.tracking_data(gid,
                                      pid,
                                      week,
                                      prechecked_gid=True,
                                      prechecked_pid=True,
                                      prechecked_week=True)

    # Get the direction of play. If the play is going right, yards will be
    # added, otherwise they will be subtracted
    play_direction = tracking['play_direction'].iloc[0]

    # Calculate the yardline needed to be gained to achieve a first down
    if play_direction == 'right':
        first_down_yardline = los + distance_to_first
    else:
        first_down_yardline = los - distance_to_first

    return first_down_yardline
コード例 #9
0
def play_id(gid=0, home='', away='', play_info={}, prechecked_gid=False):
    """
    Finds the play ID of a particular play

    Parameters
    ----------
    gid: an integer of a game_id
    home: a string representing the home team's team code
    away: a string representing the away team's team code
    play_info: a dictionary of parameters to use for subsetting. The keys MUST
        be columns in the plays data to be used. If not, they will be ignored
    prechecked_gid: a boolean of whether or not the game ID has been prechecked

    Returns
    -------
    pid: an integer of a play_id
    """
    # Game ID should be the primary lookup tool, so start with loading the
    # game's data if this is passed
    if gid != 0:
        # If the game ID is not already checked, check the game ID first
        if not prechecked_gid:
            gid = check.game_id(gid)
            prechecked_gid = True

    # If the game ID is not passed, then try to get a game ID based on the home
    # and away team. If this yields nothing, then load all games
    if home != '' or away != '':
        home = check.team_code(home)
        away = check.team_code(away)

        gid = game_id(home, away)
        prechecked_gid = True

    # Load in plays from the identified game, or from all games if game ID = 0
    plays_from_game = load.plays_data(gid=gid, prechecked_gid=prechecked_gid)

    # Subset by the information about the play in the parameter play_info
    if bool(play_info):
        for key, val in play_info.items():
            # If the desired parameter is not in the columns of the plays data,
            # alert user and skip this subsetting parameter
            if key not in plays_from_game.columns:
                print(f'{key} is not a valid column to use for subsetting as'
                      ' it does not appear in the dataset.')
                continue

            # If the value passed in the plays_info dictionary is a list, use
            # the .isin() method for subsetting
            if type(val) == list:
                plays_from_game = plays_from_game[
                    plays_from_game[f'{key}'].isin(val)]

            # Otherwise, use the key and value alone
            else:
                plays_from_game = plays_from_game[plays_from_game[f'{key}'] ==
                                                  val]

    # If the passed parameters are enough to identify the play, there
    # should only be one play ID remaining. Return this value
    if len(plays_from_game) == 1:
        pid = plays_from_game['play_id'].values[0]

    else:
        for i, play in plays_from_game.iterrows():
            print(f'{play.game_id} -- {play.play_id} -- '
                  f'{play.down_dist_summary}')

        gid = input('Which game ID were you looking for?\nGame ID: ')
        pid = input('Which play of the above are you looking for?\nPlay ID: ')

        gid = check.game_id(gid)
        prechecked_gid = True
        pid = check.play_id(gid, pid, prechecked_gid)

    return pid