wsba-hockey 1.0.0__tar.gz → 1.0.1__tar.gz
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.
- {wsba_hockey-1.0.0/src/wsba_hockey.egg-info → wsba_hockey-1.0.1}/PKG-INFO +2 -2
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/README.md +1 -1
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/pyproject.toml +1 -1
- wsba_hockey-1.0.1/src/wsba_hockey/tools/plotting.py +137 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey/tools/scraping.py +2 -3
- wsba_hockey-1.0.1/src/wsba_hockey/workspace.py +67 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey/wsba_main.py +10 -31
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1/src/wsba_hockey.egg-info}/PKG-INFO +2 -2
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey.egg-info/SOURCES.txt +1 -0
- wsba_hockey-1.0.0/src/wsba_hockey/tools/plotting.py +0 -113
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/LICENSE +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/setup.cfg +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey/__init__.py +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey/stats/calculate_viz/shot_impact.py +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey/tools/__init__.py +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey/tools/agg.py +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey/tools/archive/old_scraping.py +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey/tools/utils/__init__.py +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey/tools/utils/config.py +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey/tools/utils/save_pages.py +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey/tools/utils/shared.py +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey/tools/xg_model.py +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey.egg-info/dependency_links.txt +0 -0
- {wsba_hockey-1.0.0 → wsba_hockey-1.0.1}/src/wsba_hockey.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: wsba_hockey
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.1
|
4
4
|
Summary: WeakSide Breakout's complete Python package of access to hockey data, primairly including the scraping of National Hockey League schedule, play-by-play, and shifts information.
|
5
5
|
Author-email: Owen Singh <owenbksingh@gmail.com>
|
6
6
|
Project-URL: Homepage, https://github.com/owensingh38/wsba_hockey/
|
@@ -78,7 +78,7 @@ skater_dict = {
|
|
78
78
|
}
|
79
79
|
pbp = wsba.nhl_scrape_season('20212022',remove=[], local = True)
|
80
80
|
|
81
|
-
wsba.
|
81
|
+
wsba.nhl_plot_skaters_shots(pbp,skater_dict,['5v5'],onice='for',legend=True)
|
82
82
|
wsba.nhl_plot_games(pbp,legend=True)
|
83
83
|
```
|
84
84
|
|
@@ -63,7 +63,7 @@ skater_dict = {
|
|
63
63
|
}
|
64
64
|
pbp = wsba.nhl_scrape_season('20212022',remove=[], local = True)
|
65
65
|
|
66
|
-
wsba.
|
66
|
+
wsba.nhl_plot_skaters_shots(pbp,skater_dict,['5v5'],onice='for',legend=True)
|
67
67
|
wsba.nhl_plot_games(pbp,legend=True)
|
68
68
|
```
|
69
69
|
|
@@ -0,0 +1,137 @@
|
|
1
|
+
import matplotlib.pyplot as plt
|
2
|
+
import matplotlib.image as img
|
3
|
+
import numpy as np
|
4
|
+
import pandas as pd
|
5
|
+
from scipy.interpolate import griddata
|
6
|
+
from scipy.ndimage import gaussian_filter
|
7
|
+
from .xg_model import *
|
8
|
+
from hockey_rink import NHLRink
|
9
|
+
from hockey_rink import CircularImage
|
10
|
+
|
11
|
+
event_markers = {
|
12
|
+
'faceoff':'X',
|
13
|
+
'hit':'P',
|
14
|
+
'blocked-shot':'v',
|
15
|
+
'missed-shot':'H',
|
16
|
+
'shot-on-goal':'D',
|
17
|
+
'goal':'o',
|
18
|
+
'giveaway':'1',
|
19
|
+
'takeaway':'2',
|
20
|
+
}
|
21
|
+
|
22
|
+
def wsba_rink(display_range='offense',rotation = 0):
|
23
|
+
img = 'tools/utils/wsba.png'
|
24
|
+
rink = NHLRink(center_logo={
|
25
|
+
"feature_class": CircularImage,
|
26
|
+
"image_path": img,
|
27
|
+
"length": 25, "width": 25,
|
28
|
+
"x": 0, "y": 0,
|
29
|
+
"radius": 14,
|
30
|
+
"zorder": 11,
|
31
|
+
}
|
32
|
+
)
|
33
|
+
rink.draw(
|
34
|
+
display_range=display_range,
|
35
|
+
rotation=rotation,
|
36
|
+
despine=True
|
37
|
+
)
|
38
|
+
|
39
|
+
def prep_plot_data(pbp,events,strengths,marker_dict=event_markers,xg='moneypuck'):
|
40
|
+
try: pbp['xG']
|
41
|
+
except:
|
42
|
+
if xg == 'wsba':
|
43
|
+
pbp = wsba_xG(pbp)
|
44
|
+
else:
|
45
|
+
pbp = moneypuck_xG(pbp)
|
46
|
+
pbp['xG'] = np.where(pbp['xG'].isna(),0,pbp['xG'])
|
47
|
+
|
48
|
+
pbp['WSBA'] = pbp['event_player_1_name']+pbp['season'].astype(str)+pbp['event_team_abbr']
|
49
|
+
|
50
|
+
pbp['x_plot'] = pbp['y_fixed']*-1
|
51
|
+
pbp['y_plot'] = pbp['x_fixed']
|
52
|
+
|
53
|
+
pbp['home_on_ice'] = pbp['home_on_1'].astype(str) + ";" + pbp['home_on_2'].astype(str) + ";" + pbp['home_on_3'].astype(str) + ";" + pbp['home_on_4'].astype(str) + ";" + pbp['home_on_5'].astype(str) + ";" + pbp['home_on_6'].astype(str)
|
54
|
+
pbp['away_on_ice'] = pbp['away_on_1'].astype(str) + ";" + pbp['away_on_2'].astype(str) + ";" + pbp['away_on_3'].astype(str) + ";" + pbp['away_on_4'].astype(str) + ";" + pbp['away_on_5'].astype(str) + ";" + pbp['away_on_6'].astype(str)
|
55
|
+
|
56
|
+
pbp['onice_for'] = np.where(pbp['home_team_abbr']==pbp['event_team_abbr'],pbp['home_on_ice'],pbp['away_on_ice'])
|
57
|
+
pbp['onice_against'] = np.where(pbp['away_team_abbr']==pbp['event_team_abbr'],pbp['home_on_ice'],pbp['away_on_ice'])
|
58
|
+
|
59
|
+
pbp['size'] = np.where(pbp['xG']<=0,40,pbp['xG']*400)
|
60
|
+
pbp['marker'] = pbp['event_type'].replace(marker_dict)
|
61
|
+
|
62
|
+
pbp = pbp.loc[(pbp['event_type'].isin(events))&
|
63
|
+
(pbp['event_distance']<=89)&
|
64
|
+
(pbp['x_fixed']<=89)]
|
65
|
+
|
66
|
+
if strengths != 'all':
|
67
|
+
pbp = pbp.loc[(pbp['strength_state'].isin(strengths))]
|
68
|
+
|
69
|
+
return pbp
|
70
|
+
|
71
|
+
def league_shots(pbp,events,strengths):
|
72
|
+
pbp = prep_plot_data(pbp,events,strengths)
|
73
|
+
|
74
|
+
print(pbp[['event_player_1_name','xG','x_plot','y_plot']].head(10))
|
75
|
+
|
76
|
+
[x,y] = np.round(np.meshgrid(np.linspace(-42.5,42.5,85),np.linspace(0,100,100)))
|
77
|
+
xgoals = griddata((pbp[f'x_plot'],pbp[f'y_plot']),pbp['xG'],(x,y),method='cubic',fill_value=0)
|
78
|
+
xgoals_smooth = gaussian_filter(xgoals,sigma = 3)
|
79
|
+
|
80
|
+
return xgoals_smooth
|
81
|
+
|
82
|
+
def plot_skater_shots(pbp, player, season, team, strengths, title = None, marker_dict=event_markers, onice='for', legend=False,xg='moneypuck'):
|
83
|
+
shots = ['missed-shot','shot-on-goal','goal']
|
84
|
+
pbp = prep_plot_data(pbp,shots,strengths,marker_dict,xg)
|
85
|
+
pbp = pbp.loc[(pbp['season'].astype(str)==season)&((pbp['away_team_abbr']==team)|(pbp['home_team_abbr']==team))]
|
86
|
+
|
87
|
+
team_data = pd.read_csv('teaminfo/nhl_teaminfo.csv')
|
88
|
+
team_color = list(team_data.loc[team_data['WSBA']==f'{team}{season}','Primary Color'])[0]
|
89
|
+
team_color_2nd = list(team_data.loc[team_data['WSBA']==f'{team}{season}','Secondary Color'])[0]
|
90
|
+
|
91
|
+
if onice in ['for','against']:
|
92
|
+
skater = pbp.loc[(pbp[f'onice_{onice}'].str.contains(player.upper()))]
|
93
|
+
skater['color'] = np.where(skater['event_player_1_name']==player.upper(),team_color,team_color_2nd)
|
94
|
+
|
95
|
+
else:
|
96
|
+
skater = pbp.loc[pbp['event_player_1_name']==player.upper()]
|
97
|
+
skater['color'] = team_color
|
98
|
+
|
99
|
+
fig, ax = plt.subplots()
|
100
|
+
wsba_rink(rotation=90)
|
101
|
+
|
102
|
+
for event in shots:
|
103
|
+
plays = skater.loc[skater['event_type']==event]
|
104
|
+
ax.scatter(plays['x_plot'],plays['y_plot'],plays['size'],plays['color'],marker=event_markers[event],label=event,zorder=5)
|
105
|
+
|
106
|
+
ax.set_title(title) if title else ''
|
107
|
+
ax.legend().set_visible(legend)
|
108
|
+
ax.legend().set_zorder(1000)
|
109
|
+
|
110
|
+
return fig
|
111
|
+
|
112
|
+
def plot_game_events(pbp,game_id,events,strengths,marker_dict=event_markers,legend=False,xg='moneypuck'):
|
113
|
+
pbp = prep_plot_data(pbp,events,strengths,marker_dict,xg)
|
114
|
+
pbp = pbp.loc[pbp['game_id'].astype(str)==game_id]
|
115
|
+
|
116
|
+
away_abbr = list(pbp['away_team_abbr'])[0]
|
117
|
+
home_abbr = list(pbp['home_team_abbr'])[0]
|
118
|
+
date = list(pbp['game_date'])[0]
|
119
|
+
season = f'{game_id[0:4]}{int(game_id[0:4])+1}'
|
120
|
+
|
121
|
+
team_data = pd.read_csv('teaminfo/nhl_teaminfo.csv')
|
122
|
+
away_color = list(team_data.loc[team_data['WSBA']==f'{away_abbr}{season}','Primary Color'])[0]
|
123
|
+
home_color = list(team_data.loc[team_data['WSBA']==f'{home_abbr}{season}','Primary Color'])[0]
|
124
|
+
|
125
|
+
pbp['color'] = np.where(pbp['event_team_abbr']==away_abbr,away_color,home_color)
|
126
|
+
|
127
|
+
fig, ax = plt.subplots()
|
128
|
+
wsba_rink(display_range='full')
|
129
|
+
|
130
|
+
for event in events:
|
131
|
+
plays = pbp.loc[pbp['event_type']==event]
|
132
|
+
ax.scatter(plays['x_adj'],plays['y_adj'],plays['size'],plays['color'],marker=event_markers[event],label=event,zorder=5)
|
133
|
+
|
134
|
+
ax.set_title(f'{away_abbr} @ {home_abbr} - {date}')
|
135
|
+
ax.legend(bbox_to_anchor =(0.5,-0.35), loc='lower center',ncol=1).set_visible(legend)
|
136
|
+
|
137
|
+
return fig
|
@@ -337,7 +337,6 @@ def parse_espn(date,away,home):
|
|
337
337
|
coords_df = coords_df.assign(
|
338
338
|
coords_x = coords_df[~pd.isna(coords_df.coordinate)].coordinate.apply(lambda x: x['x']).astype(int),
|
339
339
|
coords_y = coords_df[~pd.isna(coords_df.coordinate)].coordinate.apply(lambda y: y['y']).astype(int),
|
340
|
-
event_player_1_name = coords_df[~pd.isna(coords_df.athlete)]['athlete'].apply(lambda x: x['name'])
|
341
340
|
)
|
342
341
|
|
343
342
|
#Combine
|
@@ -356,7 +355,7 @@ def parse_espn(date,away,home):
|
|
356
355
|
coords_y = np.where((pd.isna(espn_events.coords_x)) & (pd.isna(espn_events.coords_y)) &
|
357
356
|
(espn_events.event_type=='Face Off'), 0, espn_events.coords_y))
|
358
357
|
|
359
|
-
espn_events = espn_events[(~pd.isna(espn_events.coords_x)) & (~pd.isna(espn_events.coords_y))
|
358
|
+
espn_events = espn_events[(~pd.isna(espn_events.coords_x)) & (~pd.isna(espn_events.coords_y))]
|
360
359
|
|
361
360
|
espn_events = espn_events.assign(
|
362
361
|
coords_x = espn_events.coords_x.astype(int),
|
@@ -711,7 +710,7 @@ def combine_pbp(info):
|
|
711
710
|
#Route data combining - json if season is after 2009-2010:
|
712
711
|
if str(info['season']) in ['20052006','20062007','20072008','20082009','20092010']:
|
713
712
|
#ESPN x HTML
|
714
|
-
espn_pbp = parse_espn(str(info['game_date']),info['away_team_abbr'],info['home_team_abbr']).rename(columns={'coords_x':'x',"coords_y":'y'})
|
713
|
+
espn_pbp = parse_espn(str(info['game_date']),info['away_team_abbr'],info['home_team_abbr']).rename(columns={'coords_x':'x',"coords_y":'y'})
|
715
714
|
merge_col = ['period','seconds_elapsed','event_type','event_team_abbr']
|
716
715
|
|
717
716
|
df = pd.merge(html_pbp,espn_pbp,how='left',on=merge_col)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import pandas as pd
|
3
|
+
import matplotlib
|
4
|
+
import matplotlib.pyplot as plt
|
5
|
+
import wsba_hockey as wsba
|
6
|
+
|
7
|
+
season_load = wsba.repo_load_seasons()
|
8
|
+
|
9
|
+
def workspace(seasons,type):
|
10
|
+
if type == 'pbp':
|
11
|
+
#Scrape pbp
|
12
|
+
errors=[]
|
13
|
+
for season in seasons:
|
14
|
+
data = wsba.nhl_scrape_season(season,remove=[],local=True,errors=True)
|
15
|
+
errors.append(data['errors'])
|
16
|
+
data['pbp'].to_csv(f'pbp/nhl_pbp_{season}.csv',index=False)
|
17
|
+
print(f'Errors: {errors}')
|
18
|
+
|
19
|
+
elif type == 'convert':
|
20
|
+
for season in seasons[6:12]:
|
21
|
+
data = pd.read_csv(f"pbp/nhl_pbp_{season}.csv")
|
22
|
+
data = wsba.wsba_main.moneypuck_xG(data)
|
23
|
+
data.to_parquet(f'pbp/parquet/nhl_pbp_{season}.parquet',index=False)
|
24
|
+
|
25
|
+
elif type == 'standings':
|
26
|
+
#Scrape standings
|
27
|
+
stand = [wsba.nhl_scrape_standings(season) for season in season]
|
28
|
+
pd.concat(stand).to_csv('teaminfo/nhl_standings.csv',index=False)
|
29
|
+
|
30
|
+
elif type == 'stats':
|
31
|
+
#Stats building
|
32
|
+
stats = []
|
33
|
+
for season in seasons[6:18]:
|
34
|
+
for group in ['skater','team']:
|
35
|
+
pbp = pd.read_parquet(f'pbp/parquet/nhl_pbp_{season}.parquet')
|
36
|
+
stat = wsba.nhl_calculate_stats(pbp,group,[2],['5v5'],shot_impact=True)
|
37
|
+
stat.to_csv(f'stats/{group}/wsba_nhl_{season}_{group}.csv',index=False)
|
38
|
+
stats.append(stat)
|
39
|
+
pd.concat(stats).to_csv(f'stats/db/wsba_nhl_{group}_db.csv',index=False)
|
40
|
+
else:
|
41
|
+
print('Nothing here.')
|
42
|
+
|
43
|
+
pbp = pd.read_parquet('pbp/parquet/nhl_pbp_20242025.parquet')
|
44
|
+
|
45
|
+
skaters={}
|
46
|
+
|
47
|
+
for shooter,season,team in zip(pbp['event_player_1_name'],pbp['season'].astype(str),pbp['event_team_abbr']):
|
48
|
+
if shooter is None:
|
49
|
+
continue
|
50
|
+
else:
|
51
|
+
skaters.update({
|
52
|
+
shooter:[season,team]
|
53
|
+
})
|
54
|
+
|
55
|
+
plots = wsba.nhl_plot_skaters_shots(pbp,skaters,['5v5'],onice='indv',legend=True)
|
56
|
+
|
57
|
+
items = list(skaters.items())
|
58
|
+
for plot,skater in zip(plots,items):
|
59
|
+
plt.title('')
|
60
|
+
plot.savefig(f'plots/{skater[0]}{skater[1][0]}{skater[1][1]}_indv.png',bbox_inches='tight',transparent=True)
|
61
|
+
|
62
|
+
plots = wsba.nhl_plot_games(wsba.nhl_scrape_season('20242025',start='04-19',end='04-19'),wsba.wsba_main.fenwick_events,['5v5'],'all',legend=True)
|
63
|
+
|
64
|
+
i = 1
|
65
|
+
for plot in plots:
|
66
|
+
plot.savefig(f'plots/20242025_03_{i}_shotplot.png',bbox_inches='tight',transparent=True)
|
67
|
+
i += 1
|
@@ -328,8 +328,7 @@ def nhl_scrape_season(season,split_shifts = False, season_types = [2,3], remove
|
|
328
328
|
pbp_dict.update({'errors':data['errors']})
|
329
329
|
return pbp_dict
|
330
330
|
else:
|
331
|
-
pbp = data
|
332
|
-
|
331
|
+
pbp = data
|
333
332
|
if errors:
|
334
333
|
pbp_dict = {'pbp':pbp,
|
335
334
|
'errors':data['errors']}
|
@@ -337,13 +336,6 @@ def nhl_scrape_season(season,split_shifts = False, season_types = [2,3], remove
|
|
337
336
|
else:
|
338
337
|
return pbp
|
339
338
|
|
340
|
-
#errors = []
|
341
|
-
#for season in seasons[10:12]:
|
342
|
-
# data = nhl_scrape_season(season,remove=[],local=True,errors=True)
|
343
|
-
# errors.append(data['errors'])
|
344
|
-
# data['pbp'].to_csv(f'pbp/csv/nhl_pbp_{season}.csv',index=False)
|
345
|
-
#print(f'Errors: {errors}')
|
346
|
-
|
347
339
|
def nhl_scrape_seasons_info(seasons = []):
|
348
340
|
#Returns info related to NHL seasons (by default, all seasons are included)
|
349
341
|
# param 'season' - list of seasons to include
|
@@ -394,9 +386,6 @@ def nhl_scrape_standings(arg = "now", season_type = 2):
|
|
394
386
|
|
395
387
|
return pd.json_normalize(data)
|
396
388
|
|
397
|
-
#stand = [nhl_scrape_standings(season) for season in seasons]
|
398
|
-
#pd.concat(stand).to_csv('teaminfo/nhl_standings.csv',index=False)
|
399
|
-
|
400
389
|
def nhl_scrape_roster(season):
|
401
390
|
#Given a nhl season, return rosters for all participating teams
|
402
391
|
# param 'season' - NHL season to scrape
|
@@ -782,20 +771,14 @@ def nhl_calculate_stats(pbp,type,season_types,game_strength,roster_path="rosters
|
|
782
771
|
else:
|
783
772
|
return complete
|
784
773
|
|
785
|
-
|
786
|
-
#for season in seasons[6:18]:
|
787
|
-
# pbp = pd.read_parquet(f'pbp/parquet/nhl_pbp_{season}.parquet')
|
788
|
-
# stat = nhl_calculate_stats(pbp,'skater',[2],['5v5'],shot_impact=True)
|
789
|
-
# stat.to_csv(f'stats/skater/wsba_nhl_{season}.csv',index=False)
|
790
|
-
# stats.append(stat)
|
791
|
-
#pd.concat(stats).to_csv('stats/db/wsba_nhl_skater_db.csv',index=False)
|
792
|
-
|
793
|
-
def nhl_plot_skaters_shots(pbp,skater_dict,strengths,color_dict=event_colors,legend=False,xg='moneypuck'):
|
774
|
+
def nhl_plot_skaters_shots(pbp,skater_dict,strengths,marker_dict=event_markers,onice = 'indv',title = True,legend=False,xg='moneypuck'):
|
794
775
|
#Returns list of plots for specified skaters
|
795
776
|
# param 'pbp' - pbp to plot data
|
796
777
|
# param 'skater_dict' - skaters to plot shots for (format: {'Patrice Bergeron':['20242025','BOS']})
|
797
778
|
# param 'strengths' - strengths to include in plotting
|
798
|
-
# param '
|
779
|
+
# param 'marker_dict' - dict with markers to use for events
|
780
|
+
# param 'onice' - can set which shots to include in plotting for the specified skater ('indv', 'for', 'against')
|
781
|
+
# param 'title' - bool including title when true
|
799
782
|
# param 'legend' - bool which includes legend if true
|
800
783
|
# param 'xg' - xG model to apply to pbp for plotting
|
801
784
|
|
@@ -805,19 +788,19 @@ def nhl_plot_skaters_shots(pbp,skater_dict,strengths,color_dict=event_colors,leg
|
|
805
788
|
skater_plots = []
|
806
789
|
for skater in skater_dict.keys():
|
807
790
|
skater_info = skater_dict[skater]
|
808
|
-
title = f'{skater} Fenwick Shots for {skater_info[1]} in {skater_info[0][2:4]}-{skater_info[0][6:8]}'
|
809
|
-
skater_plots.append(plot_skater_shots(pbp,skater,skater_info[0],skater_info[1],strengths,title,
|
791
|
+
title = f'{skater} Fenwick Shots for {skater_info[1]} in {skater_info[0][2:4]}-{skater_info[0][6:8]}' if title else ''
|
792
|
+
skater_plots.append(plot_skater_shots(pbp,skater,skater_info[0],skater_info[1],strengths,title,marker_dict,onice,legend,xg))
|
810
793
|
|
811
794
|
#Return: list of plotted skater shot charts
|
812
795
|
return skater_plots
|
813
796
|
|
814
|
-
def nhl_plot_games(pbp,events,strengths,game_ids='all',
|
797
|
+
def nhl_plot_games(pbp,events,strengths,game_ids='all',marker_dict=event_markers,legend=False,xg='moneypuck'):
|
815
798
|
#Returns list of plots for specified games
|
816
799
|
# param 'pbp' - pbp to plot data
|
817
800
|
# param 'events' - type of events to plot
|
818
801
|
# param 'strengths' - strengths to include in plotting
|
819
802
|
# param 'game_ids' - games to plot (list if not set to 'all')
|
820
|
-
# param '
|
803
|
+
# param 'marker_dict' - dict with colors to use for events
|
821
804
|
# param 'legend' - bool which includes legend if true
|
822
805
|
# param 'xg' - xG model to apply to pbp for plotting
|
823
806
|
|
@@ -828,7 +811,7 @@ def nhl_plot_games(pbp,events,strengths,game_ids='all',color_dict=event_colors,l
|
|
828
811
|
print(f'Plotting the following games: {game_ids}...')
|
829
812
|
|
830
813
|
#Iterate through games, adding plot to list
|
831
|
-
game_plots = [plot_game_events(pbp,game,events,strengths,
|
814
|
+
game_plots = [plot_game_events(pbp,game,events,strengths,marker_dict,legend,xg) for game in game_ids]
|
832
815
|
|
833
816
|
#Return: list of plotted game events
|
834
817
|
return game_plots
|
@@ -878,7 +861,3 @@ def admin_convert_to_parquet(seasons):
|
|
878
861
|
load = pd.read_csv(f'pbp/csv/nhl_pbp_{season}.csv')
|
879
862
|
|
880
863
|
load.to_parquet(f'pbp/parquet/nhl_pbp_{season}.parquet',index=False)
|
881
|
-
|
882
|
-
#for season in seasons[6:12]:
|
883
|
-
# data = pd.read_csv(f"pbp/csv/nhl_pbp_{season}.csv")
|
884
|
-
# data.to_parquet(f'pbp/parquet/nhl_pbp_{season}.parquet',index=False)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: wsba_hockey
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.1
|
4
4
|
Summary: WeakSide Breakout's complete Python package of access to hockey data, primairly including the scraping of National Hockey League schedule, play-by-play, and shifts information.
|
5
5
|
Author-email: Owen Singh <owenbksingh@gmail.com>
|
6
6
|
Project-URL: Homepage, https://github.com/owensingh38/wsba_hockey/
|
@@ -78,7 +78,7 @@ skater_dict = {
|
|
78
78
|
}
|
79
79
|
pbp = wsba.nhl_scrape_season('20212022',remove=[], local = True)
|
80
80
|
|
81
|
-
wsba.
|
81
|
+
wsba.nhl_plot_skaters_shots(pbp,skater_dict,['5v5'],onice='for',legend=True)
|
82
82
|
wsba.nhl_plot_games(pbp,legend=True)
|
83
83
|
```
|
84
84
|
|
@@ -1,113 +0,0 @@
|
|
1
|
-
import matplotlib.pyplot as plt
|
2
|
-
import matplotlib.image as img
|
3
|
-
import numpy as np
|
4
|
-
import pandas as pd
|
5
|
-
pd.options.mode.chained_assignment = None
|
6
|
-
from scipy.interpolate import griddata
|
7
|
-
from scipy.ndimage import gaussian_filter
|
8
|
-
from .xg_model import *
|
9
|
-
from hockey_rink import NHLRink
|
10
|
-
from hockey_rink import CircularImage
|
11
|
-
|
12
|
-
event_colors = {
|
13
|
-
'faceoff':'black',
|
14
|
-
'hit':'yellow',
|
15
|
-
'blocked-shot':'pink',
|
16
|
-
'missed-shot':'red',
|
17
|
-
'shot-on-goal':'purple',
|
18
|
-
'goal':'blue',
|
19
|
-
'giveaway':'orange',
|
20
|
-
'takeaway':'green',
|
21
|
-
}
|
22
|
-
|
23
|
-
def wsba_rink(display_range='offense',rotation = 0):
|
24
|
-
img = 'tools/utils/wsba.png'
|
25
|
-
rink = NHLRink(center_logo={
|
26
|
-
"feature_class": CircularImage,
|
27
|
-
"image_path": img,
|
28
|
-
"length": 25, "width": 25,
|
29
|
-
"x": 0, "y": 0,
|
30
|
-
"radius": 14,
|
31
|
-
"zorder": 11,
|
32
|
-
}
|
33
|
-
)
|
34
|
-
rink.draw(
|
35
|
-
display_range=display_range,
|
36
|
-
rotation=rotation,
|
37
|
-
despine=True
|
38
|
-
)
|
39
|
-
|
40
|
-
def prep_plot_data(pbp,events,strengths,color_dict=event_colors,xg='moneypuck'):
|
41
|
-
try: pbp['xG']
|
42
|
-
except:
|
43
|
-
if xg == 'wsba':
|
44
|
-
pbp = wsba_xG(pbp)
|
45
|
-
else:
|
46
|
-
pbp = moneypuck_xG(pbp)
|
47
|
-
pbp['xG'] = np.where(pbp['xG'].isna(),0,pbp['xG'])
|
48
|
-
|
49
|
-
pbp['WSBA'] = pbp['event_player_1_name']+pbp['season'].astype(str)+pbp['event_team_abbr']
|
50
|
-
|
51
|
-
pbp['x_plot'] = pbp['y_fixed']*-1
|
52
|
-
pbp['y_plot'] = pbp['x_fixed']
|
53
|
-
|
54
|
-
pbp['size'] = np.where(pbp['xG']<=0,100,pbp['xG']*1000)
|
55
|
-
pbp['color'] = pbp['event_type'].replace(color_dict)
|
56
|
-
|
57
|
-
pbp = pbp.loc[(pbp['event_type'].isin(events))&
|
58
|
-
(pbp['event_distance']<=89)&
|
59
|
-
(pbp['x_fixed']<=89)&
|
60
|
-
(pbp['strength_state'].isin(strengths))]
|
61
|
-
|
62
|
-
return pbp
|
63
|
-
|
64
|
-
def league_shots(pbp,cord = 'fixed'):
|
65
|
-
[x,y] = np.round(np.meshgrid(np.linspace(0,100,100),np.linspace(-42.5,42.5,85)))
|
66
|
-
xgoals = griddata((pbp[f'x_{cord}'],pbp[f'y_{cord}']),pbp['xG'],(x,y),method='cubic',fill_value=0)
|
67
|
-
xgoals_smooth = gaussian_filter(xgoals,sigma = 3)
|
68
|
-
|
69
|
-
fig = plt.figure(figsize=(10,12), facecolor='w', edgecolor='k')
|
70
|
-
plt.imshow(xgoals_smooth,origin = 'lower')
|
71
|
-
plt.colorbar(orientation = 'horizontal', pad = 0.05)
|
72
|
-
plt.title('xGoal Array',fontdict={'fontsize': 15})
|
73
|
-
plt.show()
|
74
|
-
|
75
|
-
return xgoals_smooth
|
76
|
-
|
77
|
-
|
78
|
-
def plot_skater_shots(pbp, player, season, team, strengths, title = None, color_dict=event_colors, legend=False,xg='moneypuck'):
|
79
|
-
shots = ['missed-shot','shot-on-goal','goal']
|
80
|
-
pbp = prep_plot_data(pbp,shots,strengths,color_dict,xg)
|
81
|
-
skater = pbp.loc[pbp['WSBA'] == f'{player.upper()}{season}{team}']
|
82
|
-
|
83
|
-
fig, ax = plt.subplots()
|
84
|
-
wsba_rink(rotation=90)
|
85
|
-
|
86
|
-
for event in shots:
|
87
|
-
plays = skater.loc[skater['event_type']==event]
|
88
|
-
ax.scatter(plays['x_plot'],plays['y_plot'],plays['size'],plays['color'],label=event)
|
89
|
-
|
90
|
-
ax.set_title(title) if title else ''
|
91
|
-
ax.legend().set_visible(legend)
|
92
|
-
|
93
|
-
return fig
|
94
|
-
|
95
|
-
def plot_game_events(pbp,game_id,events,strengths,color_dict=event_colors,legend=False,xg='moneypuck'):
|
96
|
-
pbp = prep_plot_data(pbp,events,strengths,color_dict,xg)
|
97
|
-
pbp = pbp.loc[pbp['game_id'].astype(str)==game_id]
|
98
|
-
|
99
|
-
away_abbr = list(pbp['away_team_abbr'])[0]
|
100
|
-
home_abbr = list(pbp['home_team_abbr'])[0]
|
101
|
-
date = list(pbp['game_date'])[0]
|
102
|
-
|
103
|
-
fig, ax = plt.subplots()
|
104
|
-
wsba_rink(display_range='full')
|
105
|
-
|
106
|
-
for event in events:
|
107
|
-
plays = pbp.loc[pbp['event_type']==event]
|
108
|
-
ax.scatter(plays['x_adj'],plays['y_adj'],plays['size'],plays['color'],label=event)
|
109
|
-
|
110
|
-
ax.set_title(f'{away_abbr} @ {home_abbr} - {date}')
|
111
|
-
ax.legend(bbox_to_anchor =(0.5,-0.4), loc='lower center',ncol=1).set_visible(legend)
|
112
|
-
|
113
|
-
return fig
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|