sportsref/nba/pbp.py
def get_period_starters(df):
    """TODO
    """

    def players_from_play(play):
        """Figures out what players are in the game based on the players
        mentioned in a play. Returns away and home players as two sets.

        :param play: A dictionary representing a parsed play.
        :returns: (aw_players, hm_players)
        :rtype: tuple of lists
        """
        # if it's a tech FT from between periods, don't count this play
        if (
            play['clock_time'] == '12:00.0' and
            (play.get('is_tech_foul') or play.get('is_tech_fta'))
        ):
            return [], []

        stats = sportsref.nba.BoxScore(play['boxscore_id']).basic_stats()
        home_grouped = stats.groupby('is_home')
        hm_roster = set(home_grouped.player_id.get_group(True).values)
        aw_roster = set(home_grouped.player_id.get_group(False).values)
        player_keys = [
            'assister', 'away_jumper', 'blocker', 'drew_foul', 'fouler',
            'ft_shooter', 'gains_poss', 'home_jumper', 'rebounder', 'shooter',
            'stealer', 'sub_in', 'sub_out', 'to_by'
        ]
        players = [p for p in play[player_keys] if pd.notnull(p)]

        aw_players = [p for p in players if p in aw_roster]
        hm_players = [p for p in players if p in hm_roster]
        return aw_players, hm_players

    # create a mapping { quarter => (away_starters, home_starters) }
    n_periods = df.quarter.nunique()
    period_starters = [(set(), set()) for _ in range(n_periods)]

    # fill out this mapping quarter by quarter
    for qtr, qtr_grp in df.groupby(df.quarter):
        aw_starters, hm_starters = period_starters[qtr-1]
        exclude = set()
        # loop through sets of plays that happen at the "same time"
        for label, time_grp in qtr_grp.groupby(qtr_grp.secs_elapsed):
            # first, if they sub in and weren't already starters, exclude them
            sub_ins = set(time_grp.sub_in.dropna().values)
            exclude.update(sub_ins - aw_starters - hm_starters)
            # second, figure out new starters from each play at this time
            for i, row in time_grp.iterrows():
                aw_players, hm_players = players_from_play(row)
                # update overall sets for the quarter
                aw_starters.update(aw_players)
                hm_starters.update(hm_players)
            # remove excluded (subbed-in) players
            hm_starters -= exclude
            aw_starters -= exclude
            # check whether we have found all starters
            if len(hm_starters) > 5 or len(aw_starters) > 5:
                import ipdb
                ipdb.set_trace()
            if len(hm_starters) >= 5 and len(aw_starters) >= 5:
                break

        if len(hm_starters) != 5 or len(aw_starters) != 5:
            print('WARNING: wrong number of starters for a team in Q{} of {}'
                  .format(qtr, df.boxscore_id.iloc[0]))

    return period_starters
def get_period_starters(df):
    """TODO
    """

    def players_from_play(play):
        """Figures out what players are in the game based on the players
        mentioned in a play. Returns away and home players as two sets.

        :param play: A dictionary representing a parsed play.
        :returns: (aw_players, hm_players)
        :rtype: tuple of lists
        """
        # if it's a tech FT from between periods, don't count this play
        if (
            play['clock_time'] == '12:00.0' and
            (play.get('is_tech_foul') or play.get('is_tech_fta'))
        ):
            return [], []

        stats = sportsref.nba.BoxScore(play['boxscore_id']).basic_stats()
        home_grouped = stats.groupby('is_home')
        hm_roster = set(home_grouped.player_id.get_group(True).values)
        aw_roster = set(home_grouped.player_id.get_group(False).values)
        player_keys = [
            'assister', 'away_jumper', 'blocker', 'drew_foul', 'fouler',
            'ft_shooter', 'gains_poss', 'home_jumper', 'rebounder', 'shooter',
            'stealer', 'sub_in', 'sub_out', 'to_by'
        ]
        players = [p for p in play[player_keys] if pd.notnull(p)]

        aw_players = [p for p in players if p in aw_roster]
        hm_players = [p for p in players if p in hm_roster]
        return aw_players, hm_players

    # create a mapping { quarter => (away_starters, home_starters) }
    n_periods = df.quarter.nunique()
    period_starters = [(set(), set()) for _ in range(n_periods)]

    # fill out this mapping quarter by quarter
    for qtr, qtr_grp in df.groupby(df.quarter):
        aw_starters, hm_starters = period_starters[qtr-1]
        exclude = set()
        # loop through sets of plays that happen at the "same time"
        for label, time_grp in qtr_grp.groupby(qtr_grp.secs_elapsed):
            # first, if they sub in and weren't already starters, exclude them
            sub_ins = set(time_grp.sub_in.dropna().values)
            exclude.update(sub_ins - aw_starters - hm_starters)
            # second, figure out new starters from each play at this time
            for i, row in time_grp.iterrows():
                aw_players, hm_players = players_from_play(row)
                # update overall sets for the quarter
                aw_starters.update(aw_players)
                hm_starters.update(hm_players)
            # remove excluded (subbed-in) players
            hm_starters -= exclude
            aw_starters -= exclude
            # check whether we have found all starters
            if len(hm_starters) > 5 or len(aw_starters) > 5:
                import ipdb
                ipdb.set_trace()
            if len(hm_starters) >= 5 and len(aw_starters) >= 5:
                break

        if len(hm_starters) != 5 or len(aw_starters) != 5:
            print('WARNING: wrong number of starters for a team in Q{} of {}'
                  .format(qtr, df.boxscore_id.iloc[0]))

    return period_starters
def get_period_starters ( df ) : def players_from_play ( play ) : """Figures out what players are in the game based on the players
        mentioned in a play. Returns away and home players as two sets.

        :param play: A dictionary representing a parsed play.
        :returns: (aw_players, hm_players)
        :rtype: tuple of lists
        """ # if it's a tech FT from between periods, don't count this play if ( play [ 'clock_time' ] == '12:00.0' and ( play . get ( 'is_tech_foul' ) or play . get ( 'is_tech_fta' ) ) ) : return [ ] , [ ] stats = sportsref . nba . BoxScore ( play [ 'boxscore_id' ] ) . basic_stats ( ) home_grouped = stats . groupby ( 'is_home' ) hm_roster = set ( home_grouped . player_id . get_group ( True ) . values ) aw_roster = set ( home_grouped . player_id . get_group ( False ) . values ) player_keys = [ 'assister' , 'away_jumper' , 'blocker' , 'drew_foul' , 'fouler' , 'ft_shooter' , 'gains_poss' , 'home_jumper' , 'rebounder' , 'shooter' , 'stealer' , 'sub_in' , 'sub_out' , 'to_by' ] players = [ p for p in play [ player_keys ] if pd . notnull ( p ) ] aw_players = [ p for p in players if p in aw_roster ] hm_players = [ p for p in players if p in hm_roster ] return aw_players , hm_players # create a mapping { quarter => (away_starters, home_starters) } n_periods = df . quarter . nunique ( ) period_starters = [ ( set ( ) , set ( ) ) for _ in range ( n_periods ) ] # fill out this mapping quarter by quarter for qtr , qtr_grp in df . groupby ( df . quarter ) : aw_starters , hm_starters = period_starters [ qtr - 1 ] exclude = set ( ) # loop through sets of plays that happen at the "same time" for label , time_grp in qtr_grp . groupby ( qtr_grp . secs_elapsed ) : # first, if they sub in and weren't already starters, exclude them sub_ins = set ( time_grp . sub_in . dropna ( ) . values ) exclude . update ( sub_ins - aw_starters - hm_starters ) # second, figure out new starters from each play at this time for i , row in time_grp . iterrows ( ) : aw_players , hm_players = players_from_play ( row ) # update overall sets for the quarter aw_starters . update ( aw_players ) hm_starters . update ( hm_players ) # remove excluded (subbed-in) players hm_starters -= exclude aw_starters -= exclude # check whether we have found all starters if len ( hm_starters ) > 5 or len ( aw_starters ) > 5 : import ipdb ipdb . set_trace ( ) if len ( hm_starters ) >= 5 and len ( aw_starters ) >= 5 : break if len ( hm_starters ) != 5 or len ( aw_starters ) != 5 : print ( 'WARNING: wrong number of starters for a team in Q{} of {}' . format ( qtr , df . boxscore_id . iloc [ 0 ] ) ) return period_starters
TODO
TODO