diff --git a/sportsreference/nba/constants.py b/sportsreference/nba/constants.py index c91de928..08bd057f 100644 --- a/sportsreference/nba/constants.py +++ b/sportsreference/nba/constants.py @@ -289,7 +289,24 @@ 'shooting_fouls_drawn': 'td[data-stat="drawn_shooting"]', 'and_ones': 'td[data-stat="and1s"]', 'shots_blocked': 'td[data-stat="fga_blkd"]', - 'salary': 'td[data-stat="salary"]' + 'salary': 'td[data-stat="salary"]', + 'field_goals_per_poss': 'td[data-stat="fg_per_poss"]', + 'field_goal_attempts_per_poss': 'td[data-stat="fga_per_poss"]', + 'three_pointers_per_poss': 'td[data-stat="fg3_per_poss"]', + 'three_point_attempts_per_poss': 'td[data-stat="fg3a_per_poss"]', + 'two_pointers_per_poss': 'td[data-stat="fg2_per_poss"]', + 'two_point_attempts_per_poss': 'td[data-stat="fg2a_per_poss"]', + 'free_throws_per_poss': 'td[data-stat="ft_per_poss"]', + 'free_throw_attempts_per_poss': 'td[data-stat="fta_per_poss"]', + 'offensive_rebounds_per_poss': 'td[data-stat="orb_per_poss"]', + 'defensive_rebounds_per_poss': 'td[data-stat="drb_per_poss"]', + 'total_rebounds_per_poss': 'td[data-stat="trb_per_poss"]', + 'assists_per_poss': 'td[data-stat="ast_per_poss"]', + 'steals_per_poss': 'td[data-stat="stl_per_poss"]', + 'blocks_per_poss': 'td[data-stat="blk_per_poss"]', + 'turnovers_per_poss': 'td[data-stat="tov_per_poss"]', + 'personal_fouls_per_poss': 'td[data-stat="pf_per_poss"]', + 'points_per_poss': 'td[data-stat="pts_per_poss"]' } NATIONALITY = { diff --git a/sportsreference/nba/roster.py b/sportsreference/nba/roster.py index 5ebdcba0..7fdc9121 100644 --- a/sportsreference/nba/roster.py +++ b/sportsreference/nba/roster.py @@ -167,6 +167,23 @@ def __init__(self, player_id): self._shots_blocked = None self._salary = None self._contract = None + self._field_goals_per_poss = None + self._field_goal_attempts_per_poss = None + self._three_pointers_per_poss = None + self._three_point_attempts_per_poss = None + self._two_pointers_per_poss = None + self._two_point_attempts_per_poss = None + self._free_throws_per_poss = None + self._free_throw_attempts_per_poss = None + self._offensive_rebounds_per_poss = None + self._defensive_rebounds_per_poss = None + self._total_rebounds_per_poss = None + self._assists_per_poss = None + self._steals_per_poss = None + self._blocks_per_poss = None + self._turnovers_per_poss = None + self._personal_fouls_per_poss = None + self._points_per_poss = None player_data = self._pull_player_data() if not player_data: @@ -300,8 +317,8 @@ def _combine_all_stats(self, player_info): """ all_stats_dict = {} - for table_id in ['totals', 'advanced', 'shooting', 'advanced_pbp', - 'all_salaries']: + for table_id in ['totals', 'per_poss', 'advanced', 'shooting', + 'advanced_pbp', 'all_salaries']: table_items = utils._get_stats_table(player_info, 'table#%s' % table_id) career_items = utils._get_stats_table(player_info, @@ -561,19 +578,23 @@ class index value, the dictionary should be regenerated every time the 'and_ones': self.and_ones, 'assist_percentage': self.assist_percentage, 'assists': self.assists, + 'assists_per_poss': self.assists_per_poss, 'block_percentage': self.block_percentage, 'blocking_fouls': self.blocking_fouls, 'blocks': self.blocks, + 'blocks_per_poss': self.blocks_per_poss, 'box_plus_minus': self.box_plus_minus, 'center_percentage': self.center_percentage, 'defensive_box_plus_minus': self.defensive_box_plus_minus, 'defensive_rebound_percentage': self.defensive_rebound_percentage, 'defensive_rebounds': self.defensive_rebounds, + 'defensive_rebounds_per_poss': self.defensive_rebounds_per_poss, 'defensive_win_shares': self.defensive_win_shares, 'dunks': self.dunks, 'effective_field_goal_percentage': self.effective_field_goal_percentage, 'field_goal_attempts': self.field_goal_attempts, + 'field_goal_attempts_per_poss': self.field_goal_attempts_per_poss, 'field_goal_perc_sixteen_foot_plus_two_pointers': self.field_goal_perc_sixteen_foot_plus_two_pointers, 'field_goal_perc_ten_to_sixteen_feet': @@ -584,10 +605,13 @@ class index value, the dictionary should be regenerated every time the self.field_goal_perc_zero_to_three_feet, 'field_goal_percentage': self.field_goal_percentage, 'field_goals': self.field_goals, + 'field_goals_per_poss': self.field_goals_per_poss, 'free_throw_attempt_rate': self.free_throw_attempt_rate, 'free_throw_attempts': self.free_throw_attempts, + 'free_throw_attempts_per_poss': self.free_throw_attempts_per_poss, 'free_throw_percentage': self.free_throw_percentage, 'free_throws': self.free_throws, + 'free_throws_per_poss': self.free_throws_per_poss, 'games_played': self.games_played, 'games_started': self.games_started, 'half_court_heaves': self.half_court_heaves, @@ -601,6 +625,7 @@ class index value, the dictionary should be regenerated every time the 'offensive_fouls': self.offensive_fouls, 'offensive_rebound_percentage': self.offensive_rebound_percentage, 'offensive_rebounds': self.offensive_rebounds, + 'offensive_rebounds_per_poss': self.offensive_rebounds_per_poss, 'offensive_win_shares': self.offensive_win_shares, 'on_court_plus_minus': self.on_court_plus_minus, 'other_turnovers': self.other_turnovers, @@ -622,10 +647,12 @@ class index value, the dictionary should be regenerated every time the 'percentage_zero_to_three_footers': self.percentage_zero_to_three_footers, 'personal_fouls': self.personal_fouls, + 'personal_fouls_per_poss': self.personal_fouls_per_poss, 'player_efficiency_rating': self.player_efficiency_rating, 'player_id': self.player_id, 'point_guard_percentage': self.point_guard_percentage, 'points': self.points, + 'points_per_poss': self.points_per_poss, 'points_generated_by_assists': self.points_generated_by_assists, 'position': self.position, 'power_forward_percentage': self.power_forward_percentage, @@ -638,24 +665,32 @@ class index value, the dictionary should be regenerated every time the 'small_forward_percentage': self.small_forward_percentage, 'steal_percentage': self.steal_percentage, 'steals': self.steals, + 'steals_per_poss': self.steals_per_poss, 'take_fouls': self.take_fouls, 'team_abbreviation': self.team_abbreviation, 'three_point_attempt_rate': self.three_point_attempt_rate, 'three_point_attempts': self.three_point_attempts, + 'three_point_attempts_per_poss': + self.three_point_attempts_per_poss, 'three_point_percentage': self.three_point_percentage, 'three_point_shot_percentage_from_corner': self.three_point_shot_percentage_from_corner, 'three_pointers': self.three_pointers, 'three_pointers_assisted_percentage': self.three_pointers_assisted_percentage, + 'three_pointers_per_poss': self.three_pointers_per_poss, 'total_rebound_percentage': self.total_rebound_percentage, 'total_rebounds': self.total_rebounds, + 'total_rebounds_per_poss': self.total_rebounds_per_poss, 'true_shooting_percentage': self.true_shooting_percentage, 'turnover_percentage': self.turnover_percentage, 'turnovers': self.turnovers, + 'turnovers_per_poss': self.turnovers_per_poss, 'two_point_attempts': self.two_point_attempts, + 'two_point_attempts_per_poss': self.two_point_attempts_per_poss, 'two_point_percentage': self.two_point_percentage, 'two_pointers': self.two_pointers, + 'two_pointers_per_poss': self.two_pointers_per_poss, 'two_pointers_assisted_percentage': self.two_pointers_assisted_percentage, 'usage_percentage': self.usage_percentage, @@ -756,6 +791,38 @@ def games_started(self): """ return self._games_started + @_float_property_decorator + def field_goals_per_poss(self): + """ + Returns a ``float`` of the total number of field goals the player + scored per 100 posessions. + """ + return self._field_goals_per_poss + + @_float_property_decorator + def field_goal_attempts_per_poss(self): + """ + Returns a ``float`` of the total number of field goals the player + attempted per 100 posessions. + """ + return self._field_goal_attempts_per_poss + + @_float_property_decorator + def three_pointers_per_poss(self): + """ + Returns a ``float`` of the total number of three point field goals the + player made per 100 posessions. + """ + return self._three_pointers_per_poss + + @_float_property_decorator + def three_point_attempts_per_poss(self): + """ + Returns a ``float`` of the total number of three point field goals the + player attempted per 100 posessions. + """ + return self._three_point_attempts_per_poss + @_int_property_decorator def two_pointers(self): """ @@ -772,6 +839,22 @@ def two_point_attempts(self): """ return self._two_point_attempts + @_float_property_decorator + def two_pointers_per_poss(self): + """ + Returns a ``float`` of the total number of two point field goals the + player made per 100 posessions. + """ + return self._two_pointers_per_poss + + @_float_property_decorator + def two_point_attempts_per_poss(self): + """ + Returns a ``float`` of the total number of two point field goals the + player attempted per 100 posessions. + """ + return self._two_point_attempts_per_poss + @_float_property_decorator def two_point_percentage(self): """ @@ -780,6 +863,94 @@ def two_point_percentage(self): """ return self._two_point_percentage + @_float_property_decorator + def free_throws_per_poss(self): + """ + Returns a ``float`` of the total number of free throws the player made + per 100 posessions. + """ + return self._free_throws_per_poss + + @_float_property_decorator + def free_throw_attempts_per_poss(self): + """ + Returns a ``float`` of the total number of free throws the player + attempted per 100 posessions. + """ + return self._free_throw_attempts_per_poss + + @_float_property_decorator + def offensive_rebounds_per_poss(self): + """ + Returns a ``float`` of the total number of offensive rebounds the + player grabbed per 100 posessions. + """ + return self._offensive_rebounds_per_poss + + @_float_property_decorator + def defensive_rebounds_per_poss(self): + """ + Returns a ``float`` of the total number of defensive rebounds the + player grabbed per 100 posessions. + """ + return self._defensive_rebounds_per_poss + + @_float_property_decorator + def total_rebounds_per_poss(self): + """ + Returns a ``float`` of the total number of offensive and defensive + rebounds the player grabbed per 100 posessions. + """ + return self._total_rebounds_per_poss + + @_float_property_decorator + def assists_per_poss(self): + """ + Returns a ``float`` of the total number of assists the player tallied + per 100 posessions. + """ + return self._assists_per_poss + + @_float_property_decorator + def steals_per_poss(self): + """ + Returns a ``float`` of the total number of steals the player tallied + per 100 posessions. + """ + return self._steals_per_poss + + @_float_property_decorator + def blocks_per_poss(self): + """ + Returns a ``float`` of the total number of shots the player blocked + per 100 posessions. + """ + return self._blocks_per_poss + + @_float_property_decorator + def turnovers_per_poss(self): + """ + Returns a ``float`` of the total number of times the player turned the + ball over per 100 posessions. + """ + return self._turnovers_per_poss + + @_float_property_decorator + def personal_fouls_per_poss(self): + """ + Returns a ``float`` of the total number of personal fouls the player + committed per 100 posessions. + """ + return self._personal_fouls_per_poss + + @_float_property_decorator + def points_per_poss(self): + """ + Returns a ``float`` of the total number of points the player scored + per 100 posessions. + """ + return self._points_per_poss + @_float_property_decorator def player_efficiency_rating(self): """ diff --git a/tests/integration/roster/test_nba_roster.py b/tests/integration/roster/test_nba_roster.py index 6bf04aa5..eb8a9e05 100644 --- a/tests/integration/roster/test_nba_roster.py +++ b/tests/integration/roster/test_nba_roster.py @@ -157,7 +157,24 @@ def setup_method(self, *args, **kwargs): '2020-21': '$40,824,000', '2021-22': '$43,848,000', '2022-23': '$46,872,000' - } + }, + 'field_goals_per_poss': 10.0, + 'field_goal_attempts_per_poss': 22.5, + 'three_pointers_per_poss': 3.5, + 'three_point_attempts_per_poss': 9.7, + 'two_pointers_per_poss': 6.5, + 'two_point_attempts_per_poss': 12.8, + 'free_throws_per_poss': 10.4, + 'free_throw_attempts_per_poss': 12.2, + 'offensive_rebounds_per_poss': 1.1, + 'defensive_rebounds_per_poss': 6.4, + 'total_rebounds_per_poss': 7.5, + 'assists_per_poss': 8.9, + 'steals_per_poss': 2.2, + 'blocks_per_poss': 0.7, + 'turnovers_per_poss': 5.1, + 'personal_fouls_per_poss': 3.7, + 'points_per_poss': 33.9 } self.results_2018 = { @@ -259,7 +276,24 @@ def setup_method(self, *args, **kwargs): '2020-21': '$40,824,000', '2021-22': '$43,848,000', '2022-23': '$46,872,000' - } + }, + 'field_goals_per_poss': 12.6, + 'field_goal_attempts_per_poss': 27.9, + 'three_pointers_per_poss': 5.1, + 'three_point_attempts_per_poss': 13.9, + 'two_pointers_per_poss': 7.4, + 'two_point_attempts_per_poss': 14.0, + 'free_throws_per_poss': 12.0, + 'free_throw_attempts_per_poss': 14.0, + 'offensive_rebounds_per_poss': 0.8, + 'defensive_rebounds_per_poss': 6.7, + 'total_rebounds_per_poss': 7.5, + 'assists_per_poss': 12.2, + 'steals_per_poss': 2.4, + 'blocks_per_poss': 1.0, + 'turnovers_per_poss': 6.1, + 'personal_fouls_per_poss': 3.3, + 'points_per_poss': 42.3 } self.player = Player('hardeja01')