wsba-hockey 1.1.9__py3-none-any.whl → 1.2.0__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 (144) hide show
  1. wsba_hockey/tools/scraping.py +146 -170
  2. wsba_hockey/tools/utils/__init__.py +0 -1
  3. wsba_hockey/tools/utils/shared.py +14 -389
  4. wsba_hockey/tools/xg_model.py +6 -1
  5. wsba_hockey/wsba_main.py +45 -10
  6. {wsba_hockey-1.1.9.dist-info → wsba_hockey-1.2.0.dist-info}/METADATA +16 -15
  7. wsba_hockey-1.2.0.dist-info/RECORD +15 -0
  8. wsba_hockey/api/api/index.py +0 -162
  9. wsba_hockey/data_pipelines.py +0 -247
  10. wsba_hockey/evidence/weakside-breakout/node_modules/duckdb/vendor.py +0 -146
  11. wsba_hockey/evidence/weakside-breakout/node_modules/flatted/python/flatted.py +0 -149
  12. wsba_hockey/evidence/weakside-breakout/node_modules/flatted/python/test.py +0 -63
  13. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/gyp_main.py +0 -45
  14. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSNew.py +0 -367
  15. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSProject.py +0 -206
  16. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSSettings.py +0 -1270
  17. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSSettings_test.py +0 -1547
  18. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSToolFile.py +0 -59
  19. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSUserFile.py +0 -153
  20. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSUtil.py +0 -271
  21. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/MSVSVersion.py +0 -574
  22. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/__init__.py +0 -690
  23. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/common.py +0 -661
  24. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/common_test.py +0 -78
  25. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/easy_xml.py +0 -165
  26. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/easy_xml_test.py +0 -109
  27. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/flock_tool.py +0 -55
  28. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/__init__.py +0 -0
  29. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/analyzer.py +0 -808
  30. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/android.py +0 -1173
  31. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/cmake.py +0 -1321
  32. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/compile_commands_json.py +0 -120
  33. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/dump_dependency_json.py +0 -103
  34. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/eclipse.py +0 -464
  35. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/gypd.py +0 -89
  36. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/gypsh.py +0 -58
  37. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py +0 -2714
  38. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs.py +0 -3981
  39. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs_test.py +0 -44
  40. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja.py +0 -2936
  41. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja_test.py +0 -55
  42. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode.py +0 -1394
  43. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode_test.py +0 -25
  44. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/input.py +0 -3130
  45. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/input_test.py +0 -98
  46. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/mac_tool.py +0 -771
  47. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/msvs_emulation.py +0 -1271
  48. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/ninja_syntax.py +0 -174
  49. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/simple_copy.py +0 -61
  50. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/win_tool.py +0 -374
  51. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py +0 -1939
  52. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/xcode_ninja.py +0 -302
  53. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/xcodeproj_file.py +0 -3197
  54. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/pylib/gyp/xml_fix.py +0 -65
  55. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/test_gyp.py +0 -261
  56. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/tools/graphviz.py +0 -102
  57. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/tools/pretty_gyp.py +0 -156
  58. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/tools/pretty_sln.py +0 -181
  59. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/gyp/tools/pretty_vcproj.py +0 -339
  60. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/test/fixtures/test-charmap.py +0 -31
  61. wsba_hockey/evidence/weakside-breakout/node_modules/node-gyp/update-gyp.py +0 -64
  62. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/gyp_main.py +0 -45
  63. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSNew.py +0 -367
  64. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSProject.py +0 -206
  65. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSSettings.py +0 -1270
  66. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSSettings_test.py +0 -1547
  67. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSToolFile.py +0 -59
  68. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSUserFile.py +0 -153
  69. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSUtil.py +0 -271
  70. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/MSVSVersion.py +0 -574
  71. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/__init__.py +0 -666
  72. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/common.py +0 -654
  73. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/common_test.py +0 -78
  74. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/easy_xml.py +0 -165
  75. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/easy_xml_test.py +0 -109
  76. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/flock_tool.py +0 -55
  77. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/__init__.py +0 -0
  78. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/analyzer.py +0 -808
  79. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/android.py +0 -1173
  80. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/cmake.py +0 -1321
  81. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/compile_commands_json.py +0 -120
  82. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/dump_dependency_json.py +0 -103
  83. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/eclipse.py +0 -464
  84. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/gypd.py +0 -89
  85. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/gypsh.py +0 -58
  86. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py +0 -2518
  87. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs.py +0 -3978
  88. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs_test.py +0 -44
  89. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja.py +0 -2936
  90. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja_test.py +0 -55
  91. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode.py +0 -1394
  92. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode_test.py +0 -25
  93. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/input.py +0 -3137
  94. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/input_test.py +0 -98
  95. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/mac_tool.py +0 -771
  96. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/msvs_emulation.py +0 -1271
  97. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/ninja_syntax.py +0 -174
  98. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/simple_copy.py +0 -61
  99. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/win_tool.py +0 -374
  100. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py +0 -1939
  101. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/xcode_ninja.py +0 -302
  102. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/xcodeproj_file.py +0 -3197
  103. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/pylib/gyp/xml_fix.py +0 -65
  104. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/setup.py +0 -42
  105. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/test_gyp.py +0 -260
  106. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/tools/graphviz.py +0 -102
  107. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/tools/pretty_gyp.py +0 -156
  108. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/tools/pretty_sln.py +0 -181
  109. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/tools/pretty_vcproj.py +0 -339
  110. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/test/fixtures/test-charmap.py +0 -31
  111. wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/update-gyp.py +0 -46
  112. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/duos/app.py +0 -210
  113. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/duos/calc.py +0 -163
  114. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/game_stats/app.py +0 -401
  115. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/game_stats/name_fix.py +0 -47
  116. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/goalie/app.py +0 -101
  117. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/goalie/plot.py +0 -71
  118. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/goalie/rink_plot.py +0 -245
  119. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/app.py +0 -108
  120. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/plot.py +0 -95
  121. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/rink_plot.py +0 -245
  122. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/line-combos/app.py +0 -245
  123. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/line-combos/plot.py +0 -275
  124. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/line-combos/rink_plot.py +0 -245
  125. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/app.py +0 -145
  126. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/plot.py +0 -79
  127. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/rink_plot.py +0 -245
  128. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/app.py +0 -406
  129. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/plot.py +0 -79
  130. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/rink_plot.py +0 -245
  131. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/app.py +0 -110
  132. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/plot.py +0 -59
  133. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/rink_plot.py +0 -245
  134. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/team_heatmaps/app.py +0 -103
  135. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/team_heatmaps/plot.py +0 -95
  136. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/team_heatmaps/rink_plot.py +0 -245
  137. wsba_hockey/flask/app.py +0 -77
  138. wsba_hockey/tools/utils/config.py +0 -14
  139. wsba_hockey/tools/utils/save_pages.py +0 -133
  140. wsba_hockey/workspace.py +0 -28
  141. wsba_hockey-1.1.9.dist-info/RECORD +0 -148
  142. {wsba_hockey-1.1.9.dist-info → wsba_hockey-1.2.0.dist-info}/WHEEL +0 -0
  143. {wsba_hockey-1.1.9.dist-info → wsba_hockey-1.2.0.dist-info}/licenses/LICENSE +0 -0
  144. {wsba_hockey-1.1.9.dist-info → wsba_hockey-1.2.0.dist-info}/top_level.txt +0 -0
@@ -1,275 +0,0 @@
1
- import pandas as pd
2
- import numpy as np
3
- import plotly.graph_objects as go
4
- import matplotlib.pyplot as plt
5
- import rink_plot
6
- from scipy.interpolate import griddata
7
- from scipy.ndimage import gaussian_filter
8
-
9
- def wsba_rink(setting='full', vertical=False):
10
- return rink_plot.rink(setting=setting, vertical=vertical)
11
-
12
- def player_events(df,skaters):
13
- df['onice'] = df['home_on_1_id'].astype(str) + ";" + df['home_on_2_id'].astype(str) + ";" + df['home_on_3_id'].astype(str) + ";" + df['home_on_4_id'].astype(str) + ";" + df['home_on_5_id'].astype(str) + ";" + df['home_on_6_id'].astype(str) + ";" + df['away_on_1_id'].astype(str) + ";" + df['away_on_2_id'].astype(str) + ";" + df['away_on_3_id'].astype(str) + ";" + df['away_on_4_id'].astype(str) + ";" + df['away_on_5_id'].astype(str) + ";" + df['away_on_6_id'].astype(str)
14
-
15
- if len(skaters)>2:
16
- mask = ((df['onice'].str.contains(skaters[0])) & (df['onice'].str.contains(skaters[1])) & (df['onice'].str.contains(skaters[2])))
17
- else:
18
- mask = ((df['onice'].str.contains(skaters[0])) & (df['onice'].str.contains(skaters[1])))
19
-
20
- return df[mask]
21
-
22
- def heatmap(df,team,skaters,events,strengths,onice):
23
- df = df.copy()
24
- df = df.loc[df['event_type'].isin(['missed-shot','shot-on-goal','goal'])].replace({np.nan: None})
25
-
26
- df['event_team_abbr_2'] = np.where(df['home_team_abbr']==df['event_team_abbr'],df['away_team_abbr'],df['home_team_abbr'])
27
- df['strength_state_2'] = df['strength_state'].str[::-1]
28
-
29
- df = df.fillna(0)
30
- df = df.loc[(df['event_type'].isin(events))&(df['x_adj'].notna())&(df['y_adj'].notna())]
31
- if onice == 'for':
32
- df['x'] = abs(df['x_adj'])
33
- df['y'] = np.where(df['x_adj']<0,-df['y_adj'],df['y_adj'])
34
- df['event_distance'] = abs(df['event_distance'].fillna(0))
35
- df = df.loc[(df['event_distance']<=89)&(df['x']<=89)&(df['empty_net']==0)]
36
-
37
- x_min = 0
38
- x_max = 100
39
- else:
40
- df['x'] = -abs(df['x_adj'])
41
- df['y'] = np.where(df['x_adj']>0,-df['y_adj'],df['y_adj'])
42
- df['event_distance'] = -abs(df['event_distance'])
43
- df = df.loc[(df['event_distance']>-89)&(df['x']>-89)&(df['empty_net']==0)]
44
-
45
- x_min = -100
46
- x_max = 0
47
-
48
- df['home_on_ice'] = df['home_on_1_id'].astype(str) + ";" + df['home_on_2_id'].astype(str) + ";" + df['home_on_3_id'].astype(str) + ";" + df['home_on_4_id'].astype(str) + ";" + df['home_on_5_id'].astype(str) + ";" + df['home_on_6_id'].astype(str)
49
- df['away_on_ice'] = df['away_on_1_id'].astype(str) + ";" + df['away_on_2_id'].astype(str) + ";" + df['away_on_3_id'].astype(str) + ";" + df['away_on_4_id'].astype(str) + ";" + df['away_on_5_id'].astype(str) + ";" + df['away_on_6_id'].astype(str)
50
-
51
- df['onice_for'] = np.where(df['home_team_abbr']==df['event_team_abbr'],df['home_on_ice'],df['away_on_ice'])
52
- df['onice_against'] = np.where(df['away_team_abbr']==df['event_team_abbr'],df['home_on_ice'],df['away_on_ice'])
53
-
54
- df['strength_state'] = np.where(df['strength_state'].isin(['5v5','5v4','4v5']),df['strength_state'],'Other')
55
- df['strength_state_2'] = np.where(df['strength_state_2'].isin(['5v5','5v4','4v5']),df['strength_state_2'],'Other')
56
-
57
- if strengths != 'all':
58
- if onice == 'against':
59
- df = df.loc[((df['strength_state_2'].isin(strengths)))]
60
- else:
61
- df = df.loc[((df['strength_state'].isin(strengths)))]
62
-
63
- [x,y] = np.round(np.meshgrid(np.linspace(x_min,x_max,(x_max-x_min)),np.linspace(-42.5,42.5,85)))
64
- xgoals = griddata((df['x'],df['y']),df['xG'],(x,y),method='cubic',fill_value=0)
65
- xgoals = np.where(xgoals < 0,0,xgoals)
66
- xgoals_smooth = gaussian_filter(xgoals,sigma=3)
67
-
68
- if len(skaters)>2:
69
- mask = ((df['onice'].str.contains(skaters[0])) & (df['onice'].str.contains(skaters[1])) & (df['onice'].str.contains(skaters[2])))
70
- else:
71
- mask = ((df['onice'].str.contains(skaters[0])) & (df['onice'].str.contains(skaters[1])))
72
-
73
- if onice == 'for':
74
- player_shots = df.loc[(df['event_team_abbr']==team)&mask]
75
- else:
76
- player_shots = df.loc[(df['event_team_abbr_2']==team)&mask]
77
-
78
- [x,y] = np.round(np.meshgrid(np.linspace(x_min,x_max,(x_max-x_min)),np.linspace(-42.5,42.5,85)))
79
- xgoals_player = griddata((player_shots['x'],player_shots['y']),player_shots['xG'],(x,y),method='cubic',fill_value=0)
80
- xgoals_player = np.where(xgoals_player < 0,0,xgoals_player)
81
-
82
- difference = (gaussian_filter(xgoals_player,sigma = 3)) - xgoals_smooth
83
- data_min= difference.min()
84
- data_max= difference.max()
85
-
86
- if abs(data_min) > data_max:
87
- data_max = data_min * -1
88
- elif data_max > abs(data_min):
89
- data_min = data_max * -1
90
-
91
- fig = go.Figure(
92
- data = go.Contour( x=np.linspace(x_min,x_max,(x_max-x_min)),
93
- y=np.linspace(-42.5,42.5,85),
94
- z=difference,
95
- colorscale=[[0.0,'red'],[0.5,'#09090b'],[1.0,'blue']],
96
- connectgaps=True,
97
- contours=dict(
98
- type='levels',
99
- start = data_min,
100
- end = data_max,
101
- size=(data_max-data_min)/11
102
- ),
103
- colorbar=dict(
104
- len = 0.7,
105
- orientation='h',
106
- showticklabels=False,
107
- thickness=15,
108
- yref='paper',
109
- yanchor='top',
110
- y=0
111
- ))
112
- )
113
-
114
- return player_shots, fig
115
-
116
- def calc_team(pbp,game_strength):
117
- teams = []
118
- fenwick_events = ['missed-shot','shot-on-goal','goal']
119
-
120
- for team in [('away','home'),('home','away')]:
121
- #Flip strength state (when necessary) and filter by game strength if not "all"
122
- if game_strength != "all":
123
- if game_strength not in ['3v3','4v4','5v5']:
124
- for strength in game_strength:
125
- pbp['strength_state'] = np.where(np.logical_and(pbp['event_team_venue']==team[1],pbp['strength_state']==strength[::-1]),strength,pbp['strength_state'])
126
-
127
- pbp = pbp.loc[pbp['strength_state'].isin(game_strength)]
128
-
129
- pbp['xGF'] = np.where(pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr'], pbp['xG'], 0)
130
- pbp['xGA'] = np.where(pbp['event_team_abbr'] == pbp[f'{team[1]}_team_abbr'], pbp['xG'], 0)
131
- pbp['GF'] = np.where((pbp['event_type'] == "goal") & (pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr']), 1, 0)
132
- pbp['GA'] = np.where((pbp['event_type'] == "goal") & (pbp['event_team_abbr'] == pbp[f'{team[1]}_team_abbr']), 1, 0)
133
- pbp['SF'] = np.where((pbp['event_type'].isin(['shot-on-goal','goal'])) & (pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr']), 1, 0)
134
- pbp['SA'] = np.where((pbp['event_type'].isin(['shot-on-goal','goal'])) & (pbp['event_team_abbr'] == pbp[f'{team[1]}_team_abbr']), 1, 0)
135
- pbp['FF'] = np.where((pbp['event_type'].isin(fenwick_events)) & (pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr']), 1, 0)
136
- pbp['FA'] = np.where((pbp['event_type'].isin(fenwick_events)) & (pbp['event_team_abbr'] == pbp[f'{team[1]}_team_abbr']), 1, 0)
137
- pbp['CF'] = np.where((pbp['event_type'].isin(fenwick_events+['blocked-shot'])) & (pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr']), 1, 0)
138
- pbp['CA'] = np.where((pbp['event_type'].isin(fenwick_events+['blocked-shot'])) & (pbp['event_team_abbr'] == pbp[f'{team[1]}_team_abbr']), 1, 0)
139
- pbp['HF'] = np.where((pbp['event_type']=='hit') & (pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr']), 1, 0)
140
- pbp['HA'] = np.where((pbp['event_type']=='hit') & (pbp['event_team_abbr'] == pbp[f'{team[1]}_team_abbr']), 1, 0)
141
- pbp['Penl'] = np.where((pbp['event_type']=='penalty') & (pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr']), 1, 0)
142
- pbp['Penl2'] = np.where((pbp['event_type']=='penalty') & (pbp['penalty_duration']==2) & (pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr']), 1, 0)
143
- pbp['Penl5'] = np.where((pbp['event_type']=='penalty') & (pbp['penalty_duration']==5) & (pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr']), 1, 0)
144
- pbp['PIM'] = np.where((pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr']), pbp['penalty_duration'], 0)
145
- pbp['Draw'] = np.where((pbp['event_type']=='penalty') & (pbp['event_team_abbr'] == pbp[f'{team[1]}_team_abbr']), 1, 0)
146
- pbp['Give'] = np.where((pbp['event_type']=='giveaway') & (pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr']), 1, 0)
147
- pbp['Take'] = np.where((pbp['event_type']=='takeaway') & (pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr']), 1, 0)
148
- pbp['Block'] = pbp['CA'] - pbp['FA']
149
- pbp['RushF'] = np.where((pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr'])&(pbp['rush']>0), 1, 0)
150
- pbp['RushA'] = np.where((pbp['event_team_abbr'] == pbp[f'{team[1]}_team_abbr'])&(pbp['rush']>0), 1, 0)
151
- pbp['RushFxG'] = np.where((pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr'])&(pbp['rush']>0), pbp['xG'], 0)
152
- pbp['RushAxG'] = np.where((pbp['event_team_abbr'] == pbp[f'{team[1]}_team_abbr'])&(pbp['rush']>0), pbp['xG'], 0)
153
- pbp['RushFG'] = np.where((pbp['event_type'] == "goal") & (pbp['event_team_abbr'] == pbp[f'{team[0]}_team_abbr'])&(pbp['rush']>0), 1, 0)
154
- pbp['RushAG'] = np.where((pbp['event_type'] == "goal") & (pbp['event_team_abbr'] == pbp[f'{team[1]}_team_abbr'])&(pbp['rush']>0), 1, 0)
155
-
156
- stats = pbp.groupby([f'{team[0]}_team_abbr','season']).agg(
157
- GP=('game_id','nunique'),
158
- TOI=('event_length','sum'),
159
- FF=('FF', 'sum'),
160
- FA=('FA', 'sum'),
161
- GF=('GF', 'sum'),
162
- GA=('GA', 'sum'),
163
- SF=('SF','sum'),
164
- SA=('SA','sum'),
165
- xGF=('xGF', 'sum'),
166
- xGA=('xGA', 'sum'),
167
- CF=('CF','sum'),
168
- CA=('CA','sum'),
169
- HF=('HF','sum'),
170
- HA=('HA','sum'),
171
- Penl=('Penl','sum'),
172
- Penl2=('Penl2','sum'),
173
- Penl5=('Penl5','sum'),
174
- PIM=('PIM','sum'),
175
- Draw=('Draw','sum'),
176
- Give=('Give','sum'),
177
- Take=('Take','sum'),
178
- Block=('Block','sum'),
179
- RushF=('RushF','sum'),
180
- RushA=('RushA','sum'),
181
- RushFxG=('RushFxG','sum'),
182
- RushAxG=('RushAxG','sum'),
183
- RushFG=('RushFG','sum'),
184
- RushAG=('RushAG','sum'),
185
- ).reset_index().rename(columns={f'{team[0]}_team_abbr':"Team",'season':"Season",'game_id':'Game'})
186
- teams.append(stats)
187
-
188
- onice_stats = pd.concat(teams).groupby(['Team','Season']).agg(
189
- GP=('GP','sum'),
190
- TOI=('TOI','sum'),
191
- FF=('FF', 'sum'),
192
- FA=('FA', 'sum'),
193
- GF=('GF', 'sum'),
194
- GA=('GA', 'sum'),
195
- SF=('SF','sum'),
196
- SA=('SA','sum'),
197
- xGF=('xGF', 'sum'),
198
- xGA=('xGA', 'sum'),
199
- CF=('CF','sum'),
200
- CA=('CA','sum'),
201
- HF=('HF','sum'),
202
- HA=('HA','sum'),
203
- Penl=('Penl','sum'),
204
- Penl2=('Penl2','sum'),
205
- Penl5=('Penl5','sum'),
206
- PIM=('PIM','sum'),
207
- Draw=('Draw','sum'),
208
- Give=('Give','sum'),
209
- Take=('Take','sum'),
210
- Block=('Block','sum'),
211
- RushF=('RushF','sum'),
212
- RushA=('RushA','sum'),
213
- RushFxG=('RushFxG','sum'),
214
- RushAxG=('RushAxG','sum'),
215
- RushFG=('RushFG','sum'),
216
- RushAG=('RushAG','sum'),
217
- ).reset_index()
218
-
219
- for col in onice_stats.columns.to_list()[2:30]:
220
- onice_stats[col] = onice_stats[col].astype(float)
221
-
222
- onice_stats['ShF%'] = onice_stats['GF']/onice_stats['SF']
223
- onice_stats['xGF/FF'] = onice_stats['xGF']/onice_stats['FF']
224
- onice_stats['GF/xGF'] = onice_stats['GF']/onice_stats['xGF']
225
- onice_stats['FshF%'] = onice_stats['GF']/onice_stats['FF']
226
- onice_stats['ShA%'] = onice_stats['GA']/onice_stats['SA']
227
- onice_stats['xGA/FA'] = onice_stats['xGA']/onice_stats['FA']
228
- onice_stats['GA/xGA'] = onice_stats['GA']/onice_stats['xGA']
229
- onice_stats['FshA%'] = onice_stats['GA']/onice_stats['FA']
230
- onice_stats['PM%'] = onice_stats['Take']/(onice_stats['Give']+onice_stats['Take'])
231
- onice_stats['HF%'] = onice_stats['HF']/(onice_stats['HF']+onice_stats['HA'])
232
- onice_stats['PENL%'] = onice_stats['Draw']/(onice_stats['Draw']+onice_stats['Penl'])
233
- onice_stats['GSAx'] = onice_stats['xGA']/onice_stats['GA']
234
-
235
- return onice_stats
236
-
237
-
238
- def calculate_stats(pbp,team,game_strength):
239
- per_sixty = ['Fi','xGi','Gi','A1','A2','P1','P','Si','OZF','NZF','DZF','FF','FA','xGF','xGA','GF','GA','SF','SA','CF','CA','HF','HA','Give','Take','Penl','Penl2','Penl5','Draw','Block']
240
-
241
- complete = calc_team(pbp,game_strength)
242
-
243
- #WSBA
244
- complete['WSBA'] = complete['Team']+complete['Season'].astype(str)
245
-
246
- #Set TOI to minute
247
- complete['TOI'] = complete['TOI']/60
248
-
249
- #Add per 60 stats
250
- for stat in per_sixty[11:len(per_sixty)]:
251
- complete[f'{stat}/60'] = (complete[stat]/complete['TOI'])*60
252
-
253
- complete['GF%'] = complete['GF']/(complete['GF']+complete['GA'])
254
- complete['SF%'] = complete['SF']/(complete['SF']+complete['SA'])
255
- complete['xGF%'] = complete['xGF']/(complete['xGF']+complete['xGA'])
256
- complete['FF%'] = complete['FF']/(complete['FF']+complete['FA'])
257
- complete['CF%'] = complete['CF']/(complete['CF']+complete['CA'])
258
-
259
- head = ['Team','Game'] if 'Game' in complete.columns else ['Team']
260
- complete = complete[head+[
261
- 'Season','WSBA',
262
- 'GP','TOI',
263
- "GF","SF","FF","xGF","xGF/FF","GF/xGF","ShF%","FshF%",
264
- "GA","SA","FA","xGA","xGA/FA","GA/xGA","ShA%","FshA%",
265
- 'CF','CA',
266
- 'GF%','SF%','FF%','xGF%','CF%',
267
- 'HF','HA','HF%',
268
- 'Penl','Penl2','Penl5','PIM','Draw','PENL%',
269
- 'Give','Take','PM%',
270
- 'Block',
271
- 'RushF','RushA','RushFxG','RushAxG','RushFG','RushAG',
272
- 'GSAx'
273
- ]+[f'{stat}/60' for stat in per_sixty[11:len(per_sixty)]]]
274
-
275
- return complete.loc[complete['Team']==team]
@@ -1,245 +0,0 @@
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://weakside-breakout.s3.us-east-2.amazonaws.com/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
@@ -1,145 +0,0 @@
1
- import pandas as pd
2
- import plot as wsba_plt
3
- import numpy as np
4
- import plotly.graph_objects as go
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_matchup"),
15
- )
16
-
17
- def server(input, output, session):
18
- @output()
19
- @render_widget
20
- def plot_matchup():
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
- #If no input data is provided automatically provide a select skater and plot all 5v5 fenwick shots
28
- defaults = {
29
- 'seasons':['20242025,20242025'],
30
- 'teams':['EDM,FLA'],
31
- 'strength_state':['5v5'],
32
- 'season_type':['2']
33
- }
34
-
35
- for key in defaults.keys():
36
- if key not in query.keys():
37
- query.update({key:defaults[key]})
38
-
39
- #Iterate through query and parse params with multiple selections
40
- for param in query.keys():
41
- q_string = query[param][0]
42
- query[param] = q_string.split(',')
43
-
44
- for i in range(2):
45
- query[f'team_{i+1}'] = [query['teams'][i]]
46
- query[f'season_{i+1}'] = [query['seasons'][i]]
47
-
48
- print(query)
49
- season_1 = query['season_1'][0]
50
- season_2 = query['season_2'][0]
51
- #Load appropriate dataframes
52
- if season_1 == season_2:
53
- df_1 = pd.read_parquet(f'https://weakside-breakout.s3.us-east-2.amazonaws.com/pbp/{season_1}.parquet')
54
- df_2 = df_1
55
-
56
- else:
57
- df_1 = pd.read_parquet(f'https://weakside-breakout.s3.us-east-2.amazonaws.com/pbp/{season_1}.parquet')
58
- df_2 = pd.read_parquet(f'https://weakside-breakout.s3.us-east-2.amazonaws.com/pbp/{season_2}.parquet')
59
-
60
- events = ['missed-shot','shot-on-goal','goal']
61
- team_xg = {}
62
- for team, season, df, flip in [(query['team_1'][0],season_1,df_1,True),(query['team_2'][0],season_2,df_2,False)]:
63
- #Prepare dataframe for plotting based on URL parameters
64
- df = df.loc[(df['season'].astype(str) == season)&(df['season_type'].astype(str).isin(query['season_type']))].replace({np.nan: None})
65
-
66
- team_xg.update({team:{'for':wsba_plt.heatmap_prep(df,team,events,query['strength_state'],'for',flip=flip),
67
- 'against':wsba_plt.heatmap_prep(df,team,events,query['strength_state'],'against',flip=flip)}})
68
-
69
- #Left is team_1 for vs team_2 against; Right is team_2 for vs team_1 against
70
- left_diff = team_xg[query['team_1'][0]]['for']-team_xg[query['team_2'][0]]['against']
71
- right_diff = team_xg[query['team_2'][0]]['for']-team_xg[query['team_1'][0]]['against']
72
-
73
- rink = wsba_plt.wsba_rink()
74
-
75
- for difference,x_min,x_max in [(left_diff,-100,0),(right_diff,0,100)]:
76
- data_min= difference.min()
77
- data_max= difference.max()
78
-
79
- if abs(data_min) > data_max:
80
- data_max = data_min * -1
81
- elif data_max > abs(data_min):
82
- data_min = data_max * -1
83
-
84
- fig = go.Figure(
85
- data = go.Contour( x=np.linspace(x_min,x_max,(x_max-x_min)),
86
- y=np.linspace(-42.5,42.5,85),
87
- z=difference,
88
- colorscale=[[0.0,'red'],[0.5,'#09090b'],[1.0,'blue']],
89
- connectgaps=True,
90
- contours=dict(
91
- type='levels',
92
- start = data_min,
93
- end = data_max,
94
- size=(data_max-data_min)/11
95
- ),
96
- colorbar=dict(
97
- len = 0.7,
98
- orientation='h',
99
- showticklabels=False,
100
- thickness=15,
101
- yref='paper',
102
- yanchor='top',
103
- y=0
104
- ))
105
- )
106
- for trace in fig.data:
107
- rink.add_trace(trace)
108
-
109
- team_1 = query['team_1'][0]
110
- team_2 = query['team_2'][0]
111
- strengths = 'All Situations' if len(query['strength_state']) == 4 else query['strength_state']
112
- span = 'Regular Season' if query['season_type'][0]=='2' else 'Playoffs'
113
-
114
-
115
- return rink.update_layout(
116
- title=dict(
117
- text=f'{team_1} ({season_1}, {span}) vs {team_2} ({season_2}, {span}): xG at {strengths}',
118
- x=0.5, y=0.96,
119
- xanchor='center',
120
- yanchor='top',
121
- font=dict(color='white')
122
- ),
123
- ).add_annotation(
124
- text='Lower xG',
125
- xref="paper",
126
- yref="paper",
127
- xanchor='right',
128
- yanchor='top',
129
- font=dict(color='white'),
130
- x=0.3,
131
- y=0.04,
132
- showarrow=False
133
- ).add_annotation(
134
- text='Higher xG',
135
- xref="paper",
136
- yref="paper",
137
- xanchor='right',
138
- yanchor='top',
139
- font=dict(color='white'),
140
- x=0.76,
141
- y=0.04,
142
- showarrow=False
143
- )
144
-
145
- app = App(app_ui, server)