Skip to content

Commit

Permalink
Add Per 100 Possessions stats to NBA
Browse files Browse the repository at this point in the history
In addition to the overall career and season stats for NBA players,
stats per 100 possessions should also be included to represent each
player's efficiency while on the court. This is especially crucial to
compare players that might have vastly differing amounts of playtime.

Signed-Off-By: Robert Clark <robdclark@outlook.com>
  • Loading branch information
roclark committed Sep 25, 2020
1 parent 2ec7ef7 commit 1c4fcd3
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 5 deletions.
19 changes: 18 additions & 1 deletion sportsreference/nba/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
175 changes: 173 additions & 2 deletions sportsreference/nba/roster.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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':
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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):
"""
Expand All @@ -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):
"""
Expand All @@ -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
during the season.
"""
return self._points_per_poss

@_float_property_decorator
def player_efficiency_rating(self):
"""
Expand Down
38 changes: 36 additions & 2 deletions tests/integration/roster/test_nba_roster.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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')
Expand Down

0 comments on commit 1c4fcd3

Please sign in to comment.