wsba-hockey 1.0.3__py3-none-any.whl → 1.0.5__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.
Files changed (131) hide show
  1. wsba_hockey/__init__.py +1 -1
  2. wsba_hockey/data_pipelines.py +183 -0
  3. wsba_hockey/evidence/weakside-breakout/node_modules/duckdb/vendor.py +146 -0
  4. wsba_hockey/evidence/weakside-breakout/node_modules/flatted/python/flatted.py +149 -0
  5. wsba_hockey/evidence/weakside-breakout/node_modules/flatted/python/test.py +63 -0
  6. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/gyp_main.py +45 -0
  7. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSNew.py +367 -0
  8. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSProject.py +206 -0
  9. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSSettings.py +1270 -0
  10. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSSettings_test.py +1547 -0
  11. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSToolFile.py +59 -0
  12. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSUserFile.py +153 -0
  13. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSUtil.py +271 -0
  14. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSVersion.py +574 -0
  15. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/__init__.py +690 -0
  16. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/common.py +661 -0
  17. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/common_test.py +78 -0
  18. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/easy_xml.py +165 -0
  19. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/easy_xml_test.py +109 -0
  20. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/flock_tool.py +55 -0
  21. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/__init__.py +0 -0
  22. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/analyzer.py +808 -0
  23. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/android.py +1173 -0
  24. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/cmake.py +1321 -0
  25. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/compile_commands_json.py +120 -0
  26. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/dump_dependency_json.py +103 -0
  27. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/eclipse.py +464 -0
  28. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/gypd.py +89 -0
  29. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/gypsh.py +58 -0
  30. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py +2714 -0
  31. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs.py +3981 -0
  32. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs_test.py +44 -0
  33. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja.py +2936 -0
  34. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja_test.py +55 -0
  35. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode.py +1394 -0
  36. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode_test.py +25 -0
  37. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/input.py +3130 -0
  38. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/input_test.py +98 -0
  39. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/mac_tool.py +771 -0
  40. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/msvs_emulation.py +1271 -0
  41. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/ninja_syntax.py +174 -0
  42. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/simple_copy.py +61 -0
  43. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/win_tool.py +374 -0
  44. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py +1939 -0
  45. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/xcode_ninja.py +302 -0
  46. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/xcodeproj_file.py +3197 -0
  47. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/xml_fix.py +65 -0
  48. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/test_gyp.py +261 -0
  49. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/tools/graphviz.py +102 -0
  50. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/tools/pretty_gyp.py +156 -0
  51. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/tools/pretty_sln.py +181 -0
  52. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/tools/pretty_vcproj.py +339 -0
  53. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/test/fixtures/test-charmap.py +31 -0
  54. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/update-gyp.py +64 -0
  55. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/gyp_main.py +45 -0
  56. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSNew.py +367 -0
  57. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSProject.py +206 -0
  58. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSSettings.py +1270 -0
  59. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSSettings_test.py +1547 -0
  60. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSToolFile.py +59 -0
  61. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSUserFile.py +153 -0
  62. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSUtil.py +271 -0
  63. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSVersion.py +574 -0
  64. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/__init__.py +666 -0
  65. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/common.py +654 -0
  66. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/common_test.py +78 -0
  67. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/easy_xml.py +165 -0
  68. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/easy_xml_test.py +109 -0
  69. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/flock_tool.py +55 -0
  70. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/__init__.py +0 -0
  71. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/analyzer.py +808 -0
  72. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/android.py +1173 -0
  73. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/cmake.py +1321 -0
  74. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/compile_commands_json.py +120 -0
  75. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/dump_dependency_json.py +103 -0
  76. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/eclipse.py +464 -0
  77. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/gypd.py +89 -0
  78. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/gypsh.py +58 -0
  79. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py +2518 -0
  80. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs.py +3978 -0
  81. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs_test.py +44 -0
  82. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja.py +2936 -0
  83. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja_test.py +55 -0
  84. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode.py +1394 -0
  85. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode_test.py +25 -0
  86. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/input.py +3137 -0
  87. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/input_test.py +98 -0
  88. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/mac_tool.py +771 -0
  89. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/msvs_emulation.py +1271 -0
  90. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/ninja_syntax.py +174 -0
  91. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/simple_copy.py +61 -0
  92. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/win_tool.py +374 -0
  93. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py +1939 -0
  94. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/xcode_ninja.py +302 -0
  95. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/xcodeproj_file.py +3197 -0
  96. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/xml_fix.py +65 -0
  97. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/setup.py +42 -0
  98. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/test_gyp.py +260 -0
  99. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/tools/graphviz.py +102 -0
  100. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/tools/pretty_gyp.py +156 -0
  101. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/tools/pretty_sln.py +181 -0
  102. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/tools/pretty_vcproj.py +339 -0
  103. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/test/fixtures/test-charmap.py +31 -0
  104. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/update-gyp.py +46 -0
  105. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/game_stats/app.py +400 -0
  106. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/game_stats/name_fix.py +47 -0
  107. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/app.py +108 -0
  108. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/plot.py +93 -0
  109. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/rink_plot.py +245 -0
  110. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/app.py +145 -0
  111. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/plot.py +77 -0
  112. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/rink_plot.py +245 -0
  113. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/app.py +389 -0
  114. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/plot.py +70 -0
  115. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/rink_plot.py +245 -0
  116. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/app.py +110 -0
  117. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/plot.py +58 -0
  118. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/rink_plot.py +245 -0
  119. wsba_hockey/tools/agg.py +243 -54
  120. wsba_hockey/tools/plotting.py +25 -25
  121. wsba_hockey/tools/scraping.py +154 -263
  122. wsba_hockey/tools/xg_model.py +369 -315
  123. wsba_hockey/workspace.py +22 -117
  124. wsba_hockey/wsba_main.py +499 -167
  125. {wsba_hockey-1.0.3.dist-info → wsba_hockey-1.0.5.dist-info}/METADATA +1 -1
  126. wsba_hockey-1.0.5.dist-info/RECORD +135 -0
  127. {wsba_hockey-1.0.3.dist-info → wsba_hockey-1.0.5.dist-info}/WHEEL +1 -1
  128. wsba_hockey/stats/calculate_viz/shot_impact.py +0 -2
  129. wsba_hockey-1.0.3.dist-info/RECORD +0 -19
  130. {wsba_hockey-1.0.3.dist-info → wsba_hockey-1.0.5.dist-info}/licenses/LICENSE +0 -0
  131. {wsba_hockey-1.0.3.dist-info → wsba_hockey-1.0.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,70 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ import plotly.graph_objects as go
5
+ import rink_plot
6
+
7
+ event_markers = {
8
+ 'faceoff':'X',
9
+ 'hit':'P',
10
+ 'blocked-shot':'v',
11
+ 'missed-shot':'o',
12
+ 'shot-on-goal':'D',
13
+ 'goal':'*',
14
+ 'giveaway':'1',
15
+ 'takeaway':'2',
16
+ 'penalty':''
17
+ }
18
+
19
+ def wsba_rink():
20
+ return rink_plot.rink(setting='full', vertical=False)
21
+
22
+ def colors(df):
23
+ away_abbr = list(df['away_team_abbr'])[0]
24
+ home_abbr = list(df['home_team_abbr'])[0]
25
+ season = list(df['season'])[0]
26
+ team_data = pd.read_csv('https://f005.backblazeb2.com/file/weakside-breakout/info/nhl_teaminfo.csv')
27
+
28
+ team_info ={
29
+ away_abbr: list(team_data.loc[team_data['WSBA']==f'{away_abbr}{season}','Primary Color'])[0],
30
+ home_abbr: list(team_data.loc[team_data['WSBA']==f'{home_abbr}{season}','Primary Color'])[0],
31
+ }
32
+
33
+ return team_info
34
+
35
+ def prep(df,events,strengths):
36
+ df = df.loc[(df['event_type'].isin(events))]
37
+
38
+ if 'all' not in strengths:
39
+ df = df.loc[((df['strength_state'].isin(strengths)))]
40
+
41
+ df['xG'] = df['xG'].fillna(0)
42
+ df['size'] = np.where(df['xG']<=0,40,df['xG']*400)
43
+
44
+ df['marker'] = df['event_type'].replace(event_markers)
45
+
46
+ df['Description'] = df['description']
47
+ df['Team'] = df['event_team_abbr']
48
+ df['Event Num.'] = df['event_num']
49
+ df['Period'] = df['period']
50
+ df['Time (in seconds)'] = df['seconds_elapsed']
51
+ df['Strength'] = df['strength_state']
52
+ df['Away Score'] = df['away_score']
53
+ df['Home Score'] = df['home_score']
54
+ df['x'] = df['x_adj']
55
+ df['y'] = df['y_adj']
56
+ df['Event Distance from Attacking Net'] = df['event_distance']
57
+ df['Event Angle to Attacking Net'] = df['event_angle']
58
+ return df
59
+
60
+ def timelines(df):
61
+ df['Goals'] = np.where(df['event_type']=='goal',1,0)
62
+ df['Shots'] = np.where(df['event_type'].isin(['shot-on-goal','goal']),1,0)
63
+ df['Fenwick'] = np.where(df['event_type'].isin(['missed-shot','shot-on-goal','goal']),1,0)
64
+
65
+ df['xG'] = df.groupby('event_team_abbr')['xG'].cumsum()
66
+ df['Goals'] = df.groupby('event_team_abbr')['Goals'].cumsum()
67
+ df['Shots'] = df.groupby('event_team_abbr')['Shots'].cumsum()
68
+ df['Fenwick'] = df.groupby('event_team_abbr')['Fenwick'].cumsum()
69
+
70
+ return df
@@ -0,0 +1,245 @@
1
+
2
+ import numpy as np
3
+ import plotly.graph_objects as go
4
+ import io
5
+ import base64
6
+ import requests as rs
7
+ from PIL import Image
8
+
9
+ def rink(setting = "full", vertical = False):
10
+ '''
11
+ Function to plot rink in Plotly. Takes 2 arguments :
12
+
13
+ setting : full (default) for full ice, offense positive half of the ice, ozone positive quarter of ice, defense for negative half of the ice, dzone for negative quarter of the ice, and neutral for the neutral zone
14
+ vertical : True if you want a vertical rink, False (default) is for an horizontal rink
15
+
16
+ '''
17
+
18
+ def faceoff_circle(x, y, outer=True):
19
+ segments = []
20
+ theta = np.linspace(0, 2*np.pi, 300)
21
+ if outer:
22
+ # Outer circle
23
+ x_outer = x + 15*np.cos(theta)
24
+ y_outer = y + 15*np.sin(theta)
25
+ outer_circle = go.Scatter(x=x_outer, y=y_outer, mode='lines', line=dict(width=2, color='red'), showlegend=False, hoverinfo='skip')
26
+
27
+ segments.append(outer_circle)
28
+
29
+ # Inner circle
30
+ x_inner = x + np.cos(theta)
31
+ y_inner = y + np.sin(theta)
32
+ inner_circle = go.Scatter(x=x_inner, y=y_inner, mode='lines', fill='toself', fillcolor='rgba(255, 0, 0, 0.43)', line=dict(color='rgba(255, 0, 0, 1)', width=2), showlegend=False, hoverinfo='skip')
33
+
34
+ segments.append(inner_circle)
35
+
36
+ return segments #segments
37
+
38
+ fig = go.Figure()
39
+
40
+ if vertical :
41
+ setting_dict = {
42
+ "full" : [-101, 101],
43
+ "offense" : [0, 101],
44
+ "ozone" : [25, 101],
45
+ "defense" : [-101, 0],
46
+ "dzone" : [-101, -25],
47
+ "neutral" : [-25,25]
48
+ }
49
+ fig.update_layout(xaxis=dict(range=[-42.6, 42.6], showgrid=False, zeroline=False, showticklabels=False, constrain="domain"), yaxis=dict(range=setting_dict[setting], showgrid=False, zeroline=False, showticklabels=False, constrain="domain"),
50
+ showlegend=False, autosize=True, template="plotly_white")
51
+ fig.update_yaxes(
52
+ scaleanchor="x",
53
+ scaleratio=1,
54
+ )
55
+ def goal_crease(flip=1):
56
+ x_seq = np.linspace(-4, 4, 100)
57
+ x_goal = np.concatenate(([-4], x_seq, [4]))
58
+ y_goal = flip * np.concatenate(([89], 83 + x_seq**2/4**2*1.5, [89]))
59
+ goal_crease = go.Scatter(x=x_goal, y=y_goal, fill='toself', fillcolor='rgba(173, 216, 230, 0.3)', line=dict(color='red'))
60
+ return goal_crease
61
+
62
+ # Outer circle
63
+ theta = np.linspace(0, 2*np.pi, 300)
64
+ x_outer = 15 * np.cos(theta)
65
+ y_outer = 15 * np.sin(theta)
66
+ fig.add_trace(go.Scatter(x=x_outer, y=y_outer, mode='lines', line=dict(color='royalblue', width=2), showlegend=False, hoverinfo='skip'))
67
+ # Inner circle
68
+ theta2 = np.linspace(np.pi/2, 3*np.pi/2, 300)
69
+ x_inner = 42.5 + 10 * np.cos(theta2)
70
+ y_inner = 10 * np.sin(theta2)
71
+ fig.add_trace(go.Scatter(x=x_inner, y=y_inner, mode='lines', line=dict(color='red', width=2), showlegend=False, hoverinfo='skip'))
72
+ # Rink boundaries
73
+ fig.add_shape(type='rect', xref='x', yref='y', x0=-42.5, y0=25, x1=42.5, y1=26, line=dict(color='royalblue', width=1), fillcolor='royalblue', opacity=1)
74
+ fig.add_shape(type='rect', xref='x', yref='y', x0=-42.5, y0=-25, x1=42.5, y1=-26, line=dict(color='royalblue', width=1), fillcolor='royalblue', opacity=1)
75
+ fig.add_shape(type='rect', xref='x', yref='y', x0=-42.5, y0=-0.5, x1=42.5, y1=0.5, line=dict(color='red', width=2), fillcolor='red')
76
+
77
+ # Goal crease
78
+ fig.add_trace(goal_crease())
79
+ fig.add_trace(goal_crease(-1))
80
+ # Goal lines
81
+ goal_line_extreme = 42.5 - 28 + np.sqrt(28**2 - (28-11)**2)
82
+ fig.add_shape(type='line', xref='x', yref='y', x0=-goal_line_extreme, y0=89, x1=goal_line_extreme, y1=89, line=dict(color='red', width=2))
83
+ fig.add_shape(type='line', xref='x', yref='y', x0=-goal_line_extreme, y0=-89, x1=goal_line_extreme, y1=-89, line=dict(color='red', width=2))
84
+
85
+ # Faceoff circles
86
+ fig.add_traces(faceoff_circle(-22, 69))
87
+ fig.add_traces(faceoff_circle(22, 69))
88
+ fig.add_traces(faceoff_circle(-22, -69))
89
+ fig.add_traces(faceoff_circle(22, -69))
90
+ fig.add_traces(faceoff_circle(-22, -20, False))
91
+ fig.add_traces(faceoff_circle(22, -20, False))
92
+ fig.add_traces(faceoff_circle(-22, 20, False))
93
+ fig.add_traces(faceoff_circle(22, 20, False))
94
+
95
+ # Sidelines
96
+ theta_lines = np.linspace(0, np.pi/2, 20)
97
+ x_lines1 = np.concatenate(([-42.5], -42.5 + 28 - 28*np.cos(theta_lines), 42.5 - 28 + 28*np.cos(np.flip(theta_lines))))
98
+ y_lines1 = np.concatenate(([15], 72 + 28*np.sin(theta_lines), 72 + 28*np.sin(np.flip(theta_lines))))
99
+ x_lines2 = np.concatenate(([-42.5], -42.5 + 28 - 28*np.cos(theta_lines), 42.5 - 28 + 28*np.cos(np.flip(theta_lines))))
100
+ y_lines2 = np.concatenate(([15], -72 - 28*np.sin(theta_lines), -72 - 28*np.sin(np.flip(theta_lines))))
101
+ fig.add_trace(go.Scatter(x=x_lines1, y=y_lines1, mode='lines', line=dict(color='white', width=2), showlegend=False, hoverinfo='skip'))
102
+ fig.add_trace(go.Scatter(x=x_lines2, y=y_lines2, mode='lines', line=dict(color='white', width=2), showlegend=False, hoverinfo='skip'))
103
+ fig.add_shape(type='line', xref='x', yref='y', x0=42.5, y0=-72.5, x1=42.5, y1=72.5, line=dict(color='white', width=2))
104
+ fig.add_shape(type='line', xref='x', yref='y', x0=-42.5, y0=-72.5, x1=-42.5, y1=72.5, line=dict(color='white', width=2))
105
+
106
+ # Add goals
107
+ goal_width = 6 # feet
108
+ goal_depth = 4 # feet
109
+
110
+ # Top goal
111
+ fig.add_shape(
112
+ type="rect",
113
+ xref="x",
114
+ yref="y",
115
+ x0=-goal_width / 2,
116
+ y0=89,
117
+ x1=goal_width / 2,
118
+ y1=89 + goal_depth,
119
+ line=dict(color="red", width=2),
120
+ )
121
+ # Bottom goal
122
+ fig.add_shape(
123
+ type="rect",
124
+ xref="x",
125
+ yref="y",
126
+ x0=-goal_width / 2,
127
+ y0=-89 - goal_depth,
128
+ x1=goal_width / 2,
129
+ y1=-89,
130
+ line=dict(color="red", width=2),
131
+ )
132
+
133
+ else :
134
+ setting_dict = {
135
+ "full" : [-101, 101],
136
+ "offense" : [0, 101],
137
+ "ozone" : [25, 101],
138
+ "defense" : [-101, 0],
139
+ "dzone" : [-101, -25]
140
+ }
141
+ fig.update_layout(xaxis=dict(range=setting_dict[setting], showgrid=False, zeroline=False, showticklabels=False), yaxis=dict(range=[-42.6, 42.6], showgrid=False, zeroline=False, showticklabels=False, constrain="domain"),
142
+ showlegend=True, autosize =True, template="plotly_white")
143
+ fig.update_yaxes(
144
+ scaleanchor="x",
145
+ scaleratio=1,
146
+ )
147
+ def goal_crease(flip=1):
148
+ y_seq = np.linspace(-4, 4, 100)
149
+ y_goal = np.concatenate(([-4], y_seq, [4]))
150
+ x_goal = flip * np.concatenate(([89], 83 + y_seq**2/4**2*1.5, [89]))
151
+ goal_crease = go.Scatter(x=x_goal, y=y_goal, fill='toself', fillcolor='rgba(173, 216, 230, 0.3)', line=dict(color='red'), showlegend=False, hoverinfo='skip')
152
+ return goal_crease
153
+
154
+ # Outer circle
155
+ theta = np.linspace(0, 2 * np.pi, 300)
156
+ x_outer = 15 * np.sin(theta)
157
+ y_outer = 15 * np.cos(theta)
158
+ fig.add_trace(go.Scatter(x=x_outer, y=y_outer, mode='lines', line=dict(color='royalblue', width=2), showlegend=False, hoverinfo='skip'))
159
+ # Inner circle
160
+ theta2 = np.linspace(3 * np.pi / 2, np.pi / 2, 300) # Update theta2 to rotate the plot by 180 degrees
161
+ x_inner = 10 * np.sin(theta2) # Update x_inner to rotate the plot by 180 degrees
162
+ y_inner = -42.5 - 10 * np.cos(theta2) # Update y_inner to rotate the plot by 180 degrees
163
+ fig.add_trace(go.Scatter(x=x_inner, y=y_inner, mode='lines', line=dict(color='red', width=2), showlegend=False, hoverinfo='skip'))
164
+
165
+ # Rink boundaries
166
+ fig.add_shape(type='rect', xref='x', yref='y', x0=25, y0=-42.5, x1=26, y1=42.5, line=dict(color='royalblue', width=1), fillcolor='royalblue', opacity=1)
167
+ fig.add_shape(type='rect', xref='x', yref='y', x0=-25, y0=-42.5, x1=-26, y1=42.5, line=dict(color='royalblue', width=1), fillcolor='royalblue', opacity=1)
168
+ fig.add_shape(type='rect', xref='x', yref='y', x0=-0.5, y0=-42.5, x1=0.5, y1=42.5, line=dict(color='red', width=2), fillcolor='red')
169
+ # Goal crease
170
+ fig.add_trace(goal_crease())
171
+ fig.add_trace(goal_crease(-1))
172
+ # Goal lines
173
+ goal_line_extreme = 42.5 - 28 + np.sqrt(28 ** 2 - (28 - 11) ** 2)
174
+ fig.add_shape(type='line', xref='x', yref='y', x0=89, y0=-goal_line_extreme, x1=89, y1=goal_line_extreme, line=dict(color='red', width=2))
175
+ fig.add_shape(type='line', xref='x', yref='y', x0=-89, y0=-goal_line_extreme, x1=-89, y1=goal_line_extreme, line=dict(color='red', width=2))
176
+ # Faceoff circles
177
+ fig.add_traces(faceoff_circle(-69, -22))
178
+ fig.add_traces(faceoff_circle(-69, 22))
179
+ fig.add_traces(faceoff_circle(69, -22))
180
+ fig.add_traces(faceoff_circle(69, 22))
181
+ fig.add_traces(faceoff_circle(-20, -22, False))
182
+ fig.add_traces(faceoff_circle(-20, 22, False))
183
+ fig.add_traces(faceoff_circle(20, -22, False))
184
+ fig.add_traces(faceoff_circle(20, 22, False))
185
+
186
+ # Sidelines
187
+ theta_lines = np.linspace(0, np.pi / 2, 20)
188
+ x_lines1 = np.concatenate(([15], 72 + 28 * np.sin(theta_lines), 72 + 28 * np.sin(np.flip(theta_lines))))
189
+ y_lines1 = np.concatenate(([-42.5], -42.5 + 28 - 28 * np.cos(theta_lines), 42.5 - 28 + 28 * np.cos(np.flip(theta_lines))))
190
+ x_lines2 = np.concatenate(([15], -72 - 28 * np.sin(theta_lines), -72 - 28 * np.sin(np.flip(theta_lines))))
191
+ y_lines2 = np.concatenate(([-42.5], -42.5 + 28 - 28 * np.cos(theta_lines), 42.5 - 28 + 28 * np.cos(np.flip(theta_lines))))
192
+ fig.add_trace(go.Scatter(x=x_lines1, y=y_lines1, mode='lines', line=dict(color='white', width=2), showlegend=False, hoverinfo='skip'))
193
+ fig.add_trace(go.Scatter(x=x_lines2, y=y_lines2, mode='lines', line=dict(color='white', width=2), showlegend=False, hoverinfo='skip'))
194
+ fig.add_shape(type='line', xref='x', yref='y', x0=-72.5, y0=-42.5, x1=72.5, y1=-42.5, line=dict(color='white', width=2))
195
+ fig.add_shape(type='line', xref='x', yref='y', x0=-72.5, y0=42.5, x1=72.5, y1=42.5, line=dict(color='white', width=2))
196
+
197
+ # Add goals
198
+ goal_width = 6 # feet
199
+ goal_depth = 4 # feet
200
+
201
+ # Right goal
202
+ fig.add_shape(
203
+ type="rect",
204
+ xref="x",
205
+ yref="y",
206
+ x0=89,
207
+ y0=-goal_width / 2,
208
+ x1=89 + goal_depth,
209
+ y1=goal_width / 2,
210
+ line=dict(color="red", width=2),
211
+ )
212
+ # Left goal
213
+ fig.add_shape(
214
+ type="rect",
215
+ xref="x",
216
+ yref="y",
217
+ x0=-89 - goal_depth,
218
+ y0=-goal_width / 2,
219
+ x1=-89,
220
+ y1=goal_width / 2,
221
+ line=dict(color="red", width=2),
222
+ )
223
+
224
+ # Add logo
225
+ logo = Image.open(rs.get('https://f005.backblazeb2.com/file/weakside-breakout/utils/wsba.png',stream=True).raw)
226
+
227
+ fig.add_layout_image(
228
+ dict(
229
+ source=logo,
230
+ xref="x",
231
+ yref="y",
232
+ x=-12,
233
+ y=12,
234
+ sizex=24,
235
+ sizey=24,
236
+ sizing="stretch",
237
+ opacity=1)
238
+ )
239
+
240
+ #Set background to transparent
241
+ fig.update_layout(
242
+ paper_bgcolor="rgba(0,0,0,0)",
243
+ plot_bgcolor="rgba(0,0,0,0)"
244
+ )
245
+ return fig
@@ -0,0 +1,110 @@
1
+ import pandas as pd
2
+ import plotly.express as px
3
+ import plot as wsba_plt
4
+ import numpy as np
5
+ from urllib.parse import *
6
+ from shiny import *
7
+ from shinywidgets import output_widget, render_widget
8
+
9
+ app_ui = ui.page_fluid(
10
+ ui.tags.style(
11
+ "body {background:#09090b"
12
+ "}"
13
+ ),
14
+ output_widget("plot_skater"),
15
+ )
16
+
17
+ def server(input, output, session):
18
+ @output()
19
+ @render_widget
20
+ def plot_skater():
21
+ #Retreive query parameters
22
+ search = session.input[".clientdata_url_search"]()
23
+ query = parse_qs(urlparse(search).query)
24
+
25
+ print(query)
26
+ #If no input data is provided automatically provide a select skater and plot all 5v5 fenwick shots
27
+ defaults = {
28
+ 'skater':['8473419'],
29
+ 'season':['20182019'],
30
+ 'team':['BOS'],
31
+ 'event_type':['missed-shot,shot-on-goal,goal'],
32
+ 'strength_state':['5v5'],
33
+ 'season_type':['2']
34
+ }
35
+
36
+ for key in defaults.keys():
37
+ if key not in query.keys():
38
+ query.update({key:defaults[key]})
39
+
40
+ #Iterate through query and parse params with multiple selections
41
+ for param in query.keys():
42
+ q_string = query[param][0]
43
+ query[param] = q_string.split(',')
44
+
45
+ print(query)
46
+ #Determine which season to load based on the input
47
+ season = query['season'][0]
48
+ #Load appropriate dataframe
49
+ df = pd.read_parquet(f'https://f005.backblazeb2.com/file/weakside-breakout/pbp/{season}.parquet')
50
+
51
+ #Prepare dataframe for plotting based on URL parameters
52
+ df = df.loc[(df['event_player_1_id'].astype(str).str.replace('.0','').isin(query['skater']))&(df['season'].astype(str).isin(query['season']))&(df['event_team_abbr'].astype(str).isin(query['team']))&(df['season_type'].astype(str).isin(query['season_type']))].replace({np.nan: None})
53
+ df = wsba_plt.prep(df,events=query['event_type'],strengths=query['strength_state'])
54
+
55
+ #Return empty rink if no data exists else continue
56
+ if df.empty:
57
+ return wsba_plt.wsba_rink(setting='offense',vertical=True)
58
+ else:
59
+ colors = wsba_plt.colors(df)
60
+ rink = wsba_plt.wsba_rink(setting='offense',vertical=True)
61
+
62
+ plot = px.scatter(df,
63
+ x='x', y='y',
64
+ size='size',
65
+ color='Team',
66
+ color_discrete_map=colors,
67
+ hover_name='Description',
68
+ hover_data=['Event Num.', 'Period', 'Time (in seconds)',
69
+ 'Strength',
70
+ 'Away Score', 'Home Score', 'x', 'y',
71
+ 'Event Distance from Attacking Net',
72
+ 'Event Angle to Attacking Net',
73
+ 'xG'])
74
+
75
+ for trace in plot.data:
76
+ rink.add_trace(trace)
77
+
78
+ player = df['event_player_1_name'].to_list()[0]
79
+ season = int(season[0:4])
80
+ team = df['event_team_abbr'].to_list()[0]
81
+ strengths = 'All Situations' if len(query['strength_state']) == 4 else query['strength_state']
82
+ span = 'Regular Season' if query['season_type'][0]=='2' else 'Playoffs'
83
+
84
+ rink.add_annotation(
85
+ text=f'{season}-{season+1}, {span}, {team}',
86
+ xref="paper",
87
+ yref="paper",
88
+ xanchor='center',
89
+ yanchor='top',
90
+ font=dict(color='white'),
91
+ x=0.5,
92
+ y=0,
93
+ showarrow=False
94
+ )
95
+
96
+ return rink.update_layout(
97
+ title=dict(
98
+ text=f'{player} Fenwick Shots at {strengths}',
99
+ x=0.5, y=0.96,
100
+ xanchor='center',
101
+ yanchor='top',
102
+ font=dict(color='white')
103
+ ),
104
+
105
+ hoverlabel=dict(
106
+ font_size=10
107
+ )
108
+ )
109
+
110
+ app = App(app_ui, server)
@@ -0,0 +1,58 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ import plotly.graph_objects as go
5
+ import rink_plot
6
+
7
+ event_markers = {
8
+ 'faceoff':'X',
9
+ 'hit':'P',
10
+ 'blocked-shot':'v',
11
+ 'missed-shot':'o',
12
+ 'shot-on-goal':'D',
13
+ 'goal':'*',
14
+ 'giveaway':'1',
15
+ 'takeaway':'2',
16
+ 'penalty':''
17
+ }
18
+
19
+ def wsba_rink(setting='full', vertical=False):
20
+ return rink_plot.rink(setting=setting, vertical=vertical)
21
+
22
+ def colors(df):
23
+ team = list(df['away_team_abbr'])[0]
24
+ season = list(df['season'])[0]
25
+ team_data = pd.read_csv('https://f005.backblazeb2.com/file/weakside-breakout/info/nhl_teaminfo.csv')
26
+
27
+ team_info ={
28
+ team: list(team_data.loc[team_data['WSBA']==f'{team}{season}','Primary Color'])[0],
29
+ }
30
+
31
+ return team_info
32
+
33
+ def prep(df,events,strengths):
34
+ df = df.loc[(df['event_type'].isin(events))]
35
+
36
+ df['strength_state'] = np.where(df['strength_state'].isin(['5v5','5v4','4v5']),df['strength_state'],'Other')
37
+ if strengths != 'all':
38
+ df = df.loc[((df['strength_state'].isin(strengths)))]
39
+
40
+ df['xG'] = df['xG'].fillna(0)
41
+ df['size'] = np.where(df['xG']<=0,40,df['xG']*400)
42
+
43
+ df['marker'] = df['event_type'].replace(event_markers)
44
+
45
+ df['Description'] = df['description']
46
+ df['Team'] = df['event_team_abbr']
47
+ df['Event Num.'] = df['event_num']
48
+ df['Period'] = df['period']
49
+ df['Time (in seconds)'] = df['seconds_elapsed']
50
+ df['Strength'] = df['strength_state']
51
+ df['Away Score'] = df['away_score']
52
+ df['Home Score'] = df['home_score']
53
+ df['x'] = np.where(df['x_adj']<0,-df['y_adj'],df['y_adj'])
54
+ df['y'] = abs(df['x_adj'])
55
+ df['Event Distance from Attacking Net'] = df['event_distance']
56
+ df['Event Angle to Attacking Net'] = df['event_angle']
57
+ df['xG'] = df['xG']*100
58
+ return df