nbastatpy 0.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of nbastatpy might be problematic. Click here for more details.

nbastatpy/__init__.py ADDED
@@ -0,0 +1 @@
1
+ name = "nbastatpy"
nbastatpy/game.py ADDED
@@ -0,0 +1,153 @@
1
+ from typing import List
2
+
3
+ import nba_api.stats.endpoints as nba
4
+ import pandas as pd
5
+
6
+ from nbastatpy.utils import Formatter
7
+
8
+
9
+ class Game:
10
+ def __init__(self, game_id: str):
11
+ """This represents a game. Given an ID, you can get boxscore (and other) information through one of the 'get' methods
12
+
13
+ Args:
14
+ game_id (str): string with 10 digits
15
+ """
16
+ self.game_id = Formatter.format_game_id(game_id)
17
+
18
+ def get_boxscore(self) -> List[pd.DataFrame]:
19
+ """Gets traditional boxscore
20
+
21
+ Returns:
22
+ List[pd.DataFrame]: list of dataframes (players, starters/bench, team)
23
+ """
24
+ self.boxscore = nba.BoxScoreTraditionalV3(self.game_id).get_data_frames()
25
+ return self.boxscore
26
+
27
+ def get_advanced(self):
28
+ """
29
+ Retrieves the advanced box score data for the game.
30
+
31
+ Returns:
32
+ pandas.DataFrame: The advanced box score data for the game.
33
+ """
34
+ self.adv_box = nba.BoxScoreAdvancedV3(self.game_id).get_data_frames()
35
+ return self.adv_box
36
+
37
+ def get_defense(self):
38
+ """
39
+ Retrieves the defensive statistics for the game.
40
+
41
+ Returns:
42
+ def_box (pandas.DataFrame): DataFrame containing the defensive statistics.
43
+ """
44
+ self.def_box = nba.BoxScoreDefensiveV2(self.game_id).get_data_frames()
45
+ return self.def_box
46
+
47
+ def get_four_factors(self):
48
+ """
49
+ Retrieves the four factors data for the game.
50
+
51
+ Returns:
52
+ pandas.DataFrame: The four factors data for the game.
53
+ """
54
+ self.four_factors = nba.BoxScoreFourFactorsV3(self.game_id).get_data_frames()
55
+ return self.four_factors
56
+
57
+ def get_hustle(self) -> List[pd.DataFrame]:
58
+ """Gets hustle data for a given game
59
+
60
+ Returns:
61
+ List[pd.DataFrame]: list of two dataframes (players, teams)
62
+ """
63
+ self.hustle = nba.BoxScoreHustleV2(self.game_id).get_data_frames()
64
+ return self.hustle
65
+
66
+ def get_matchups(self):
67
+ """
68
+ Retrieves the matchups for the game.
69
+
70
+ Returns:
71
+ pandas.DataFrame: The matchups data for the game.
72
+ """
73
+ self.matchups = nba.BoxScoreMatchupsV3(self.game_id).get_data_frames()
74
+ return self.matchups
75
+
76
+ def get_misc(self):
77
+ """
78
+ Retrieves miscellaneous box score data for the game.
79
+
80
+ Returns:
81
+ pandas.DataFrame: The miscellaneous box score data.
82
+ """
83
+ self.misc = nba.BoxScoreMiscV3(self.game_id).get_data_frames()
84
+ return self.misc
85
+
86
+ def get_scoring(self):
87
+ """
88
+ Retrieves the scoring data for the game.
89
+
90
+ Returns:
91
+ pandas.DataFrame: The scoring data for the game.
92
+ """
93
+ self.scoring = nba.BoxScoreScoringV3(self.game_id).get_data_frames()
94
+ return self.scoring
95
+
96
+ def get_usage(self) -> List[pd.DataFrame]:
97
+ """Gets usage data for a given game
98
+
99
+ Returns:
100
+ List[pd.DataFrame]: list of two dataframes (players, teams)
101
+ """
102
+ self.usage = nba.BoxScoreUsageV3(self.game_id).get_data_frames()
103
+ return self.usage
104
+
105
+ def get_playertrack(self):
106
+ """
107
+ Retrieves the player tracking data for the game.
108
+
109
+ Returns:
110
+ playertrack (pandas.DataFrame): The player tracking data for the game.
111
+ """
112
+ self.playertrack = nba.BoxScorePlayerTrackV3(self.game_id).get_data_frames()
113
+ return self.playertrack
114
+
115
+ def get_rotations(self):
116
+ """
117
+ Retrieves the rotations data for the game.
118
+
119
+ Returns:
120
+ pandas.DataFrame: The rotations data for the game.
121
+ """
122
+ self.rotations = pd.concat(
123
+ nba.GameRotation(game_id=self.game_id).get_data_frames()
124
+ )
125
+ return self.rotations
126
+
127
+ def get_playbyplay(self) -> pd.DataFrame:
128
+ """
129
+ Retrieves the play-by-play data for the game.
130
+
131
+ Returns:
132
+ pd.DataFrame: The play-by-play data as a pandas DataFrame.
133
+ """
134
+ self.playbyplay = nba.PlayByPlayV3(self.game_id).get_data_frames()[0]
135
+ return self.playbyplay
136
+
137
+ def get_win_probability(self) -> pd.DataFrame:
138
+ """
139
+ Retrieves the win probability data for the game.
140
+
141
+ Returns:
142
+ pd.DataFrame: The win probability data as a pandas DataFrame.
143
+ """
144
+ self.win_probability = nba.WinProbabilityPBP(
145
+ game_id=self.game_id
146
+ ).get_data_frames()[0]
147
+ return self.win_probability
148
+
149
+
150
+ if __name__ == "__main__":
151
+ GAME_ID = "0022301148"
152
+ game = Game(game_id=GAME_ID)
153
+ print(game.get_win_probability())
nbastatpy/player.py ADDED
@@ -0,0 +1,507 @@
1
+ from io import BytesIO
2
+
3
+ import nba_api.stats.endpoints as nba
4
+ import pandas as pd
5
+ import requests
6
+ from bs4 import BeautifulSoup
7
+ from loguru import logger
8
+ from nba_api.stats.endpoints import leaguegamefinder
9
+ from nba_api.stats.static import players, teams
10
+ from PIL import Image
11
+
12
+ from nbastatpy.utils import Formatter, PlayTypes
13
+
14
+
15
+ class Player:
16
+ def __init__(
17
+ self,
18
+ player: str,
19
+ season_year: str = None,
20
+ playoffs: bool = False,
21
+ permode: str = "PERGAME",
22
+ ):
23
+ """
24
+ Initializes a Player object.
25
+
26
+ Args:
27
+ player (str): The name or ID of the player.
28
+ season_year (str, optional): The season year. Defaults to None.
29
+ playoffs (bool, optional): Whether to retrieve playoff data. Defaults to False.
30
+ permode (str, optional): The per mode for the player's stats. Defaults to "PERGAME".
31
+ """
32
+ self.permode = PlayTypes.PERMODE[
33
+ permode.replace("_", "").replace("-", "").upper()
34
+ ]
35
+ self.name_meta = players.find_player_by_id(player)
36
+ if self.name_meta:
37
+ self.name_meta = [self.name_meta]
38
+ else:
39
+ self.name_meta = players.find_players_by_full_name(player)
40
+
41
+ if not self.name_meta:
42
+ raise ValueError(f"{player} not found")
43
+ if len(self.name_meta) > 1:
44
+ logger.warning(
45
+ f"Multiple players returned. Using: {self.name_meta[0]['full_name']}"
46
+ )
47
+ self.id = self.name_meta[0]["id"]
48
+ self.name = self.name_meta[0]["full_name"]
49
+ self.first_name = self.name_meta[0]["first_name"]
50
+ self.last_name = self.name_meta[0]["last_name"]
51
+ self.is_active = self.name_meta[0]["is_active"]
52
+
53
+ if season_year:
54
+ self.season_year = season_year
55
+ else:
56
+ self.season_year = Formatter.get_current_season_year()
57
+ self.season = Formatter.format_season(self.season_year)
58
+ self.season_type = "Regular Season"
59
+ if playoffs:
60
+ self.season_type = "Playoffs"
61
+
62
+
63
+ def get_common_info(self) -> pd.DataFrame:
64
+ """Gets common info like height, weight, draft_year, etc. and sets as class attr
65
+
66
+ Returns:
67
+ pd.DataFrame: A DataFrame containing the common information of the player.
68
+ """
69
+ self.common_info = (
70
+ nba.CommonPlayerInfo(self.id).get_data_frames()[0].iloc[0].to_dict()
71
+ )
72
+
73
+ for attr_name, value in self.common_info.items():
74
+ setattr(self, attr_name.lower(), self.common_info.get(attr_name, None))
75
+
76
+ return self.common_info
77
+
78
+ def get_salary(self) -> pd.DataFrame:
79
+ """
80
+ Retrieves the salary information for a player from hoopshype.com.
81
+
82
+ Returns:
83
+ pd.DataFrame: A DataFrame containing the salary information for the player.
84
+ """
85
+ salary_url = f"https://hoopshype.com/player/{self.first_name}-{self.last_name}/salary/".lower()
86
+ result = requests.get(salary_url)
87
+ soup = BeautifulSoup(result.content, features="html.parser")
88
+ tables = soup.find_all("table")
89
+ if len(tables) > 1:
90
+ # Get the table rows
91
+ rows = [[cell.text.strip() for cell in row.find_all('td')] for row in tables[0].find_all('tr')]
92
+
93
+ rows2 = [[cell.text.strip() for cell in row.find_all('td')] for row in tables[1].find_all('tr')]
94
+
95
+ projected = pd.DataFrame(rows[1:], columns=rows[0])
96
+ projected["Team"] = projected.columns[1]
97
+ projected = projected.rename(columns={projected.columns[1]:"Salary"})
98
+ projected["Salary_Type"] = "Projected"
99
+
100
+ historical = pd.DataFrame(rows2[1:], columns=rows2[0])
101
+ historical["Salary_Type"] = "Historical"
102
+
103
+ self.salary_df = pd.concat([projected, historical])
104
+
105
+ else:
106
+ # Get the table rows
107
+ rows = [[cell.text.strip() for cell in row.find_all('td')] for row in tables[0].find_all('tr')]
108
+ self.salary_df = pd.DataFrame(rows[1:], columns=rows[0])
109
+
110
+ return self.salary_df
111
+
112
+ def get_headshot(self):
113
+ """
114
+ Retrieves the headshot image of the player.
115
+
116
+ Returns:
117
+ PIL.Image.Image: The headshot image of the player.
118
+ """
119
+ pic_url = f"https://cdn.nba.com/headshots/nba/latest/1040x760/{self.id}.png"
120
+ pic = requests.get(pic_url)
121
+ self.headshot = Image.open(BytesIO(pic.content))
122
+ return self.headshot
123
+
124
+ def get_season_career_totals(self) -> pd.DataFrame:
125
+ """Gets seasons and career data
126
+
127
+ Returns:
128
+ pd.DataFrame: 2 dataframes, season totals and career
129
+ """
130
+ df_list = nba.PlayerCareerStats(player_id=self.id).get_data_frames()
131
+ self.career_totals = df_list[1]
132
+ self.season_totals = df_list[0]
133
+ return self.season_totals, self.career_totals
134
+
135
+ def get_splits(self) -> pd.DataFrame:
136
+ """Gets all splits for a given season"""
137
+
138
+ self.splits_data = pd.concat(
139
+ nba.PlayerDashboardByGeneralSplits(
140
+ self.id,
141
+ season=self.season,
142
+ season_type_playoffs=self.season_type,
143
+ per_mode_detailed=self.permode,
144
+ ).get_data_frames()
145
+ )
146
+
147
+ return self.splits_data
148
+
149
+ def get_game_splits(self) -> pd.DataFrame:
150
+ """Gets splits for the game (halftime, quarter, etc.)
151
+
152
+ Returns:
153
+ pd.DataFrame: dataframe with all game splits for a season
154
+ """
155
+
156
+ self.game_splits = pd.concat(
157
+ nba.PlayerDashboardByGameSplits(
158
+ self.id,
159
+ season=self.season,
160
+ season_type_playoffs=self.season_type,
161
+ per_mode_detailed=self.permode,
162
+ ).get_data_frames()
163
+ )
164
+ return self.game_splits
165
+
166
+ def get_shooting_splits(self) -> pd.DataFrame:
167
+
168
+ self.shooting_splits = pd.concat(
169
+ nba.PlayerDashboardByShootingSplits(
170
+ self.id,
171
+ season=self.season,
172
+ season_type_playoffs=self.season_type,
173
+ per_mode_detailed=self.permode,
174
+ ).get_data_frames()
175
+ )
176
+ return self.shooting_splits
177
+
178
+ def get_combine_stats(self) -> pd.DataFrame:
179
+ """Gets draft combine data for the player's draft year"""
180
+ if not hasattr(
181
+ self, "draft_year"
182
+ ): # Check if we know the player's draft year yet
183
+ self.get_common_info()
184
+
185
+ self.combine_stats = nba.DraftCombineStats(
186
+ season_all_time=self.draft_year
187
+ ).get_data_frames()[0]
188
+
189
+ self.combine_nonstationary_shooting = nba.DraftCombineNonStationaryShooting(
190
+ season_year=self.draft_year
191
+ ).get_data_frames()[0]
192
+
193
+ self.combine_spot_shooting = nba.DraftCombineSpotShooting(
194
+ season_year=self.draft_year
195
+ ).get_data_frames()[0]
196
+
197
+ return [
198
+ self.combine_stats,
199
+ self.combine_nonstationary_shooting,
200
+ self.combine_spot_shooting,
201
+ ]
202
+
203
+ def get_awards(self) -> pd.DataFrame:
204
+ """Gets any awards won by the player.
205
+
206
+ Returns:
207
+ pd.DataFrame: A DataFrame containing the player's awards.
208
+ """
209
+ self.awards = nba.PlayerAwards(self.id).get_data_frames()[0]
210
+ return self.awards
211
+
212
+ def get_games_boxscore(self) -> pd.DataFrame:
213
+ """
214
+ Retrieves the boxscore data for the games played by the player.
215
+
216
+ Returns:
217
+ pd.DataFrame: The boxscore data for the player's games.
218
+ """
219
+ self.games_boxscore = leaguegamefinder.LeagueGameFinder(
220
+ player_id_nullable=self.id,
221
+ season_nullable=self.season,
222
+ season_type_nullable=self.season_type,
223
+ ).get_data_frames()[0]
224
+ return self.games_boxscore
225
+
226
+ def get_matchups(self, defense: bool = False) -> pd.DataFrame:
227
+ """
228
+ Retrieves the matchups data for the player.
229
+
230
+ Args:
231
+ defense (bool, optional): If True, retrieves the defensive matchups data.
232
+ If False, retrieves the offensive matchups data. Defaults to False.
233
+
234
+ Returns:
235
+ pd.DataFrame: The matchups data for the player.
236
+ """
237
+ if defense:
238
+ self.matchups = nba.LeagueSeasonMatchups(
239
+ def_player_id_nullable=self.id,
240
+ season=self.season,
241
+ season_type_playoffs=self.season_type,
242
+ per_mode_simple=self.permode,
243
+ ).get_data_frames()[0]
244
+ else:
245
+ self.matchups = nba.LeagueSeasonMatchups(
246
+ off_player_id_nullable=self.id,
247
+ season=self.season,
248
+ season_type_playoffs=self.season_type,
249
+ per_mode_simple=self.permode,
250
+ ).get_data_frames()[0]
251
+ return self.matchups
252
+
253
+ def get_clutch(self) -> pd.DataFrame:
254
+ """Gets clutch data for multiple clutch segments
255
+
256
+ Returns:
257
+ pd.DataFrame: dataframe with a given year of clutch segments
258
+ """
259
+ self.clutch = pd.concat(
260
+ nba.PlayerDashboardByClutch(
261
+ player_id=self.id,
262
+ season=self.season,
263
+ season_type_playoffs=self.season_type,
264
+ per_mode_detailed=self.permode,
265
+ ).get_data_frames()
266
+ )
267
+ return self.clutch
268
+
269
+ def get_pt_pass(self) -> pd.DataFrame:
270
+ """
271
+ Retrieves the passing statistics for the player.
272
+
273
+ Returns:
274
+ pd.DataFrame: A DataFrame containing the passing statistics for the player.
275
+ """
276
+ if not hasattr(self, "season_totals"):
277
+ logger.info("Getting Teams")
278
+ teams = self.get_season_career_totals()[0]
279
+ else:
280
+ teams = self.season_totals.copy()
281
+
282
+ teams = teams[(teams["SEASON_ID"] == self.season) & (teams["TEAM_ID"] != 0)][
283
+ "TEAM_ID"
284
+ ].tolist()
285
+
286
+ if len(teams) > 1:
287
+ self.pt_pass = []
288
+ for team in teams:
289
+ self.pt_pass.append(
290
+ pd.concat(
291
+ nba.PlayerDashPtPass(
292
+ player_id=self.id,
293
+ team_id=team,
294
+ season=self.season,
295
+ season_type_all_star=self.season_type,
296
+ per_mode_simple=self.permode,
297
+ ).get_data_frames()
298
+ )
299
+ )
300
+
301
+ self.pt_pass = pd.concat(self.pt_pass)
302
+
303
+ else:
304
+ self.pt_pass = pd.concat(
305
+ nba.PlayerDashPtPass(
306
+ player_id=self.id,
307
+ team_id=teams[0],
308
+ season=self.season,
309
+ season_type_all_star=self.season_type,
310
+ per_mode_simple=self.permode,
311
+ ).get_data_frames()
312
+ )
313
+
314
+ group_cols = ["PASS_TO", "PASS_FROM"]
315
+ self.pt_pass["GROUP_SET"] = self.pt_pass[group_cols].apply(
316
+ Formatter.combine_strings, axis=1
317
+ )
318
+ self.pt_pass = self.pt_pass.drop(columns=group_cols)
319
+
320
+ return self.pt_pass
321
+
322
+ def get_pt_reb(self) -> pd.DataFrame:
323
+ """
324
+ Retrieves the rebounds data for the player.
325
+
326
+ Returns:
327
+ pd.DataFrame: A DataFrame containing the rebounds data.
328
+ """
329
+ if not hasattr(self, "season_totals"):
330
+ logger.info("Getting Teams")
331
+ teams = self.get_season_career_totals()[0]
332
+ else:
333
+ teams = self.season_totals.copy()
334
+
335
+ teams = teams[(teams["SEASON_ID"] == self.season) & (teams["TEAM_ID"] != 0)][
336
+ "TEAM_ID"
337
+ ].tolist()
338
+
339
+ if len(teams) > 1:
340
+ self.pt_reb = []
341
+ for team in teams:
342
+ self.pt_reb.append(
343
+ pd.concat(
344
+ nba.PlayerDashPtReb(
345
+ player_id=self.id,
346
+ team_id=team,
347
+ season=self.season,
348
+ season_type_all_star=self.season_type,
349
+ per_mode_simple=self.permode,
350
+ ).get_data_frames()
351
+ )
352
+ )
353
+
354
+ self.pt_reb = pd.concat(self.pt_reb)
355
+
356
+ else:
357
+ self.pt_reb = pd.concat(
358
+ nba.PlayerDashPtReb(
359
+ player_id=self.id,
360
+ team_id=teams[0],
361
+ season=self.season,
362
+ season_type_all_star=self.season_type,
363
+ per_mode_simple=self.permode,
364
+ ).get_data_frames()
365
+ )
366
+
367
+ group_cols = [
368
+ "OVERALL",
369
+ "SHOT_TYPE_RANGE",
370
+ "REB_NUM_CONTESTING_RANGE",
371
+ "SHOT_DIST_RANGE",
372
+ "REB_DIST_RANGE",
373
+ ]
374
+ self.pt_reb["GROUP_SET"] = self.pt_reb[group_cols].apply(
375
+ Formatter.combine_strings, axis=1
376
+ )
377
+ self.pt_reb = self.pt_reb.drop(columns=group_cols)
378
+
379
+ return self.pt_reb
380
+
381
+ def get_defense_against_team(self, opposing_team: str) -> pd.DataFrame:
382
+ """
383
+ Retrieves the defensive statistics of the player against a specific team.
384
+
385
+ Args:
386
+ opposing_team (str): The abbreviation of the opposing team.
387
+
388
+ Returns:
389
+ pd.DataFrame: A DataFrame containing the defensive statistics of the player against the specified team.
390
+ """
391
+ opp_tm_id = teams.find_team_by_abbreviation(opposing_team)["id"]
392
+
393
+ self.defense_against_team = nba.PlayerDashPtShotDefend(
394
+ player_id=self.id,
395
+ team_id=opp_tm_id,
396
+ season=self.season,
397
+ season_type_all_star=self.season_type,
398
+ per_mode_simple=self.permode,
399
+ ).get_data_frames()[0]
400
+ return self.defense_against_team
401
+
402
+ def get_pt_shots(self) -> pd.DataFrame:
403
+ """
404
+ Retrieves the shots data for the player.
405
+
406
+ Returns:
407
+ pd.DataFrame: The shots data for the player.
408
+ """
409
+ if not hasattr(self, "season_totals"):
410
+ logger.info("Getting Teams")
411
+ teams = self.get_season_career_totals()[0]
412
+ else:
413
+ teams = self.season_totals.copy()
414
+
415
+ teams = teams[(teams["SEASON_ID"] == self.season) & (teams["TEAM_ID"] != 0)][
416
+ "TEAM_ID"
417
+ ].tolist()
418
+
419
+ if len(teams) > 1:
420
+ self.pt_shots = []
421
+ for team in teams:
422
+ self.pt_shots.append(
423
+ pd.concat(
424
+ nba.PlayerDashPtShots(
425
+ player_id=self.id,
426
+ team_id=team,
427
+ season=self.season,
428
+ season_type_all_star=self.season_type,
429
+ per_mode_simple=self.permode,
430
+ ).get_data_frames()
431
+ )
432
+ )
433
+
434
+ self.pt_shots = pd.concat(self.pt_shots)
435
+
436
+ else:
437
+ self.pt_shots = pd.concat(
438
+ nba.PlayerDashPtShots(
439
+ player_id=self.id,
440
+ team_id=teams[0],
441
+ season=self.season,
442
+ season_type_all_star=self.season_type,
443
+ per_mode_simple=self.permode,
444
+ ).get_data_frames()
445
+ )
446
+
447
+ group_cols = [
448
+ "SHOT_TYPE",
449
+ "SHOT_CLOCK_RANGE",
450
+ "DRIBBLE_RANGE",
451
+ "CLOSE_DEF_DIST_RANGE",
452
+ "TOUCH_TIME_RANGE",
453
+ ]
454
+ self.pt_shots["GROUP_SET"] = self.pt_shots[group_cols].apply(
455
+ Formatter.combine_strings, axis=1
456
+ )
457
+ self.pt_shots = self.pt_shots.drop(columns=group_cols)
458
+
459
+ return self.pt_shots
460
+
461
+ def get_shot_chart(self) -> pd.DataFrame:
462
+ """
463
+ Retrieves the shot chart data for the player.
464
+
465
+ Returns:
466
+ pd.DataFrame: The shot chart data for the player.
467
+ """
468
+ if not hasattr(self, "season_totals"):
469
+ logger.info("Getting Teams")
470
+ teams = self.get_season_career_totals()[0]
471
+ else:
472
+ teams = self.season_totals.copy()
473
+
474
+ teams = teams[(teams["SEASON_ID"] == self.season) & (teams["TEAM_ID"] != 0)][
475
+ "TEAM_ID"
476
+ ].tolist()
477
+
478
+ if len(teams) > 1:
479
+ self.shot_chart = []
480
+ for team in teams:
481
+ self.shot_chart.append(
482
+ nba.ShotChartDetail(
483
+ player_id=self.id,
484
+ team_id=team,
485
+ season_nullable=self.season,
486
+ season_type_all_star=self.season_type,
487
+ ).get_data_frames()[0]
488
+ )
489
+
490
+ self.shot_chart = pd.concat(self.shot_chart)
491
+
492
+ else:
493
+ self.shot_chart = nba.ShotChartDetail(
494
+ player_id=self.id,
495
+ team_id=teams[0],
496
+ season_nullable=self.season,
497
+ season_type_all_star=self.season_type,
498
+ ).get_data_frames()[0]
499
+
500
+ return self.shot_chart
501
+
502
+
503
+ if __name__ == "__main__":
504
+ player_name = "giannis"
505
+ player_seas = "2020"
506
+ player = Player(player_name, player_seas)
507
+ print(player.get_salary())