Esempio n. 1
0
def game_id(home, away):
    """
    Finds the game_id of a game between the home and away team. The function
    checks whether or not the teams are valid, and if the teams are reversed,
    will provide the correct game_id for the meeting of these two teams
    
    Parameters
    ----------
    home: a string of the home team's code
    away: a string of the away team's code

    Returns
    -------
    desired_game_id: the game_id of the game in which home hosted away
    """
    # Validate that the home and away team codes supplied are valid team codes
    home = check.team_code(home)
    away = check.team_code(away)

    game_found = False
    while not game_found:
        # Bring in the schedule information
        games = load.games_data()

        # Check if the game existed as supplied
        desired_game = games[(games['home'] == home) & (games['away'] == away)]

        # If it did, break out of the loop
        if len(desired_game) == 1:
            game_found = True

        # Otherwise, alert user that the home team did not host the away team
        else:
            print(f'{home} did not host {away}. Checking if {away} hosted '
                  f'{home}')

            # Check if away team hosted home team
            desired_game = games[(games['home'] == away)
                                 & (games['away'] == home)]

            # If they did, break out of the loop
            if len(desired_game) == 1:
                game_found = True

            else:
                # Otherwise, prompt user to supply two new team codes
                print(f'{home} and {away} did not play each other in this '
                      'dataset')
                home = check.team_code('')
                away = check.team_code('')

    # Once a game has been identified, give back the game ID for the game
    desired_game_id = desired_game['game_id'].iloc[0]

    return desired_game_id
def plays_and_games(gid=0, home='', away='', prechecked_gid=False):
    """
    Merges play and game data together to better illustrate what plays are
    being run by which team and against which opponent

    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
    -------
    plays_from_game: a merged dataframe of play and game data

    """
    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 = find.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)

    # Load in the games data to merge
    games_data = load.games_data(
        gid, prechecked_gid)[['game_id', 'home', 'away', 'week']]

    plays_from_game = pd.merge(left=plays_from_game,
                               right=games_data,
                               how='inner',
                               on='game_id')

    plays_from_game['offensive_team'] = plays_from_game['possession_team']
    plays_from_game['defensive_team'] = np.where(
        plays_from_game['offensive_team'] == plays_from_game['home'],
        plays_from_game['away'], plays_from_game['home'])

    plays_from_game = plays_from_game[[
        'game_id', 'play_id', 'play_description', 'quarter', 'down',
        'yds_to_go', 'possession_team', 'play_type', 'yardline_side',
        'yardline_number', 'offense_formation', 'personnel_offense',
        'defenders_in_box', 'n_pass_rushers', 'personnel_defense',
        'type_dropback', 'presnap_away_score', 'presnap_home_score',
        'game_clock', 'absolute_yard_line', 'penalty_code', 'penalty_player',
        'pass_result', 'offensive_play_result', 'play_result', 'epa',
        'is_defensive_pi', 'down_dist_summary', 'home', 'away',
        'offensive_team', 'defensive_team', 'week'
    ]]

    return plays_from_game
def field(gid=0, home='nfl', away='', show=False, unit='yd', zero='l'):
    """
    Draws a football field with the teams who are participating in the game.
    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 of a game_id for which to draw the field
    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
    show: a boolean of whether or not to show the plot
    unit: a string for the units with which to draw the field. Default is 'yds'
        for yards, could be 'ft' for feet
    zero: a string for where the origin of the plot should be. Default is 'l',
        meaning lower left corner. Could be 'c' for center

    Returns
    -------
    fig, ax: the figure and axes objects (respectively)
    """

    # If a game ID is provided, get the home and away team from the provided
    # game ID
    if gid != 0:
        gid = check.game_id(gid)
        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)

    # Get the teams' color codes
    team_info = load.teams_data()

    home_info = team_info[team_info['team_code'] == home]
    away_info = team_info[team_info['team_code'] == away]

    #############################
    # Get the field coordinates #
    #############################
    sidelines, endlines, goal_lines, midline, minor_yd_lines_b, \
    minor_yd_lines_t, minor_yd_lines_l, minor_yd_lines_u, major_yd_lines, \
    hashes_l, hashes_u, extra_pt_mark, arrow_40_l, arrow_40_u, \
    arrow_30_l, arrow_30_u, arrow_20_l, arrow_20_u, arrow_10_l, \
    arrow_10_u, field_marks = load.football_field_coords()

    #################
    # Make the plot #
    #################
    fig, ax = plt.subplots()

    ax.set_aspect('equal')
    fig.set_size_inches(50, 22.2)
    ax.xaxis.set_visible(False)
    ax.yaxis.set_visible(False)

    # Set field color
    ax.set_facecolor('#196f0c')

    # Put home logo at midfield
    if home.lower() in ['', 'nfl', 'nfc', 'afc']:
        img = os.path.join('img', 'logos', 'nfl.png')
    else:
        img = os.path.join('img', 'logos', f'{home}.png')

    img = plt.imread(img)

    if unit == 'yd':
        ax.imshow(img, extent=[52., 68., 18.65, 34.65], zorder=10)
    else:
        ax.imshow(img, extent=[-18., 18., -18., 18.], zorder=10)

    # Add sidelines, goal line, and 50 yard line
    ax.fill(sidelines['x'], sidelines['y'], '#ffffff')
    ax.fill(endlines['x'], endlines['y'], '#ffffff')
    ax.fill(goal_lines['x'], goal_lines['y'], '#ffffff')
    ax.fill(midline['x'], midline['y'], '#ffffff')

    # Add minor yard lines and major yard lines
    ax.fill(minor_yd_lines_b['x'], minor_yd_lines_b['y'], '#ffffff')
    ax.fill(minor_yd_lines_t['x'], minor_yd_lines_t['y'], '#ffffff')
    ax.fill(minor_yd_lines_l['x'], minor_yd_lines_l['y'], '#ffffff')
    ax.fill(minor_yd_lines_u['x'], minor_yd_lines_u['y'], '#ffffff')
    ax.fill(major_yd_lines['x'], major_yd_lines['y'], '#ffffff')

    # Add hash marks and extra point markers
    ax.fill(hashes_l['x'], hashes_l['y'], '#ffffff')
    ax.fill(hashes_u['x'], hashes_u['y'], '#ffffff')
    ax.fill(extra_pt_mark['x'], extra_pt_mark['y'], '#ffffff')

    # Add the numbers to the field
    for i, label in field_marks.iterrows():
        ax.text(x=label['x'],
                y=label['y'],
                s=label['text'],
                fontsize=50,
                color='#ffffff',
                fontweight='bold',
                rotation=label['rotation'],
                fontname='Impact')

    # Add the arrows to the field
    ax.fill(arrow_40_l['x'], arrow_40_l['y'], '#ffffff')
    ax.fill(arrow_40_u['x'], arrow_40_u['y'], '#ffffff')
    ax.fill(arrow_30_l['x'], arrow_30_l['y'], '#ffffff')
    ax.fill(arrow_30_u['x'], arrow_30_u['y'], '#ffffff')
    ax.fill(arrow_20_l['x'], arrow_20_l['y'], '#ffffff')
    ax.fill(arrow_20_u['x'], arrow_20_u['y'], '#ffffff')
    ax.fill(arrow_10_l['x'], arrow_10_l['y'], '#ffffff')
    ax.fill(arrow_10_u['x'], arrow_10_u['y'], '#ffffff')

    ax.text(x=5,
            y=26.65,
            s=f'{home_info.nickname.iloc[0]}',
            fontdict={
                'ha': 'center',
                'va': 'center'
            },
            fontsize=100,
            fontweight='bold',
            fontname='Impact',
            color=f'{home_info.endzone_text.iloc[0]}',
            rotation=90,
            path_effects=[
                pe.withStroke(linewidth=20,
                              foreground=f'{home_info.endzone_shadow.iloc[0]}')
            ])

    ax.text(x=114,
            y=26.65,
            s=f'{away_info.nickname.iloc[0]}',
            fontdict={
                'ha': 'center',
                'va': 'center'
            },
            fontsize=100,
            fontweight='bold',
            fontname='Impact',
            color=f'{away_info.endzone_text.iloc[0]}',
            rotation=-90,
            path_effects=[
                pe.withStroke(linewidth=20,
                              foreground=f'{away_info.endzone_shadow.iloc[0]}')
            ])

    if show:
        plt.show()
        return None
    else:
        return fig, ax
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
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
Esempio n. 6
0
def plays_matching(gid=0,
                   home='',
                   away='',
                   play_info={},
                   prechecked_gid=False):
    """
    

    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
    -------
    plays_from_game: a dataframe of plays that match the passed criteria
    """
    # 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 data
    plays_from_game = merge.plays_and_games(gid, home, away, prechecked_gid)

    # Subset by the information about the play in the parameter play_info
    if bool(play_info):
        # Fix all strings to be upper case
        if 'offensive_team' in play_info.keys():
            play_info['offensive_team'] = play_info['offensive_team'].upper()

        if 'defensive_team' in play_info.keys():
            play_info['defensive_team'] = play_info['defensive_team'].upper()

        if 'possession_team' in play_info.keys():
            play_info['possession_team'] = play_info['possession_team'].upper()

        if 'home' in play_info.keys():
            play_info['home'] = play_info['home'].upper()

        if 'away' in play_info.keys():
            play_info['away'] = play_info['away'].upper()

        if 'pass_result' in play_info.keys():
            play_info['pass_result'] = play_info['pass_result'].upper()

        if 'type_dropback' in play_info.keys():
            play_info['type_dropback'] = play_info['type_dropback'].upper()

        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]

    # Return all plays that match the criteria
    return plays_from_game
Esempio n. 7
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